본문 바로가기

Debugging/Linux

ELF Format #3 - Section Header

3. Section header

ELF file에는 여러 Section이 위치하고, 각 Section 관련 정보는 Section header에 보관됩니다.

또한, 각 Section은 고유의 이름을 가질 수 있고 주요 Section으로는 .text와 .data를 들 수 있습니다.

.text section은 프로그램의 instruction을 보관하고 있고, .data section은 프로그램 시작 전 초기화되는 값들을 보관하고 있습니다.

 

아래는 실행 파일과 오브젝트 파일의 Section header 정보 예제입니다.

예제1) 실행 파일

hwjung@jhaewon-z01:~$ readelf -S helloworld
There are 31 section headers, starting at offset 0x3978:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000318  00000318
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.gnu.propert NOTE             0000000000000338  00000338
       0000000000000020  0000000000000000   A       0     0     8
  [ 3] .note.gnu.build-i NOTE             0000000000000358  00000358
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .note.ABI-tag     NOTE             000000000000037c  0000037c
       0000000000000020  0000000000000000   A       0     0     4
  [ 5] .gnu.hash         GNU_HASH         00000000000003a0  000003a0
       0000000000000024  0000000000000000   A       6     0     8
  [ 6] .dynsym           DYNSYM           00000000000003c8  000003c8
       00000000000000a8  0000000000000018   A       7     1     8
  [ 7] .dynstr           STRTAB           0000000000000470  00000470
       0000000000000082  0000000000000000   A       0     0     1
  [ 8] .gnu.version      VERSYM           00000000000004f2  000004f2
       000000000000000e  0000000000000002   A       6     0     2
  [ 9] .gnu.version_r    VERNEED          0000000000000500  00000500
       0000000000000020  0000000000000000   A       7     1     8
  [10] .rela.dyn         RELA             0000000000000520  00000520
       00000000000000c0  0000000000000018   A       6     0     8
  [11] .rela.plt         RELA             00000000000005e0  000005e0
       0000000000000018  0000000000000018  AI       6    24     8
  [12] .init             PROGBITS         0000000000001000  00001000
       000000000000001b  0000000000000000  AX       0     0     4
  [13] .plt              PROGBITS         0000000000001020  00001020
       0000000000000020  0000000000000010  AX       0     0     16
  [14] .plt.got          PROGBITS         0000000000001040  00001040
       0000000000000010  0000000000000010  AX       0     0     16
  [15] .plt.sec          PROGBITS         0000000000001050  00001050
       0000000000000010  0000000000000010  AX       0     0     16
  [16] .text             PROGBITS         0000000000001060  00001060
       0000000000000185  0000000000000000  AX       0     0     16
  [17] .fini             PROGBITS         00000000000011e8  000011e8
       000000000000000d  0000000000000000  AX       0     0     4
  [18] .rodata           PROGBITS         0000000000002000  00002000
       0000000000000010  0000000000000000   A       0     0     4
  [19] .eh_frame_hdr     PROGBITS         0000000000002010  00002010
       0000000000000044  0000000000000000   A       0     0     4
  [20] .eh_frame         PROGBITS         0000000000002058  00002058
       0000000000000108  0000000000000000   A       0     0     8
  [21] .init_array       INIT_ARRAY       0000000000003db8  00002db8
       0000000000000008  0000000000000008  WA       0     0     8
  [22] .fini_array       FINI_ARRAY       0000000000003dc0  00002dc0
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .dynamic          DYNAMIC          0000000000003dc8  00002dc8
       00000000000001f0  0000000000000010  WA       7     0     8
  [24] .got              PROGBITS         0000000000003fb8  00002fb8
       0000000000000048  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000004000  00003000
       0000000000000010  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000004010  00003010
       0000000000000008  0000000000000000  WA       0     0     1
  [27] .comment          PROGBITS         0000000000000000  00003010
       0000000000000024  0000000000000001  MS       0     0     1
  [28] .symtab           SYMTAB           0000000000000000  00003038
       0000000000000618  0000000000000018          29    46     8
  [29] .strtab           STRTAB           0000000000000000  00003650
       0000000000000208  0000000000000000           0     0     1
  [30] .shstrtab         STRTAB           0000000000000000  00003858
       000000000000011a  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

 

예제2) 오브젝트 파일

