ELF file 是 Linux 使用的檔案格式。不管是要學習 linkers 或是反組譯,都需要了解 ELF file。本文章將介紹 ELF 檔案格式。
Table of Contents
ELF File
ELF(Executable and Linking Format)是一種 object file 格式。有三種主要 object file 型態:
- Relocatable files:它包含 code 和 data。Linkers 鏈結一個 relocatable file 與其他的 object files 來建立一個 executable 或 shared object file。
- Executable files:它包含 program,因此可以被執行。
- Shared object files:它包含 code 和 data。它有兩種使用方式:
- Linker 鏈結一個 shared object file 以及其他的 relocatable files 和 shared object files 來建立另一個 object file。
- Dynamic linker 結合它以及一個 executable file 和其他的 shared objects 來建立一個 process。
由以上的來看,當一個 object file 是 relocatable file 或 shared object file 時,它可以被 linker 存取。當它是 executable file 時,它可以被執行。因此,一個 ELF file 可以由兩種觀點來看,分別是 linking view 和 execution view,如下圖。
Assemblers 和 linkers 是用左邊的 linkable sections 觀點來看一個 ELF file。也就是說,他們對待一個 ELF file 為一個 relocatable 或 shred object file。對於他們來說,一個 ELF file 是由數個 sections 所組成。他們透過 section header table 來存取所有的 sections。
System loaders 是用右邊的 executable segments 觀點來看一個 ELF file。也就是說,他們對待一個 ELF file 為一個 executable 或 shared object file。對於他們來說,一個 ELF file 是由數個 segments 所組成。他們透過 program head table 來存取所有的 segments。
Relocatable files 有 section tables,executable files 有 program header table,而 shared object files 兩者都有。此外,一個 segment 通常由數個 sections 組成。

