본문 바로가기

Debugging/Linux

ELF Format #2 - Program Header

 

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