hwjung@jhaewon-z01:~$ readelf -S functionA.o
There are 12 section headers, starting at offset 0x258:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       0000000000000013  0000000000000000  AX       0     0     1
  [ 2] .data             PROGBITS         0000000000000000  00000053
       0000000000000000  0000000000000000  WA       0     0     1
  [ 3] .bss              NOBITS           0000000000000000  00000053
       0000000000000000  0000000000000000  WA       0     0     1
  [ 4] .comment          PROGBITS         0000000000000000  00000053
       0000000000000025  0000000000000001  MS       0     0     1
  [ 5] .note.GNU-stack   PROGBITS         0000000000000000  00000078
       0000000000000000  0000000000000000           0     0     1
  [ 6] .note.gnu.propert NOTE             0000000000000000  00000078
       0000000000000020  0000000000000000   A       0     0     8
  [ 7] .eh_frame         PROGBITS         0000000000000000  00000098
       0000000000000038  0000000000000000   A       0     0     8
  [ 8] .rela.eh_frame    RELA             0000000000000000  000001d8
       0000000000000018  0000000000000018   I       9     7     8
  [ 9] .symtab           SYMTAB           0000000000000000  000000d0
       00000000000000f0  0000000000000018          10     9     8
  [10] .strtab           STRTAB           0000000000000000  000001c0
       0000000000000017  0000000000000000           0     0     1
  [11] .shstrtab         STRTAB           0000000000000000  000001f0
       0000000000000067  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

 

Section header는 다음 구조체로 정의되어 있습니다. 아래는 32bit 예제입니다.

 // Section header.
 struct Elf32_Shdr {
   Elf32_Word sh_name;
   Elf32_Word sh_type;
   Elf32_Word sh_flags;
   Elf32_Addr sh_addr;
   Elf32_Off sh_offset;
   Elf32_Word sh_size;
   Elf32_Word sh_link;
   Elf32_Word sh_info;
   Elf32_Word sh_addralign;
   Elf32_Word sh_entsize;
 };

 

위 구조체의 각 필드에 대해서 알아보겠습니다.

1) sh_name : section 이름

section 이름은 section header 중 string table section을 이용하여 표현합니다.

우선 ELF header에 있는 string table index 값을 확인합니다. 여기서는 30번째 section이 string table이라는 것을 의미합니다.

hwjung@jhaewon-z01:~$ readelf -h helloworld
ELF Header:
...
  Section header string table index: 30

 

다음 section 중 30번째 section을 확인합니다. 30번째 section의 이름을 보면 .shstrtab으로 되어 있는 것을 알 수 있습니다.

hwjung@jhaewon-z01:~$ readelf -S helloworld
There are 31 section headers, starting at offset 0x3978:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000318  00000318
       000000000000001c  0000000000000000   A       0     0     1
...
  [30] .shstrtab         STRTAB           0000000000000000  00003858
       000000000000011a  0000000000000000           0     0     1

 

section 이름을 찾기 위해서는 이 .shstrtab section의 주소와 section의 sh_name 값을 더 해서 위치 값을 계산해야 합니다.

sh_name을 찾기 위해 section header의 starting offset 값인 0x3978 근처를 hexdump로 확인합니다.다음과 같이 sh_name 값이 0x1b 인 것을 알 수 있습니다.

00003970  74 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |t...............|
00003980  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000039b0  00 00 00 00 00 00 00 00  1b 00 00 00 01 00 00 00  |................|
                                                           ## sh_name ## sh_type
000039c0  02 00 00 00 00 00 00 00  18 03 00 00 00 00 00 00  |................|
                  ## sh_flags                       ## sh_addr
000039d0  18 03 00 00 00 00 00 00  1c 00 00 00 00 00 00 00  |................|
                  ## sh_offset                      ## sh_size
000039e0  00 00 00 00 00 00 00 00  01 00 00 00 00 00 00 00  |................|
                  ## sh_link    ## sh_info     ## sh_addralign
000039f0  00 00 00 00 00 00 00 00  23 00 00 00 07 00 00 00  |........#.......|
                  ## sh_entsize

 

그러면, .shstrtab section 주소인 0x3858과 sh_name 값인 0x1b를 더하면, 0x3873이므로 해당 위치의 값을 Hexdump로 확인해보면 section의 이름인 .interp를 확인할 수 있습니다.

