Executable and linkage format

Ahn9807 (토론 | 기여)님의 2023년 2월 11일 (토) 02:21 판 (새 문서: 분류: 파일 포맷 분류: 프로그램 실행 == 개요 == ELF (Executable and Linkable Fromat)이란 유닉스 시스템에서 사용하기 위해서 발명된 Executable Format이다. ELF 포맷은 프로그램의 실행을 크게 TEXT, DATA, BSS섹션으로 나눈다. 현재 ELF포맷은 유닉스시스템에서 표준으로 사용되며 많은 영향을 미치고 있다. == 구조 == ELF는 프로그램의 부분을 담고 있는 포맷이다. ELF는 여...)
(차이) ← 이전 판 | 최신판 (차이) | 다음 판 → (차이)


개요

ELF (Executable and Linkable Fromat)이란 유닉스 시스템에서 사용하기 위해서 발명된 Executable Format이다. ELF 포맷은 프로그램의 실행을 크게 TEXT, DATA, BSS섹션으로 나눈다. 현재 ELF포맷은 유닉스시스템에서 표준으로 사용되며 많은 영향을 미치고 있다.

구조

ELF는 프로그램의 부분을 담고 있는 포맷이다. ELF는 여러 섹션으로 나누어지는데, 이 섹션은 헤더에 의해서 어떻게 메모리에 적제될지 결정하게 된다. 이러한 헤더에는 링킹단계에서 symbol 테이블에 의해서 결정된다.

.text 코드 영역이 작성되는 부분이다. objdump -drS .process.o will show you that
.data global tables, variables, etc. 들이 적재되는 공간이다. objdump -s -j .data .process.o will hexdump it.
.bss 0으로 초기화 되는 변수들이 저장되는 공간이다. 0으로 초기화 되는 변수들은 여기에 위치함으로써 링커가 로딩과정에서 자동으로 0으로 채우게 된다.
.rodata 문자열이 저장되는 공간이다. ReadOnly data의 약자이며, 문자열과 같은 const데이터들은 여기에 저장된다. (! 만약 const를 초기화 하지 않으면 이 또한 bss섹션으로 저장된다.)
.comment & .note 컴파일러나 링커가 작성한 커맨트 들이 들어간다.
.stab & .stabstr 디버깅 심볼과 디버깅 관련 정보가 들어간다.

컴파일러와 링커는 이 정보를 해석하여 ELF파일 헤더로 변환하여 저장하게 된다. 각각의 ELF파일 헤더는 위치하는 섹션에대한 offset과 size등과 같은 메모리 로드에 필요한 정보를 포함하여, ELF파일의 어떤 위치에 어떤 정보가 저장되어 있는지 나타내게 된다.

ELF 파일 헤더는 32비트 또는 64비트 주소들이 사용되어야 하는지를 정의한다.

ELF 헤더
오프셋 크기(Bytes) 필드 목적
32-bit 64-bit 32-bit 64-bit
0x00 4 e_ident[EI_MAG0] 부터 e_ident[EI_MAG3] 0x7FASCII 코드 ELF; 이 네 바이트가 매직 넘버를 형성한다.
0x04 1 e_ident[EI_CLASS] 이 바이트는 1 또는 2로 설정되며 32비트 또는 64비트 형식을 나타낸다.
0x05 1 e_ident[EI_DATA] 이 바이트는 1 또는 2로 설정되며 리틀 또는 빅엔디언을 나타낸다.
0x06 1 e_ident[EI_VERSION] 오리지널 버전의 ELF인 경우 1로 설정된다.
0x07 1 e_ident[EI_OSABI] 대상 운영 체제 ABI를 구별한다..
ABI
0x00 System V
0x01 HP-UX
0x02 NetBSD
0x03 리눅스
0x06 솔라리스
0x07 AIX
0x08 IRIX
0x09 FreeBSD
0x0C OpenBSD
0x0D OpenVMS

이것은 대상 플랫폼과 관련 없이 종종 0으로 설정된다.

