· 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



ID
Password
Join
Your mode of life will be changed for the better because of good news soon.


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.0014 sec