00003860  00 2e 73 74 72 74 61 62  00 2e 73 68 73 74 72 74  |..strtab..shstrt|
00003870  61 62 00 2e 69 6e 74 65  72 70 00 2e 6e 6f 74 65   |ab..interp..note|
00003880  2e 67 6e 75 2e 70 72 6f   70 65 72 74 79 00 2e 6e  |.gnu.property..n|
00003890  6f 74 65 2e 67 6e 75 2e   62 75 69 6c 64 2d 69 64  |ote.gnu.build-id|

 

2) sh_type : section 유형

 // Section types.
 enum : unsigned {
   SHT_NULL = 0,           // No associated section (inactive entry).
   SHT_PROGBITS = 1,       // Program-defined contents.
   SHT_SYMTAB = 2,         // Symbol table.
   SHT_STRTAB = 3,         // String table.
   SHT_RELA = 4,           // Relocation entries; Elf32_Rela 형태
   SHT_HASH = 5,           // Symbol hash table. dynamic linking의 경우 hash table 필요
   SHT_DYNAMIC = 6,        // Information for dynamic linking. dynamic section은 오직 1개
   SHT_NOTE = 7,           // Information about the file.
   SHT_NOBITS = 8,         // Data occupies no space in the file.
   SHT_REL = 9,            // Relocation entries; no explicit addends.
   SHT_SHLIB = 10,         // Reserved.
   SHT_DYNSYM = 11,        // Symbol table.
   ...
 }

 

3) sh_flags : section flag

 // Section flags.
 enum : unsigned {
   SHF_WRITE = 0x1, // Section data should be writable during execution.
   SHF_ALLOC = 0x2, // Section occupies memory during program execution.
   SHF_EXECINSTR = 0x4, // Section contains executable machine instructions.
   SHF_MERGE = 0x10, // The data in this section may be merged.
   SHF_STRINGS = 0x20, // The data in this section is null-terminated strings.
   SHF_INFO_LINK = 0x40U, // A field in this section holds a section header table index.
   SHF_LINK_ORDER = 0x80U, // Adds special ordering requirements for link editors.
   SHF_OS_NONCONFORMING = 0x100U, // This section requires special OS-specific processing to avoid incorrect behavior.
   SHF_GROUP = 0x200U, // This section is a member of a section group.
   SHF_TLS = 0x400U, // This section holds Thread-Local Storage.
   ...
}

 

4) sh_addr : section 시작 주소

5) sh_offset : file 내에서 section offset

6) sh_size : section 크기

 

7) sh_link : 다른 section으로의 포인터

symbol table(SHT_SYMTAB)과 dynamic section(SHT_DYNAMIC)의 경우, sh_link 값은 string table의 index를 제공합니다.

symbol hash table(SHT_GNU_HASH)과 relocation table(SHT_RELA)의 경우, sh_link 값은 관련된 symbol table의 index를 제공합니다.

 

8) sh_info : section 추가 정보

relocation table(RELA)의 경우, sh_info 값은 적용할 section의 index를 제공합니다.

 

9) sh_addralign : section 주소 정렬

10) sh_entsize : section에 보관된 record의 크기

 

Section 관련하여, 좀 더 자세히 정리해보겠습니다.

Section name Usage
.text 프로그램 code instruction 저장, Text Segment에 위치
.data 초기화된 전역 변수 등의 데이터 저장, Data Segment에 위치
.rodata 읽기 전용 데이터, 문자열 등이 저장
.bss 초기화 되지 않은 전역 데이터 저장, Data Segment에 위치
프로그램 로드 시에 0으로 초기화
.dynsym 공유 라이브러리에서 import된 동적 Symbol 정보, Text Segment에 위치
.dynstr 동적 Symbol String Table 정보
.symtab ElfN_Sym 형식의 Symbol 정보
.strtab .symtab section의 ElfN_Sym 구조체에 있는 st_name field에 의해 참조되는 Symbol String Table 정보
.rel.* relocation section은 ELF 객체 또는 프로세스 이미지의 어떤 부분이 Linking 시 또는 Runtime에 relocate 되어야 하는지 알려줌
.got.plt 이 section과 PLT 정보를 통해 import된 공유 라이브러리 함수에 접근 가능
Dynamic linker에 의해 Runtime에 정보 수정 가능
.hash Symbol 참조 hash table
.shstrtab Null terminator 문자열과 각 section 이름을 나타내는 section header
ELF header의 e_shstrndx 의 offset에 의해 참조

 

이상으로 ELF Format에 대한 내용을 마치겠습니다.

'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 #2 - Program Header  (0) 2022.10.08
ELF Format #1 - ELF Header  (0) 2022.10.03