0x08 1 e_ident[EI_ABIVERSION] ABI 버전을 더 명시한다. 이 해석은 대상 ABO에 따라 달라진다. 2.6 버전 이후의 리눅스 커널은 이것을 정의하지 않는다. 이 경우 오프셋과 크기는 8이다.
0x09 7 e_ident[EI_PAD] 현재 사용하지 않음
0x10 2 e_type 1, 2, 3, 4는 각각 재배치, 실행, 공유 그리고 코어를 명시한다.
0x12 2 e_machine 대상 명령어 집합을 명시한다. 예를 들면:
ISA
0x00 특정한 명령어 집합 없음
0x02 SPARC
0x03 x86
0x08 MIPS
0x14 파워PC
0x28 ARM
0x2A 슈퍼H
0x32 IA-64
0x3E x86-64
0xB7 AArch64
0x14 4 e_version 오리지날 버전의 ELF인 경우 1로 설정된다.
0x18 4 8 e_entry 이것은 엔트리 포인트의 메모리 주소이다. 즉 프로세스가 어디서 실행을 시작하는지를 말해준다. 이 필드는 위에서 정의한 32비트 또는 64비트에 따라 길이가 다르다.
0x1C 0x20 4 8 e_phoff 프로그램 헤더 테이블의 시작을 가리킨다.
0x20 0x28 4 8 e_shoff 섹션 헤더 테이블의 시작을 가리킨다.
0x24 0x30 4 e_flags 대상 아키텍처에 따라 이 필드의 해석이 달라진다.
0x28 0x34 2 e_ehsize 이 헤더의 크기를 가지며 일반적으로 64비트의 경우 64바이트, 32비트의 경우 52바이트이다.
0x2A 0x36 2 e_phentsize 프로그램 헤더 테이블 엔트리의 크기를 갖는다.
0x2C 0x38 2 e_phnum 프로그램 헤더 테이블에서 엔트리의 개수.
0x2E 0x3A 2 e_shentsize 섹션 헤더 테이블 엔트리의 크기를 갖는다.
0x30 0x3C 2 e_shnum 섹션 헤더 테이블에서 엔트리의 개수.
0x32 0x3E 2 e_shstrndx 섹션 이름들을 포함하는 섹션 헤더 테이블 엔트리의 인덱스.

64 bit version의 Executable Header.

Position Value
0-3 Type of segment (see below)
4-7 Flags (see below)
8-15 The offset in the file that the data for this segment can be found (p_offset)
16-23 Where you should start to put this segment in virtual memory (p_vaddr)
24-31 Undefined for the System V ABI
32-39 Size of the segment in the file (p_filesz)
40-47 Size of the segment in memory (p_memsz)
48-55 The required alignment for this section (must be a power of 2)

Segment types: 0 = null - ignore the entry; 1 = load - clear p_memsz bytes at p_vaddr to 0, then copy p_filesz bytes from p_offset to p_vaddr; 2 = dynamic - requires dynamic linking; 3 = interp - contains a file path to an executable to use as an interpreter for the following segment; 4 = note section. There are more values, but mostly contain architecture/environment specific information, which is probably not required for the majority of ELF files.

Flags: 1 = executable, 2 = writable, 4 = readable.

ELF파일의 로딩

Elfdiagram.png
  1. ELF파일의 Magic이 일치하는지 확인한다.
  2. ELF파일의 첫부분에 있는 ELF헤더를 읽어서 ELF파일을 해석할 준비를 한다.
  3. ELF파일 헤더에 나타난 Executable헤더를 찾아가서 그 정보를 읽는다.
  4. 프로그램 헤더를 파싱해서, 프로그램 세그먼트를 몇개 로딩해야 하는지 구한다. 이떄 PT_LOAD type만이 로딩할수 있는 헤더이다.
  5. 각각의 로더블 세그먼트를 로딩한다.
    1. 각각의 세그먼트에 대한 Virtual memory를 할당하고 (p_vaddr)길이는 p_memsz만큼 할당한다.
    2. 세그먼트 데이터를 파일 오프셋 p_offset부터 p_filesz만큼 copy 한다.
    3. p_filesz와 p_memsz가 다르면 0으로 padding되었다는 것이다.