#title XIP 살펴보기 '''''eXecute In Place (XIP) 살펴보기''''' 저자: Phil Wilshire [[BR]] 번역: 김남형 [[TableOfContents]] == XIP (eXecute In Place) == XIP 는 uClinux 상에서 활용할 수 있는 유용한 옵션 중의 하나이다. XIP 의 장점은 동일한 프로그램을 여러번 실행할 때 텍스트 세그먼트를 복사할 필요가 없다는 것이다. 실제로 텍스트 세그먼트는 플래시 메모리 상에 존재할 수 있기 때문에 시스템의 RAM 에 복사될 필요가 전혀 없다. 이것은 커다란 코드 영역을 가지는 프로그램이 시스템 상에 여러 개의 인스턴스로 실행되는 태스크에서 유용하다. 이 경우 각각의 프로세스에 대해서 오직 스택, BSS, 데이터 세그먼트만이 새로 생성될 필요가 있다. 그리고 텍스트 세그먼트 영역은 플래시 메모리에 존재하거나, 성능이 문제가 되는 경우 파일 시스템의 내용을 RAM 에 복사한 후에 램 디스크로 마운트될 수 있다. 만약 파일 시스템 내의 실행 파일이 XIP 를 지원하도록 컴파일 되었고 헤더 파일에서 XIP 플래그를 설정하였으면 오직 하나의 텍스트 세그먼트만이 로드되어 실행된다. == XIP 와 커널 == 커널은 항상 "XIP" 로 동작한다. 여기서 가질 수 있는 단 한가지 의문점은 만약 커널이 실행되기 전에 RAM 으로 복사되는 경우에 대한 것이다. 이것은 다음과 같은 여러가지 일들에 의존한다: * 커널이 텍스트 섹션에 있는 변수를 사용하는가? * 커널 링크 맵이 ROM 내의 커널의 베이스로 정의되어 있는가? * 커널 스타트 업 코드 (커널 소스 트리 내의 `head.S` 혹은 `crt0.s` 파일) 가 커널을 RAM 으로 재배치 (relocate) 하도록 요구하는가? 일설에 따르면 ARM 커널의 어떤 변수가 텍스트 세그먼트 내에 있기 때문에 ARM 커널을 ROM 에서 실행시키기 어렵다고 한다. (TODO: 시뮬레이터를 통해 확인해 볼 것!) 68K (Dragonball) 와 Coldfire 커널은 ROM 에서 실행될 수 있어야 한다. 모든 초기화 코드는 나중에 여기에 사용된 메모리를 다시 사용할 수 있게 하기 위해서 RAM 상에 존재하도록 해야 한다 (아니면 메모리를 해제하지 않도록 한다). == XIP 와 사용자 태스크 == 사용자 태스크도 XIP 로 동작하도록 정의할 수 있다. 이 경우 오직 데이터, BSS, 재배치 정보와 스택 만이 각 태스크 고유의 RAM 영역에 존재한다. 스택 영역의 크기가 증가될 수 없음에 주의하라. 이 크기는 `elf2flt` 가 동작할 때 고정된다. 이것은 `elf2flt` 프로그램을 다시 실행하거나 `flthdr` (아래 참조)를 사용해서 변경할 수 있다. /!\ 주의: 이 플래그?를 이용해서 XIP 가 아닌 코드를 XIP 코드로 변경할 수는 없다. 하지만 XIP 코드가 (여러 인스턴스가 실행될 때) 텍스트 섹션을 강제로 로드하도록 할 수는 있다. 모든 flat 파일을 로드하는 커널 코드는 여기에 있다: '''`linux-2.[04].x/fs/binfmt_flat.c`''' 그 중 필요한 RAM 영역을 할당하는 부분의 코드는 다음과 같다. {{{ extra = MAX(bss_len + stack_len, relocs * sizeof(unsigned long)); down_write(&t->mm->mmap_sem); realdatastart = do_mmap(0, 0, data_len + extra + MAX_SHARED_LIBS * sizeof(unsigned long), PROT_READ|PROT_WRITE|PROT_EXEC, 0, 0); up_write(&t->mm->mmap_sem); }}} == Flat 파일 재배치 == 로더 프로세스는 데이터 세그먼트가 할당된 영역이 변경됨에 따라 필요한 작업들을 처리해야 한다. 이것은 MMU 가 없기 때문에 발생하는 문제이다. 우리는 단순히 "적절한 곳에 메모리가 존재하도록" 할 수 없다. 재배치 목록은 elf 파일이 flat 파일로 변환될 때 `elf2flt` 프로그램에 의해 정해진다. 고려해 볼 만한 두가지 종류의 재배치 방법이 있다. === Global Offset Table (GOT) === 이것은 사실 이 문서에서 다룰 내용은 아니지만 언급할 가치가 있으므로 얘기하고자 한다. 만일 컴파일시 옵션으로 GOT를 활성화 시켰다면 데이터 세그먼트는 GOT 영역에서 시작하게 된다. 이 테이블에 있는 주소 값들은 데이터 섹션의 실제 주소를 반영하여 재배치될 필요가 있다. GOT 테이블의 마지막은 (존재하는 경우) -1 (0xFFFFFFFF) 값을 가진다. 아래의 코드는 `binfmt_flat.c` 의 내용으로 GOT 테이블을 업데이트하는 부분이다. {{{ if (flags & FLAT_FLAG_GOTPIC) { for (rp = (unsigned long *)datapos; *rp != 0xffffffff;rp++) { unsigned long addr; if (*rp) { addr = calc_reloc(*rp, libinfo, id, 0); if (addr == RELOC_FAILED) return -ENOEXEC; *rp = addr; } } } }}} === 코드 재배치 (Code RELOCS) === 이 방법은 `elf2flt` 프로세스에 의해 생성된 재배치 테이블을 사용한다. 주의할 점은, 만약 파일이 XIP 로 동작하도록 되어 있다면 여러 개의 실행 파일에서 공유되기 위해 텍스트 세그먼트 영역은 영향을 주지 않는다는 것이다. 테이블의 각 엔트리는 재배치될 필요가 있는 영역의 주소를 나타낸다. 재배치할 영역 자체가 재배치되기 때문에 이 영역의 주소가 먼저 수정되어야 한다. 일단 변경된 주소가 계산되면 그 주소가 가리키는 영역의 내용은 결정되며 해당 영역에는 새로운 주소가 적용된다. 주소들간에 다른 바이트 오더링 형식 (little/big endian) 이 사용된다면 복잡한 문제가 발생되며 재배치되는 실제 주소는 정렬되지 않을 수 있다. 다시 한번 `binfmt_flat.c` 파일을 살펴 보자. 아래의 코드는 재배치를 수행하는 부분을 나타낸다. {{{ for (i=0; i < relocs; i++) { unsigned long addr; /* 재배치될 포인터의 주소를 얻어온다. (물론 먼저 이 주소를 재배치 해야 한다) */ rp = (unsigned long *)calc_reloc(ntohl(reloc[i]), libinfo, id, 1); if (rp == (unsigned long *)RELOC_FAILED) return -ENOEXEC; /* 포인터가 가리키는 내용을 가져 온다. */ addr = get_unaligned (rp); if (addr != 0) { /* * 재배치를 수행한다. * 데이터 섹션의 PIC 재배치는 이미 타겟의 바이트 오더로 되어 있다. */ addr = calc_reloc( (flags & FLAT_FLAG_GOTPIC) ? addr : ntohl(addr), libinfo, id, 0); if (addr == RELOC_FAILED) return -ENOEXEC; /* 재배치된 포인터를 기록한다(write back). */ put_unaligned (addr, rp); } } }}} == 연결 == 위에 살펴보았듯이 이것이 동작하기 위해서는 여러 시스템들이 함께 적절히 동작해야 한다. * `bitfmt_flat.c` 는 `elf2flt` 의 출력을 이해해야 한다. * `elf2flt` 는 (특히) 재배치 영역에 대한 적절한 출력을 생성해야 한다. * 컴파일러는 올바른 GOT 정보와 PIC 코드를 생성해야 한다. * crt0.o 과 같은 라이브러리 요소는 위의 모든 것들과 잘 동작해야 한다. 이 모든 요소들이 함께 관리되기 위해 리비전 번호가 생성되었다. == Flathdr - flat 파일 매니저 == 이 프로그램은 `elf2flt` 에 의해 생성된 flat 파일 내의 flat 헤더의 내용을 조사하거나 변경하기 위해 사용된다. 사용 방법은 아래와 같다 {{{ /usr/local/bin/flthdr romfs/bin/boa romfs/bin/boa Magic: bFLT Rev: 4 Entry: 0x50 Data Start: 0x11980 Data End: 0x13d40 BSS End: 0x15604 Stack Size: 0x2000 Reloc Start: 0x13d40 Reloc Count: 0x4f Flags: 0x2 ( Has-PIC-GOT )flt. }}} 사용할 수 있는 옵션은 아래와 같다. {{{ -p : print current settings -z : compressed flat file -d : compressed data-only flat file -Z : un-compressed flat file -r : ram load -R : do not RAM load -s size : stack size -o file : output-file (default is to modify input file) }}} 아래는 `flthdr` 을 사용하여 압축된 flat 파일을 생성하는 예제이다. {{{ /usr/local/bin/flthdr -z romfs/bin/boa -o romfs/bin/boaz 그리고 그 결과는 아래와 같다 /usr/local/bin/flthdr -p romfs/bin/boaz romfs/bin/boaz Magic: bFLT Rev: 4 Entry: 0x50 Data Start: 0x11980 Data End: 0x13d40 BSS End: 0x15604 Stack Size: 0x2000 Reloc Start: 0x13d40 Reloc Count: 0x4f Flags: 0x6 ( Has-PIC-GOT Gzip-Compressed ) ls -l romfs/bin/boa* -rwxr--r-- 1 philw philw 81532 Jul 18 04:22 romfs/bin/boa -rw-rw-r-- 1 philw philw 40261 Jul 18 04:16 romfs/bin/boaz }}} load to ram 비트는 -r 혹은 -R 옵션에 의해 변경될 수 있다. {{{ /usr/local/bin/flthdr -r romfs/bin/boa 결과는 아래와 같다 /usr/local/bin/flthdr -p romfs/bin/boa romfs/bin/boa Magic: bFLT Rev: 4 Entry: 0x50 Data Start: 0x11980 Data End: 0x13d40 BSS End: 0x15604 Stack Size: 0x2000 Reloc Start: 0x13d40 Reloc Count: 0x4f Flags: 0x3 ( Load-to-Ram Has-PIC-GOT ) }}} 스택의 크기는 -s 옵션을 이용하여 변경할 수 있다. {{{ /usr/local/bin/flthdr -s 16384 romfs/bin/boa 결과는 아래와 같다 usr/local/bin/flthdr -p romfs/bin/boa romfs/bin/boa Magic: bFLT Rev: 4 Entry: 0x50 Data Start: 0x11980 Data End: 0x13d40 BSS End: 0x15604 Stack Size: 0x4000 Reloc Start: 0x13d40 Reloc Count: 0x4f Flags: 0x2 ( Has-PIC-GOT ) }}} == 컴파일 플래그 == 이 부분이 이 문서의 핵심이다. 각각의 시스템에서 XIP 로 동작시키기 위한 컴파일 플래그는 무엇일까? 다음 테이블을 살펴보면 도움을 얻을 수 있을 것이다. (TODO: 확인하고 테이블 완성하기) GOT 옵션은..?? || 아키텍처 || 사용자 프로그램 || 커널 || || M68K (Dragonball) || `-msep_data` || `-DMAGIC_ROM_PTR` || || Coldfire || `-msep_data` || `-DMAGIC_ROM_PTR` || || ARM || `-D__PIC__ -fpic -msingle-pic-base` || (`-D__PIC__` 는 임시방편이다) || || SH3 || ` ` || ` ` || || Mips || `-G 0 -mabicalls -fpic` || `-G0 -mno-abicalls -fno-pic` || || Sparc || ` ` || ` ` || || Etrax (cris) || ` ` || ` ` || == 저자 == 이 문서의 저자는 Phil Wilshire 이며 [http://www.sysdcs.com/ System Design & Consulting Services (SDCS)] 의 트레이닝 프로그램의 일부로 사용되었다. ---- Please see: [uClinux] ---- CategoryDevelopment