Data Representation
ELF 支援各種的 processors,因此它有 32-bit 和 64-bit 的版本。所有的欄位都一樣,只是欄位的長度不同。
| Purposes | 32-Bit Name | 32-Bit Size | 32-Bit Alignment | 64-Bit Name | 64-Bit Size | 64-Bit Alignment |
|---|---|---|---|---|---|---|
| Unsigned program address | ELF32_Addr | 4 | 4 | ELF64_Addr | 8 | 8 |
| Unsigned file offset | ELF32_Off | 4 | 4 | ELF64_Off | 8 | 8 |
| Unsigned medium integer | ELF32_Half | 2 | 2 | ELF64_Half | 2 | 2 |
| Unsigned integer | ELF32_Word | 4 | 4 | ELF64_Word | 4 | 4 |
| Signed integer | ELF32_Sword | 4 | 4 | ELF64_Sword | 4 | 4 |
| Unsigned long integer | ELF64_Xword | 8 | 8 | |||
| Signed long integer | ELF64_Sxword | 8 | 8 | |||
| Unsigned small integer | unsigned char | 1 | 1 | unsigned char | 1 | 1 |
ELF Header
我們可以在 /usr/include/elf.h 中,找到 ELF header 的定義。
#define EI_NIDENT (16)
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf64_Half e_type; /* Object file type */
Elf64_Half e_machine; /* Architecture */
Elf64_Word e_version; /* Object file version */
Elf64_Addr e_entry; /* Entry point virtual address */
Elf64_Off e_phoff; /* Program header table file offset */
Elf64_Off e_shoff; /* Section header table file offset */
Elf64_Word e_flags; /* Processor-specific flags */
Elf64_Half e_ehsize; /* ELF header size in bytes */
Elf64_Half e_phentsize; /* Program header table entry size */
Elf64_Half e_phnum; /* Program header table entry count */
Elf64_Half e_shentsize; /* Section header table entry size */
Elf64_Half e_shnum; /* Section header table entry count */
Elf64_Half e_shstrndx; /* Section header string table index */
} Elf64_Ehdr;我們將簡單地介紹每一個欄位,詳細的說明請參照 ELF Header。
| Fields | Descriptions |
|---|---|
| e_ident | 參照 e_ident。 |
| e_type | Object file type。ET_REL: 1 => Relocatable fileET_EXEC: 2 => Executable fileET_DYN: 3 => Shared object fileET_CORE: 4 => Core file |
| e_machine | 要求的 architecture。 ex: EM_386: 3 |
| e_version | Object file 版本。EV_CURRENT: 1 |
| e_entry | 如果此 file 是 executable file,這是 entry point 的 virtual address。否則,此為 0。 |
| e_phoff | Program header table 在檔案中的 offset。如果此檔案沒有 program header table,此為 0。 |
| e_shoff | Section header table 在檔案中的 offset。如果此檔案沒有 section header table,此為 0。 |
| e_flags | Processor-specific flags。 |
| e_ehsize | ELF header 的大小。 |
| e_phentsize | Program header table 的一個 entry 的大小。所有的 entries 都是相同的大小。 |
| e_phnum | Program header table 擁有的 entries 的個數。假如此檔案沒有 program header table,此為 0。 Program header table 的總大小為 e_phentsize * e_phnum bytes。 |
| e_shentsize | Section header table 的一個 entry 的大小。所有的 entries 都是相同的大小。 |
| e_shnum | Section header table 擁有的 entries 的個數。假如此檔案沒有 section header table,此為 0。 Section header table 的總大小為 e_shentsize * e_shnum bytes。 |
| e_shstrndx | 在 Section header table 裡,包含 section name 的 string table 的 index。 |
e_ident
e_ident 總共 16 bytes,其詳情可參照 ELF Identification。
| Fields | Bytes | Descriptions |
|---|---|---|
| EI_MAG | 4 | 檔案識別碼。 Magic number:7f 45 4c 46 => 7f E L F。 |
| EI_CLASS | 1 | 檔案的 class。ELFCLASS32: 1 => 32-bit objectsELFCLASS64: 2 => 64-bite object |
| EI_DATA | 1 | 資料編碼。ELFDATA2LSB: 1 => 2’s complement, little endianELFDATA2MSB: 2 => 2’s complement, big endian |
| EI_VERSION | 1 | ELF header 的版本。 |
| EI_OSABI | 1 | 特定 OS 或 ABI 的 ELF extensions。 |
| EI_ABIVERSION | 1 | 識別該檔案所針對的 ABI 版本。 |
| EI_PAD | 7 | 保留,全設為 0。 |
範例
現在讓我們來看一個真實的例子。我們會用以下的 hello.c 來當作範例。
// hello.c
#include <stdio.h>
int inited_var = 6;
int uninited_var;
int sum(int x, int y) {
return x * y;
}
int main() {
int s = sum(10, 20);
printf("The sum 10 and 20 is %d.\n", s);
return 0;
}我們先將 hello.c 編譯成 relocatable file,並利用 readelf -h 來讀取 hello.o 的 ELF header,如下。
$ gcc -c hello.c -o hello.o $ readelf -h hello.o 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: REL (Relocatable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x0 Start of program headers: 0 (bytes into file) Start of section headers: 904 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 0 (bytes) Number of program headers: 0 Size of section headers: 64 (bytes) Number of section headers: 13 Section header string table index: 12
我們再將 hello.c 編譯成 executable file,並利用 readelf -h 來讀取 hello 的 ELF header,如下。
$ gcc hello.c -o hello $ readelf -h hello 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: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x400440 Start of program headers: 64 (bytes into file) Start of section headers: 6528 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 9 Size of section headers: 64 (bytes) Number of section headers: 30 Section header string table index: 29
Sections
Section Header
Section header table 是一個 Elf32_Shdr 或 Elf64_Shdr 的 array。ELF header 的 e_shoff 指出 section header table 從檔案開頭的 offset,e_shnum 告訴我們 section header table 有多少 entries,而 e_shentsize 指出每一條 entry 的 size。Section header table 的 entry 定義如下。
/* Section header. */
typedef struct
{
Elf32_Word sh_name; /* Section name (string tbl index) */
Elf32_Word sh_type; /* Section type */
Elf32_Word sh_flags; /* Section flags */
Elf32_Addr sh_addr; /* Section virtual addr at execution */
Elf32_Off sh_offset; /* Section file offset */
Elf32_Word sh_size; /* Section size in bytes */
Elf32_Word sh_link; /* Link to another section */
Elf32_Word sh_info; /* Additional section information */
Elf32_Word sh_addralign; /* Section alignment */
Elf32_Word sh_entsize; /* Entry size if section holds table */
} Elf32_Shdr;
typedef struct
{
Elf64_Word sh_name; /* Section name (string tbl index) */
Elf64_Word sh_type; /* Section type */
Elf64_Xword sh_flags; /* Section flags */
Elf64_Addr sh_addr; /* Section virtual addr at execution */
Elf64_Off sh_offset; /* Section file offset */
Elf64_Xword sh_size; /* Section size in bytes */
Elf64_Word sh_link; /* Link to another section */
Elf64_Word sh_info; /* Additional section information */
Elf64_Xword sh_addralign; /* Section alignment */
Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;我們簡單地介紹一下每個欄位。
| Fields | Descriptions |
|---|---|
| sh_name | Section 名稱。 該值是 section header string table section 裡的 index。 |
| sh_type | Section 的 contents 和 semantics 的 type,請參照 sh_type。 |
| sh_flags | Flag bits,請參照 sh_flags。 |
| sh_addr | 假如此 section 是 loadable,此值是此 section 屬於的 address。 |
| sh_offset | 此 section 在檔案裡的 offset。 |
| sh_size | 此 section 的大小。 |
| sh_link | Section header table index link,根據 section 的 type 有所不同。 |
| sh_info | 額外的 information,根據 section 的 type 有所不同。 |
| sh_addralign | 有些 sections 的 address 有 alignment 限制。 |
| sh_entsize | 有些 sections 是一個有固定大小 entries 的 table。此值每個 entry 的大小。 |
sh_type
以下列出一部分的 sh_type,詳情請參照 Sections。
| Names | Values | Descriptions |
|---|---|---|
| SHT_NULL | 0 | 該 section 為無效。 |
| SHT_PROGBITS | 1 | 內容為程式碼。程式碼包含 code、data、和 debugger information。 |
| SHT_SYMTAB | 2 | 內容為 symbol table,包含所有給 regular linking 使用的 symbols。 |
| SHT_STRTAB | 3 | 內容為 string table。 |
| SHT_RELA | 4 | 內容為 relocation entries with explicit addends。 |
| SHT_HASH | 5 | 內容為 symbol hash table。 |
| SHT_DYNAMIC | 6 | 內容為 dynamic linking 的 information。 |
| SHT_NOTE | 7 | 內容為一些該檔案的資訊。 |
| SHT_NOBITS | 8 | 與 SHT_PROGBITS 相似,但在檔案中不佔任何空間。這是給 .bss 使用。它會在程式的 load time 時被 allocated。 |
| SHT_REL | 9 | 內容為 relocation entries without explicit addends。 |
| SHT_SHLIB | 10 | 保留。 |
| SHT_DYNSYM | 11 | 內容為 symbol table,包含一些給 dynamic linking 使用的 symbols。 |
sh_flags
以下列出一部分的 sh_flags,詳情請參照 Sections。
| Names | Values | Descriptions |
|---|---|---|
| SHF_WRITE | 0x1 | 該 section 包含 data,且當它被 loaded 後,是 writable。 |
| SHF_ALLOC | 0x2 | 當 program 被 loaded,該 section 佔據一些 memory。 |
| SHF_EXECINSTR | 0x4 | 該 section 包含可執行的 machine code。 |
Special Sections
雖然我們可以定義任何的 sections,但是也有一些常用的 sections。以下列出一些常用的 sections,了解它們將有助於我們更了解 ELF files。這些 sections 都有 prefix .。其他更多的 special sections,請參照 Special Sections。
| Names | Types | Descriptions |
|---|---|---|
| .bss | SHT_NOBITS | 未初始化的 data。在檔案中,沒有佔據任何空間。 |
| .comment | SHT_PROGBITS | 版本資訊。 |
| .data .data1 | SHT_PROGBITS | 已初始化的 data。 |
| .debug | SHT_PROGBITS | Symbolic debugging 資訊。 |
| .dynamic | SHT_DYNAMIC | Dynamic linking 資訊。 |
| .dynstr | SHT_STRTAB | Dynamic linker symbol table 的字串。 |
| .dynsym | SHT_DYNSYM | Dynamic linking symbol table。 |
| .got | SHT_PROGBITS | Global offset table。 |
| .hash | SHT_HASH | Symbol hash table。 |
| .line | SHT_PROGBITS | Symbolic debugging 的行號資訊。 |
| .note | SHT_NOTE | Note section。 |
| .plt | SHT_PROGBITS | Procedure linkage table。 |
| .relname | SHT_REL | Relocation 資訊。 .rel.text 是相對於 .text 的 relocation section。 .rel.data 是相對於 .data 的 relocation section。 |
| .relaname | SHT_RELA | Relocation 資訊。 .rela.text 是相對於 .text 的 relocation section。 .rela.data 是相對於 .data 的 relocation section。 |
| .rodata .rodata1 | SHT_PROGBITS | 不可寫的 segment 的唯讀 data。 |
| .shstrtab | SHT_STRTAB | Section names。 |
| .strtab | SHT_STRTAB | Symbol table 的字串。 |
| .symtab | SHT_STRTAB | Symbol table。 |
| .tbss | SHT_PROGBITS | 未初始化的 thread-local data,在檔案中,沒有佔據任何空間。 |
| .tdata | SHT_PROGBITS | 已初始化的 thread-local data。 |
| .text | SHT_PROGBITS | 可執行的 instructions。 |
範例
我們可以利用 readelf -S 來讀取 section headers。以下是 hello.o 的 section headers。
$ readelf -s hello.o
There are 13 section headers, starting at offset 0x388:
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
0000000000000048 0000000000000000 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 000002a8
0000000000000048 0000000000000018 I 10 1 8
[ 3] .data PROGBITS 0000000000000000 00000088
0000000000000004 0000000000000000 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 0000008c
0000000000000000 0000000000000000 WA 0 0 1
[ 5] .rodata PROGBITS 0000000000000000 0000008c
000000000000001a 0000000000000000 A 0 0 1
[ 6] .comment PROGBITS 0000000000000000 000000a6
000000000000002e 0000000000000001 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 000000d4
0000000000000000 0000000000000000 0 0 1
[ 8] .eh_frame PROGBITS 0000000000000000 000000d8
0000000000000058 0000000000000000 A 0 0 8
[ 9] .rela.eh_frame RELA 0000000000000000 000002f0
0000000000000030 0000000000000018 I 10 8 8
[10] .symtab SYMTAB 0000000000000000 00000130
0000000000000150 0000000000000018 11 9 8
[11] .strtab STRTAB 0000000000000000 00000280
0000000000000026 0000000000000000 0 0 1
[12] .shstrtab STRTAB 0000000000000000 00000320
0000000000000061 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)以下是 hello 的 section headers。
$ readelf -s hello
There are 30 section headers, starting at offset 0x1980:
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 0000000000400238 00000238
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000400254 00000254
0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.build-i NOTE 0000000000400274 00000274
0000000000000024 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400298 00000298
000000000000001c 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 00000000004002b8 000002b8
0000000000000060 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 0000000000400318 00000318
000000000000003f 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 0000000000400358 00000358
0000000000000008 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000400360 00000360
0000000000000020 0000000000000000 A 6 1 8
[ 9] .rela.dyn RELA 0000000000400380 00000380
0000000000000018 0000000000000018 A 5 0 8
[10] .rela.plt RELA 0000000000400398 00000398
0000000000000048 0000000000000018 AI 5 23 8
[11] .init PROGBITS 00000000004003e0 000003e0
000000000000001a 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 0000000000400400 00000400
0000000000000040 0000000000000010 AX 0 0 16
[13] .text PROGBITS 0000000000400440 00000440
00000000000001b2 0000000000000000 AX 0 0 16
[14] .fini PROGBITS 00000000004005f4 000005f4
0000000000000009 0000000000000000 AX 0 0 4
[15] .rodata PROGBITS 0000000000400600 00000600
000000000000002a 0000000000000000 A 0 0 8
[16] .eh_frame_hdr PROGBITS 000000000040062c 0000062c
000000000000003c 0000000000000000 A 0 0 4
[17] .eh_frame PROGBITS 0000000000400668 00000668
0000000000000114 0000000000000000 A 0 0 8
[18] .init_array INIT_ARRAY 0000000000600e10 00000e10
0000000000000008 0000000000000008 WA 0 0 8
[19] .fini_array FINI_ARRAY 0000000000600e18 00000e18
0000000000000008 0000000000000008 WA 0 0 8
[20] .jcr PROGBITS 0000000000600e20 00000e20
0000000000000008 0000000000000000 WA 0 0 8
[21] .dynamic DYNAMIC 0000000000600e28 00000e28
00000000000001d0 0000000000000010 WA 6 0 8
[22] .got PROGBITS 0000000000600ff8 00000ff8
0000000000000008 0000000000000008 WA 0 0 8
[23] .got.plt PROGBITS 0000000000601000 00001000
0000000000000030 0000000000000008 WA 0 0 8
[24] .data PROGBITS 0000000000601030 00001030
0000000000000008 0000000000000000 WA 0 0 4
[25] .bss NOBITS 0000000000601038 00001038
0000000000000008 0000000000000000 WA 0 0 4
[26] .comment PROGBITS 0000000000000000 00001038
000000000000002d 0000000000000001 MS 0 0 1
[27] .symtab SYMTAB 0000000000000000 00001068
0000000000000630 0000000000000018 28 46 8
[28] .strtab STRTAB 0000000000000000 00001698
00000000000001dd 0000000000000000 0 0 1
[29] .shstrtab STRTAB 0000000000000000 00001875
0000000000000108 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)String Table
String Table
String table 包含了一堆 null-terminated 字串。Object file 使用這些字串來表示 symbol 和 section names。String table 的第一個 byte 必定為 \0。
Section header 的 sh_name 會包含 string table 的 index。假設一個 section header 的 sh_name 的值是 11,則表示的字串是 able。


