· KLDP.org · KLDP.net · KLDP Wiki · KLDP BBS ·
XIP 살펴보기

eXecute In Place (XIP) 살펴보기

저자: Phil Wilshire <philwil AT earthlink.net>
번역: 김남형 <namhyung AT gmail.com>


1. XIP (eXecute In Place)

XIP 는 uClinux 상에서 활용할 수 있는 유용한 옵션 중의 하나이다. XIP 의 장점은 동일한 프로그램을 여러번 실행할 때 텍스트 세그먼트를 복사할 필요가 없다는 것이다. 실제로 텍스트 세그먼트는 플래시 메모리 상에 존재할 수 있기 때문에 시스템의 RAM 에 복사될 필요가 전혀 없다. 이것은 커다란 코드 영역을 가지는 프로그램이 시스템 상에 여러 개의 인스턴스로 실행되는 태스크에서 유용하다.

이 경우 각각의 프로세스에 대해서 오직 스택, BSS, 데이터 세그먼트만이 새로 생성될 필요가 있다. 그리고 텍스트 세그먼트 영역은 플래시 메모리에 존재하거나, 성능이 문제가 되는 경우 파일 시스템의 내용을 RAM 에 복사한 후에 램 디스크로 마운트될 수 있다. 만약 파일 시스템 내의 실행 파일이 XIP 를 지원하도록 컴파일 되었고 헤더 파일에서 XIP 플래그를 설정하였으면 오직 하나의 텍스트 세그먼트만이 로드되어 실행된다.


2. XIP 와 커널

커널은 항상 "XIP" 로 동작한다. 여기서 가질 수 있는 단 한가지 의문점은 만약 커널이 실행되기 전에 RAM 으로 복사되는 경우에 대한 것이다. 이것은 다음과 같은 여러가지 일들에 의존한다:

  • 커널이 텍스트 섹션에 있는 변수를 사용하는가?
  • 커널 링크 맵이 ROM 내의 커널의 베이스로 정의되어 있는가?
  • 커널 스타트 업 코드 (커널 소스 트리 내의 head.S 혹은 crt0.s 파일) 가 커널을 RAM 으로 재배치 (relocate) 하도록 요구하는가?

일설에 따르면 ARM 커널의 어떤 변수가 텍스트 세그먼트 내에 있기 때문에 ARM 커널을 ROM 에서 실행시키기 어렵다고 한다. (TODO: 시뮬레이터를 통해 확인해 볼 것!)

68K (Dragonball) 와 Coldfire 커널은 ROM 에서 실행될 수 있어야 한다. 모든 초기화 코드는 나중에 여기에 사용된 메모리를 다시 사용할 수 있게 하기 위해서 RAM 상에 존재하도록 해야 한다 (아니면 메모리를 해제하지 않도록 한다).


3. 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);


4. Flat 파일 재배치

로더 프로세스는 데이터 세그먼트가 할당된 영역이 변경됨에 따라 필요한 작업들을 처리해야 한다. 이것은 MMU 가 없기 때문에 발생하는 문제이다. 우리는 단순히 "적절한 곳에 메모리가 존재하도록" 할 수 없다. 재배치 목록은 elf 파일이 flat 파일로 변환될 때 elf2flt 프로그램에 의해 정해진다.

고려해 볼 만한 두가지 종류의 재배치 방법이 있다.

4.1. 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;
        }
    }
}


4.2. 코드 재배치 (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);
    }
}


5. 연결

위에 살펴보았듯이 이것이 동작하기 위해서는 여러 시스템들이 함께 적절히 동작해야 한다.

  • bitfmt_flat.celf2flt 의 출력을 이해해야 한다.
  • elf2flt 는 (특히) 재배치 영역에 대한 적절한 출력을 생성해야 한다.
  • 컴파일러는 올바른 GOT 정보와 PIC 코드를 생성해야 한다.
  • crt0.o 과 같은 라이브러리 요소는 위의 모든 것들과 잘 동작해야 한다.

이 모든 요소들이 함께 관리되기 위해 리비전 번호가 생성되었다.


6. 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 )


7. 컴파일 플래그

이 부분이 이 문서의 핵심이다. 각각의 시스템에서 XIP 로 동작시키기 위한 컴파일 플래그는 무엇일까? 다음 테이블을 살펴보면 도움을 얻을 수 있을 것이다. (TODO: 확인하고 테이블 완성하기) GOT 옵션은..??

아키텍처 사용자 프로그램 커널
M68K (Dragonball) -msep_data -DMAGIC_ROM_PTR
Coldfire -msep_data -DMAGIC_ROM_PTR
ARM -DPIC -fpic -msingle-pic-base (-DPIC 는 임시방편이다)
SH3 ` ` ` `
Mips -G 0 -mabicalls -fpic -G0 -mno-abicalls -fno-pic
Sparc ` ` ` `
Etrax (cris) ` ` ` `

8. 저자

이 문서의 저자는 Phil Wilshire 이며 [http]System Design & Consulting Services (SDCS) 의 트레이닝 프로그램의 일부로 사용되었다.


Please see: uClinux





sponsored by andamiro
sponsored by cdnetworks
sponsored by HP

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2006-11-21 15:38:31
Processing time 0.0209 sec