2. Program header
Program header는 ELF header 바로 다음에 위치한 Program header table에 있습니다.
각 Program header는 관련된 Section 정보를 가지고 있습니다.
프로그램을 시작할 때, OS Loader는 Program header에 있는 정보를 기반으로 Segment들을 Virtual Address Space에 위치시킵니다.
Segment는 Read Only, Read/Write, Executable 속성으로 분리됩니다.
hwjung@jhaewon-z01:~$ readelf -l helloworld
Elf file type is DYN (Shared object file)
Entry point 0x1060
There are 13 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000002d8 0x00000000000002d8 R 0x8
INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000005f8 0x00000000000005f8 R 0x1000
LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000
0x00000000000001f5 0x00000000000001f5 R E 0x1000
LOAD 0x0000000000002000 0x0000000000002000 0x0000000000002000
0x0000000000000160 0x0000000000000160 R 0x1000
LOAD 0x0000000000002db8 0x0000000000003db8 0x0000000000003db8
0x0000000000000258 0x0000000000000260 RW 0x1000
DYNAMIC 0x0000000000002dc8 0x0000000000003dc8 0x0000000000003dc8
0x00000000000001f0 0x00000000000001f0 RW 0x8
NOTE 0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000020 0x0000000000000020 R 0x8
NOTE 0x0000000000000358 0x0000000000000358 0x0000000000000358
0x0000000000000044 0x0000000000000044 R 0x4
GNU_PROPERTY 0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000020 0x0000000000000020 R 0x8
GNU_EH_FRAME 0x0000000000002014 0x0000000000002014 0x0000000000002014
0x0000000000000044 0x0000000000000044 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000002db8 0x0000000000003db8 0x0000000000003db8
0x0000000000000248 0x0000000000000248 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03 .init .plt .plt.got .plt.sec .text .fini
04 .rodata .eh_frame_hdr .eh_frame
05 .init_array .fini_array .dynamic .got .data .bss
06 .dynamic
07 .note.gnu.property
08 .note.gnu.build-id .note.ABI-tag
09 .note.gnu.property
10 .eh_frame_hdr
11
12 .init_array .fini_array .dynamic .got
여러 오브젝트 파일들은 Linker를 통해 Section을 Segment별로 통합하여, Program header에 정보를 기록합니다.
즉, Segment는 비슷한 종류의 Section을 모아놓은 것을 의미합니다.
참고로, 오브젝트 파일의 ELF File Format에는 Segments가 존재하지 않습니다.
hwjung@jhaewon-z01:~$ readelf -l functionA.o There are no program headers in this file. |
이제 Program header에 있는 내용을 보다 자세히 알아보겠습니다.
ELF Header에 있는 ELF Program header와 관련된 정보는 다음과 같습니다.
Start of program headers: 64 (bytes into file) Size of program headers: 56 (bytes) Number of program headers: 13 |
Program header의 구조체 정보는 다음과 같습니다. 이제 이 구조체의 Field 값들을 하나씩 알아보겠습니다.
# 32bit와 64bit 구조체 내의 Field 값 순서가 다소 차이가 있으니 주의하시기 바랍니다.
struct Elf64_Phdr {
Elf64_Word p_type;
Elf64_Word p_flags;
Elf64_Off p_offset;
Elf64_Addr p_vaddr;
Elf64_Addr p_paddr;
Elf64_Xword p_filesz;
Elf64_Xword p_memsz;
Elf64_Xword p_align;
};
1) p_type : Programe header가 가리키는 Segement의 유형
Type | Description | Value |
PT_NULL | Unused segment | 0x0 |
PT_LOAD | Loadable segment Text, Data segment가 이 segment 형식 |
0x1 |
PT_DYNAMIC | Dynamic linking information(아래는 주로 사용되는 정보) Runtime 중에 Link되는 Shared Library 목록 Global Offset Table(GOT) 주소 Relocation Table Entry 정보 Section에 Dynamic Array Tag 값으로 구분 |
0x2 |
PT_INTERP | Interpreter pathname(동적 링커) | 0x3 |
PT_NOTE | Auxiliary information | 0x4 |
PT_SHLIB | Reserved | 0x5 |
PT_PHDR | The program header table itself | 0x6 |
PT_TLS | The thread-local storage template | 0x7 |
PT_GNU_EH_FRAME | the location and size of the exception handling information | 0x6474e550 |
PT_GNU_STACK | Indicates stack executability | 0x6474e551 |
PT_GNU_RELRO | Read-only after relocation | 0x6474e552 |
PT_GNU_PROPERTY | .note.gnu.property notes sections | 0x6474e553 |
2) p_offset : Segment Offset
3) p_vaddr : Segment Virtual Address
4) p_paddr : Segment Physical Address
5) p_filesz : File 내에서 Segment 크기
6) p_memsz : Memory 내에서 Segment 크기
7) p_flags : Segment flag(PF_X, PF_W, PF_R, PF_MASKPROC)
8) p_align : Memory에서 Segment 정렬
아래 예제를 가지고, 실제 Program header에 어떻게 값이 Mapping 되어있는지 보도록 하겠습니다.
우선 ELF header에서 Program header의 Offset을 확인합니다.
아래 ELF header 정보에서 "Start of program headers"를 보면 ELF File내에 Program header의 Offset이 0x40(0n64)인 것을 알 수 있습니다.
hwjung@jhaewon-z01:~$ readelf -h helloworld
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x1060
Start of program headers: 64 (bytes into file)
Start of section headers: 14712 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 13
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 30
Hexdump를 이용하여, helloworld 파일을 16진수로 출력해보면 Program header 정보를 Mapping 시켜볼 수 있습니다.
여기서는 여러 Program header중 Program header 자체에 대한 정보를 가지고 있는 PT_PHDR 유형의 header를 다뤄보겠습니다.
먼저, readelf 도구로 program header를 조회해봅니다.
아래 결과를 보면, Type은 PHDR로 되어 있고, Offset 값이 우리가 위에서 확인했던 0x40으로 되어 있는 것을 알 수 있습니다.
hwjung@jhaewon-z01:~$ readelf -l helloworld | head
Elf file type is DYN (Shared object file)
Entry point 0x1060
There are 13 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000002d8 0x00000000000002d8 R 0x8
위 결과값을 Hexdump로 helloword 파일을 조회한 값과 비교하면 다음과 같이 그려볼 수 있습니다.
조금 더 깔끔하게 볼 수 있게 정리해보면 다음과 같습니다.
이 외에 Program header segment에 이어서 나오는 "INTERP", "LOAD" segment등도 동일하게 확인해 볼 수 있습니다.
hwjung@jhaewon-z01:~$ readelf -l helloworld | head -n 20
Elf file type is DYN (Shared object file)
Entry point 0x1060
There are 13 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000002d8 0x00000000000002d8 R 0x8
INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000005f8 0x00000000000005f8 R 0x1000
LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000
0x00000000000001f5 0x00000000000001f5 R E 0x1000
LOAD 0x0000000000002000 0x0000000000002000 0x0000000000002000
0x0000000000000160 0x0000000000000160 R 0x1000
LOAD 0x0000000000002db8 0x0000000000003db8 0x0000000000003db8
다음은 Segment 내에 위치한 Section 관련 정보를 담고 있는 Section header에 대해서 알아보겠습니다.
'Debugging > Linux' 카테고리의 다른 글
CPU Registers and Instructions - Instructions (0) | 2022.11.05 |
---|---|
CPU Registers and Instructions - Registers (0) | 2022.11.03 |
64bit Stack Walking (0) | 2022.10.14 |
ELF Format #3 - Section Header (0) | 2022.10.09 |
ELF Format #1 - ELF Header (0) | 2022.10.03 |