範例
我們可以利用 readelf -p name 來讀取 string table。只要 section 的 type 是 SHT_STRTAB 的話,它就是一個 string table。以下是 hello.o 所有的 string tables。
$ readelf -p .strtab hello.o String dump of section '.strtab': [ 1] hello.c [ 9] uninited_var [ 16] sum [ 1a] main [ 1f] printf $ readelf -p .shstrtab hello.o String dump of section '.shstrtab': [ 1] .symtab [ 9] .strtab [ 11] .shstrtab [ 1b] .rela.text [ 26] .data [ 2c] .bss [ 31] .rodata [ 39] .comment [ 42] .note.GNU-stack [ 52] .rela.eh_frame
以下是 hello 所有的 string tables。
$ readelf -p .dynstr hello String dump of section '.dynstr': [ 1] libc.so.6 [ b] printf [ 12] __libc_start_main [ 24] __gmon_start__ [ 33] GLIBC_2.2.5 $ readelf -p .strtab hello String dump of section '.strtab': [ 1] crtstuff.c [ c] __JCR_LIST__ [ 19] deregister_tm_clones [ 2e] __do_global_dtors_aux [ 44] completed.6355 [ 53] __do_global_dtors_aux_fini_array_entry [ 7a] frame_dummy [ 86] __frame_dummy_init_array_entry [ a5] hello.c [ ad] __FRAME_END__ [ bb] __JCR_END__ [ c7] __init_array_end [ d8] _DYNAMIC [ e1] __init_array_start [ f4] __GNU_EH_FRAME_HDR [ 107] _GLOBAL_OFFSET_TABLE_ [ 11d] __libc_csu_fini [ 12d] _edata [ 134] printf@@GLIBC_2.2.5 [ 148] __libc_start_main@@GLIBC_2.2.5 [ 167] __data_start [ 174] __gmon_start__ [ 183] __dso_handle [ 190] sum [ 194] _IO_stdin_used [ 1a3] __libc_csu_init [ 1b3] uninited_var [ 1c0] __bss_start [ 1cc] main [ 1d1] __TMC_END__ $ readelf -p .shstrtab hello String dump of section '.shstrtab': [ 1] .symtab [ 9] .strtab [ 11] .shstrtab [ 1b] .interp [ 23] .note.ABI-tag [ 31] .note.gnu.build-id [ 44] .gnu.hash [ 4e] .dynsym [ 56] .dynstr [ 5e] .gnu.version [ 6b] .gnu.version_r [ 7a] .rela.dyn [ 84] .rela.plt [ 8e] .init [ 94] .text [ 9a] .fini [ a0] .rodata [ a8] .eh_frame_hdr [ b6] .eh_frame [ c0] .init_array [ cc] .fini_array [ d8] .jcr [ dd] .dynamic [ e6] .got [ eb] .got.plt [ f4] .data [ fa] .bss [ ff] .comment
Symbol Table
Symbol Table
Symbol table 包含了要 locate 和 relocate 一個程式的 symbolic definitions 和 references。一個 symbol table 的 entry 的定義如下。
typedef struct
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;
typedef struct
{
Elf64_Word st_name; /* Symbol name (string tbl index) */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf64_Section st_shndx; /* Section index */
Elf64_Addr st_value; /* Symbol value */
Elf64_Xword st_size; /* Symbol size */
} Elf64_Sym;
typedef struct
{
Elf32_Half si_boundto; /* Direct bindings, symbol bound to */
Elf32_Half si_flags; /* Per symbol flags */
} Elf32_Syminfo;
typedef struct
{
Elf64_Half si_boundto; /* Direct bindings, symbol bound to */
Elf64_Half si_flags; /* Per symbol flags */
} Elf64_Syminfo;我們簡單地介紹一下每個欄位。
| Fields | Descriptions |
|---|---|
| st_name | Symbol 名稱。 該值是 symbol string table 裡的 index。 |
| st_value | Symbol 的值。 |
| st_size | Symbol 的大小。 |
| st_info | Symbol 的 type 和 binding attributes。 請參照 Symbol Binding 和 Symbol Types。 |
| st_other | Symbol 的 visibility。 |
| st_shndx | 與該 symbol 相關的 section header table index。 |
Symbol Binding
Symbol table 的 st_info 的高四位表示 symbol binding。以下列出它的一部分的值,其他的值請參照 Symbol Table。
| Fields | Values | Descriptions |
|---|---|---|
| STB_LOCAL | 0 | Local symbols 無法被其他的 object files 看見。 |
| STB_GLOBAL | 1 | Global symbols 可被其他的 object files 看見。 |
| STB_WEAK | 2 | Weak symbols 類似於 global symbols,但它們的定義具有較低的優先級 |
Symbol Types
Symbol table 的 st_info 的低四位表示 symbol type。以下列出它的一部分的值,其他的值請參照 Symbol Table。
| Fields | Values | Descriptions |
|---|---|---|
| STT_NOTYPE | 0 | 未指定。 Not specified. |
| STT_OBJECT | 1 | Data object,如 variable、array、等等。 |
| STT_FUNC | 2 | Function 或其他可執行 code。 |
| STT_SECTION | 3 | Relocation 的 section。 |
| STT_FILE | 4 | Souece file 的名稱。 |
| STT_COMMON | 5 | 未初始化的 common 區塊。 |
範例
我們可以利用 readelf -s 來讀取 symbol table。以下是 hello.o 的 symbol tables。
$ readelf -s hello.o
Symbol table '.symtab' contains 14 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 8
8: 0000000000000000 0 SECTION LOCAL DEFAULT 6
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 inited_var
10: 0000000000000004 4 OBJECT GLOBAL DEFAULT COM uninited_var
11: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 sum
12: 0000000000000013 53 FUNC GLOBAL DEFAULT 1 main
13: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf以下是 hello.o 的 symbol tables。
$ readelf -s hello
Symbol table '.dynsym' contains 4 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
Symbol table '.symtab' contains 66 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000400238 0 SECTION LOCAL DEFAULT 1
2: 0000000000400254 0 SECTION LOCAL DEFAULT 2
3: 0000000000400274 0 SECTION LOCAL DEFAULT 3
4: 0000000000400298 0 SECTION LOCAL DEFAULT 4
5: 00000000004002b8 0 SECTION LOCAL DEFAULT 5
6: 0000000000400318 0 SECTION LOCAL DEFAULT 6
7: 0000000000400358 0 SECTION LOCAL DEFAULT 7
8: 0000000000400360 0 SECTION LOCAL DEFAULT 8
9: 0000000000400380 0 SECTION LOCAL DEFAULT 9
10: 0000000000400398 0 SECTION LOCAL DEFAULT 10
11: 00000000004003e0 0 SECTION LOCAL DEFAULT 11
12: 0000000000400400 0 SECTION LOCAL DEFAULT 12
13: 0000000000400440 0 SECTION LOCAL DEFAULT 13
14: 00000000004005f4 0 SECTION LOCAL DEFAULT 14
15: 0000000000400600 0 SECTION LOCAL DEFAULT 15
16: 000000000040062c 0 SECTION LOCAL DEFAULT 16
17: 0000000000400668 0 SECTION LOCAL DEFAULT 17
18: 0000000000600e10 0 SECTION LOCAL DEFAULT 18
19: 0000000000600e18 0 SECTION LOCAL DEFAULT 19
20: 0000000000600e20 0 SECTION LOCAL DEFAULT 20
21: 0000000000600e28 0 SECTION LOCAL DEFAULT 21
22: 0000000000600ff8 0 SECTION LOCAL DEFAULT 22
23: 0000000000601000 0 SECTION LOCAL DEFAULT 23
24: 0000000000601030 0 SECTION LOCAL DEFAULT 24
25: 0000000000601038 0 SECTION LOCAL DEFAULT 25
26: 0000000000000000 0 SECTION LOCAL DEFAULT 26
27: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
28: 0000000000600e20 0 OBJECT LOCAL DEFAULT 20 __JCR_LIST__
29: 0000000000400470 0 FUNC LOCAL DEFAULT 13 deregister_tm_clones
30: 00000000004004a0 0 FUNC LOCAL DEFAULT 13 register_tm_clones
31: 00000000004004e0 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux
32: 0000000000601038 1 OBJECT LOCAL DEFAULT 25 completed.6355
33: 0000000000600e18 0 OBJECT LOCAL DEFAULT 19 __do_global_dtors_aux_fin
34: 0000000000400500 0 FUNC LOCAL DEFAULT 13 frame_dummy
35: 0000000000600e10 0 OBJECT LOCAL DEFAULT 18 __frame_dummy_init_array_
36: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
37: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
38: 0000000000400778 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__
39: 0000000000600e20 0 OBJECT LOCAL DEFAULT 20 __JCR_END__
40: 0000000000000000 0 FILE LOCAL DEFAULT ABS
41: 0000000000600e18 0 NOTYPE LOCAL DEFAULT 18 __init_array_end
42: 0000000000600e28 0 OBJECT LOCAL DEFAULT 21 _DYNAMIC
43: 0000000000600e10 0 NOTYPE LOCAL DEFAULT 18 __init_array_start
44: 000000000040062c 0 NOTYPE LOCAL DEFAULT 16 __GNU_EH_FRAME_HDR
45: 0000000000601000 0 OBJECT LOCAL DEFAULT 23 _GLOBAL_OFFSET_TABLE_
46: 00000000004005f0 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
47: 0000000000601034 4 OBJECT GLOBAL DEFAULT 24 inited_var
48: 0000000000601030 0 NOTYPE WEAK DEFAULT 24 data_start
49: 0000000000601038 0 NOTYPE GLOBAL DEFAULT 24 _edata
50: 00000000004005f4 0 FUNC GLOBAL DEFAULT 14 _fini
51: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.2.5
52: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
53: 0000000000601030 0 NOTYPE GLOBAL DEFAULT 24 __data_start
54: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
55: 0000000000400608 0 OBJECT GLOBAL HIDDEN 15 __dso_handle
56: 000000000040052d 19 FUNC GLOBAL DEFAULT 13 sum
57: 0000000000400600 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
58: 0000000000400580 101 FUNC GLOBAL DEFAULT 13 __libc_csu_init
59: 0000000000601040 0 NOTYPE GLOBAL DEFAULT 25 _end
60: 0000000000400440 0 FUNC GLOBAL DEFAULT 13 _start
61: 000000000060103c 4 OBJECT GLOBAL DEFAULT 25 uninited_var
62: 0000000000601038 0 NOTYPE GLOBAL DEFAULT 25 __bss_start
63: 0000000000400540 53 FUNC GLOBAL DEFAULT 13 main
64: 0000000000601038 0 OBJECT GLOBAL HIDDEN 24 __TMC_END__
65: 00000000004003e0 0 FUNC GLOBAL DEFAULT 11 _initProgram Header
Program Header
Executable 或 shared object files 有 program header table。每一個 program header 描述一個 segment 或其他的 information。系統在準備執行 program 時,就會需要這些它們。一個 segment 包含一個或多個 sections。
ELF header 的 e_phnum 告訴我們 program header table 有多少 entries,而 e_phensize 指出每一條 entry 的 size。
以下是 program header 的定義。
typedef struct
{
Elf32_Word p_type; /* Segment type */
Elf32_Off p_offset; /* Segment file offset */
Elf32_Addr p_vaddr; /* Segment virtual address */
Elf32_Addr p_paddr; /* Segment physical address */
Elf32_Word p_filesz; /* Segment size in file */
Elf32_Word p_memsz; /* Segment size in memory */
Elf32_Word p_flags; /* Segment flags */
Elf32_Word p_align; /* Segment alignment */
} Elf32_Phdr;
typedef struct
{
Elf64_Word p_type; /* Segment type */
Elf64_Word p_flags; /* Segment flags */
Elf64_Off p_offset; /* Segment file offset */
Elf64_Addr p_vaddr; /* Segment virtual address */
Elf64_Addr p_paddr; /* Segment physical address */
Elf64_Xword p_filesz; /* Segment size in file */
Elf64_Xword p_memsz; /* Segment size in memory */
Elf64_Xword p_align; /* Segment alignment */
} Elf64_Phdr;我們簡單地介紹一下每個欄位。
| Fields | Descriptions |
|---|---|
| p_type | Segment type。 |
| p_flags | Segment flags。 |
| p_offset | 該 segment 在檔案裡的 offset。 |
| p_vaddr | 該 segment 在 memory 裡的 virtual address。 |
| p_paddr | 該 segment 在 memory 裡的 physical address。 System V 忽略 physical addressing。 |
| p_filesz | 該 segment 在檔案裡的大小。 |
| p_memsz | 該 segment 在 memory 裡的大小。 |
| p_align | 該 segment 在檔案和 memory 裡被 aligned 的值。 |
Segment Types
以下列出一部分 segment types,其他的值請參照 Program Header。
| Names | Values | Descriptions |
|---|---|---|
| PT_NULL | 0 | 未指定。 |
| PT_LOAD | 1 | Loadable segment。 |
| PT_DYNAMIC | 2 | Dynamic linking 資訊。 |
| PT_PHDR | 6 | Program header table 在檔案和 memory 的 location 和大小。 |
Segment Contents
一個 segment 包含一個或多個 sections,不過 program header 並知道這個事實。下圖是一個典型的 text segment 範例。它包含了唯讀的 instructions 和 data。

下圖是一個典型的 data segment 範例。它包含了可寫的 data 和 instructions。

範例
我們可以利用 readelf -l 來讀取 program header。以下是 hello 的 program header。hello.o 沒有 program header,因為它不是一個 executable file。
$ readelf -l hello
Elf file type is EXEC (Executable file)
Entry point 0x400440
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000000077c 0x000000000000077c R E 200000
LOAD 0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
0x0000000000000228 0x0000000000000230 RW 200000
DYNAMIC 0x0000000000000e28 0x0000000000600e28 0x0000000000600e28
0x00000000000001d0 0x00000000000001d0 RW 8
NOTE 0x0000000000000254 0x0000000000400254 0x0000000000400254
0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x000000000000062c 0x000000000040062c 0x000000000040062c
0x000000000000003c 0x000000000000003c R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
0x00000000000001f0 0x00000000000001f0 R 1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got 結語
本文章閱讀起來可能會覺得有點枯燥,因為它只是在介紹 ELF file。但是,了解 ELF file 是學習 linkers 的必經之路。
參考
- System V Application Binary Interface.
- John R. Levine, Linkers & Loaders.
- readelf, Linux manual page.








