ELF File in linux
Executable and Linking Format
ELF 格式文件在编译和运行过程有四种文件类型
- relocatable object file 可重定位的对象文件, 就是只编译不链接时一个.C文件生成的.o文件.
- 可执行文件
- 动态链接库文件.so
- coredump 文件

relocatable object file 工作在linking view 下, 各个section 只在链接时是需要的. section header table 描述所有section的信息,在linking view下section table 是必须存在的.
可执行文件和 动态链接库工作在executable view 下, 在执行视角下基本单位是segment
ELF 如何管理 section & segment
ELF header 结构中有e_shoff,e_shentsize, e_shnum
- e_shoff : ELF section header table 的相对于文件开头的offset
- e_shentsize: section header table 每个entry 的size
- e_shnum: section header table entry 的数量
对于progream header 相应的有e_phoff, e_phentsize, e_phnum
sections
section header table 是section header 组成的array, section header 管理每一个section 的信息.
section 是link view 下基本单元.
查看object file的section header
bash
readelf -S main.o
There are 14 section headers, starting at offset 0x410:
节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040
000000000000009d 0000000000000000 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 000002c0
0000000000000090 0000000000000018 I 11 1 8
[ 3] .data PROGBITS 0000000000000000 000000dd
0000000000000000 0000000000000000 WA 0 0 1
[ 4] .bss NOBITS 0000000000000000 000000dd
0000000000000000 0000000000000000 WA 0 0 1
[ 5] .rodata PROGBITS 0000000000000000 000000dd
0000000000000020 0000000000000000 A 0 0 1
[ 6] .comment PROGBITS 0000000000000000 000000fd
000000000000002f 0000000000000001 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 0000012c
0000000000000000 0000000000000000 0 0 1
[ 8] .note.gnu.pr[...] NOTE 0000000000000000 00000130
0000000000000030 0000000000000000 A 0 0 8
[ 9] .eh_frame PROGBITS 0000000000000000 00000160
0000000000000078 0000000000000000 A 0 0 8
[10] .rela.eh_frame RELA 0000000000000000 00000350
0000000000000048 0000000000000018 I 11 9 8
[11] .symtab SYMTAB 0000000000000000 000001d8
00000000000000c0 0000000000000018 12 4 8
[12] .strtab STRTAB 0000000000000000 00000298
0000000000000026 0000000000000000 0 0 1
[13] .shstrtab STRTAB 0000000000000000 00000398
0000000000000074 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),
D (mbind), l (large), p (processor specific)

symbol table section
object file 中symbol table section 用来记录符号的定义信息,包括符号名和地址,实现定位和重定位这个symbol. linker 使用符号表和重定位表完成链接操作. 首先object file 中调用操作执行时,它的目标就是一个符号,此时,会有一个符号项记录这个符号. symbol table 和reclocation entry 相互配合,一个类似一个拼图突出的部分(符号定义位置), 一个类似一个拼图凹进去的位置(符号的引用位置)
以这个简单程序为例:
c
#include <stdio.h>
int test_sum(int a, int b);
int test_sub(int a, int b)
{
return a - b;
}
int main(int argc, char *argv[])
{
int a = 1;
int b = 2;
int c = 0;
c = test_sum(a,b);
printf("test sum is %d\n", c);
c = test_sub(10, 5);
printf("test sub is %d\n", c);
return 0;
}
int test_sum(int a, int b)
{
return a + b;
}
bash
readelf -s main.o
Symbol table '.symtab' contains 8 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 .text
3: 0000000000000000 0 SECTION LOCAL DEFAULT 5 .rodata
4: 0000000000000000 18 FUNC GLOBAL DEFAULT 1 test_sub
5: 0000000000000012 119 FUNC GLOBAL DEFAULT 1 main
6: 0000000000000089 20 FUNC GLOBAL DEFAULT 1 test_sum
7: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
object file 有八个符号, 4,5,6是在file中定义的函数符号,它们的地址按照先后顺序排列. Ndx 表示符号关联的section 的index, 符号所在的section 是.text. 符号7 并没有在file中定义,他表示对这个符号的引用操作, Ndx 是UND.
Linker在链接的过程中,它会合并多个section, section的位置会变,并且生成可执行文件时会用virtual address 替换对应的st_value,链接的过程中,找到符号并修改符号的地址就是重定位.

编译后```bash
readelf -r main.o
重定位节 '.rela.text' at offset 0x2c0 contains 6 entries:
偏移量 信息 类型 符号值 符号名称 + 加数
000000000041 000600000004 R_X86_64_PLT32 0000000000000089 test_sum - 4
00000000004e 00030000000a R_X86_64_32 0000000000000000 .rodata + 0
000000000058 000700000004 R_X86_64_PLT32 0000000000000000 printf - 4
000000000067 000400000004 R_X86_64_PLT32 0000000000000000 test_sub - 4
000000000074 00030000000a R_X86_64_32 0000000000000000 .rodata + 10
00000000007e 000700000004 R_X86_64_PLT32 0000000000000000 printf - 4
的可执行文件中的符号表
```bash
Symbol table '.symtab' contains 36 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS crt1.o
2: 00000000004012f0 32 OBJECT LOCAL DEFAULT 18 __abi_tag
3: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtbegin.o
4: 00000000004003c0 0 FUNC LOCAL DEFAULT 4 deregister_tm_clones
5: 00000000004003f0 0 FUNC LOCAL DEFAULT 4 register_tm_clones
6: 0000000000400430 0 FUNC LOCAL DEFAULT 4 __do_global_dtors_aux
7: 000000000040300c 1 OBJECT LOCAL DEFAULT 25 completed.0
8: 0000000000402e00 0 OBJECT LOCAL DEFAULT 20 __do_global_dtor[...]
9: 0000000000400460 0 FUNC LOCAL DEFAULT 4 frame_dummy
10: 0000000000402df8 0 OBJECT LOCAL DEFAULT 19 __frame_dummy_in[...]
11: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c
12: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtend.o
13: 00000000004012a8 0 OBJECT LOCAL DEFAULT 16 __FRAME_END__
14: 0000000000000000 0 FILE LOCAL DEFAULT ABS
15: 0000000000402e08 0 OBJECT LOCAL DEFAULT 21 _DYNAMIC
16: 00000000004011a0 0 NOTYPE LOCAL DEFAULT 15 __GNU_EH_FRAME_HDR
17: 0000000000402fe8 0 OBJECT LOCAL DEFAULT 23 _GLOBAL_OFFSET_TABLE_
18: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_mai[...]
19: 0000000000403008 0 NOTYPE WEAK DEFAULT 24 data_start
20: 000000000040300c 0 NOTYPE GLOBAL DEFAULT 24 _edata
21: 0000000000400504 0 FUNC GLOBAL HIDDEN 5 _fini
22: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5
23: 0000000000403008 0 NOTYPE GLOBAL DEFAULT 24 __data_start
24: 0000000000400466 18 FUNC GLOBAL DEFAULT 4 test_sub
25: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
26: 0000000000401178 0 OBJECT GLOBAL HIDDEN 14 __dso_handle
27: 0000000000401170 4 OBJECT GLOBAL DEFAULT 14 _IO_stdin_used
28: 0000000000403010 0 NOTYPE GLOBAL DEFAULT 25 _end
29: 00000000004003b0 5 FUNC GLOBAL HIDDEN 4 _dl_relocate_sta[...]
30: 0000000000400380 38 FUNC GLOBAL DEFAULT 4 _start
31: 000000000040300c 0 NOTYPE GLOBAL DEFAULT 25 __bss_start
32: 0000000000400478 119 FUNC GLOBAL DEFAULT 4 main
33: 0000000000403010 0 OBJECT GLOBAL HIDDEN 24 __TMC_END__
34: 00000000004004ef 20 FUNC GLOBAL DEFAULT 4 test_sum
35: 000000000040033c 0 FUNC GLOBAL HIDDEN 2 _init
可以看到链接后的可执行文件三个符号对应的Ndx变成了4,三个符号的value 被修改为virtual address,但是三个符号的相对位置没有变化。
Relocation
重定位是连接符号引用和符号定义的过程。比如在一个object file 中定义了一个函数,它对应一个符号,在下面的code中有对这个函数的调用语句,有对这个符号的引用。当object file 被link 时,这段代码的位置发生了改变,引用这个符号的所有绝对跳转指令的符号地址部分都需要根据更新后代码的位置进行修改。Relocation entry 实际上记录的就是对一个符号的调用的位置. 当这个符号的地址准备好后,要更新的地址就是Relocation entry 记录的地方.

r_offset在object file 中表示相对符号所在section的offset,Relocation 时如何找到对应的section? r_info的低位byte表示relocation entry的type, 高位其他表示symbol table 的index,通过index 就能找到对应的symbol,symbol中又有所在的section 信息。
在可执行文件和动态链接库中r_offset 是virtual address.
bash
readelf -r main.o
重定位节 '.rela.text' at offset 0x2c0 contains 6 entries:
偏移量 信息 类型 符号值 符号名称 + 加数
000000000041 000600000004 R_X86_64_PLT32 0000000000000089 test_sum - 4
00000000004e 00030000000a R_X86_64_32 0000000000000000 .rodata + 0
000000000058 000700000004 R_X86_64_PLT32 0000000000000000 printf - 4
000000000067 000400000004 R_X86_64_PLT32 0000000000000000 test_sub - 4
000000000074 00030000000a R_X86_64_32 0000000000000000 .rodata + 10
00000000007e 000700000004 R_X86_64_PLT32 0000000000000000 printf - 4
Program loading and dynamic linking
可执行对象文件是process的静态表示。程序的加载和连接在运行视角下基于segment的过程与在链接视角下基于section的过程有很大不同。
- 程序的加载: 将elf format的文家加载到内存,使得进程能够运行
- 动态链接:在系统加载程序后,通过加载程序依赖的连接库文件来解决程序代码中的符号引用,这些链接库是进程的一部分。
Program Header
program header table 只有在可执行文件和动态链接库文件中存在. program header 描述segment,多个program header结构组成table,在ELF header中有指向它的地址. 当然在可执行文件和动态链接库文件中必然有section header table,在形成可执行文件时构成相应segment的seciton是排列在一起的.

查看可执行文件的segment
bash
readelf -l test_main
Elf 文件类型为 EXEC (可执行文件)
Entry point 0x400380
There are 13 program headers, starting at offset 64
程序头:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000002d8 0x00000000000002d8 R 0x8
INTERP 0x0000000000001000 0x0000000000401000 0x0000000000401000
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000511 0x0000000000000511 R E 0x1000
LOAD 0x0000000000001000 0x0000000000401000 0x0000000000401000
0x0000000000000310 0x0000000000000310 R 0x1000
LOAD 0x0000000000001df8 0x0000000000402df8 0x0000000000402df8
0x0000000000000214 0x0000000000000218 RW 0x1000
DYNAMIC 0x0000000000001e08 0x0000000000402e08 0x0000000000402e08
0x00000000000001d0 0x00000000000001d0 RW 0x8
NOTE 0x0000000000000318 0x0000000000400318 0x0000000000400318
0x0000000000000024 0x0000000000000024 R 0x4
NOTE 0x00000000000012b0 0x00000000004012b0 0x00000000004012b0
0x0000000000000040 0x0000000000000040 R 0x8
NOTE 0x00000000000012f0 0x00000000004012f0 0x00000000004012f0
0x0000000000000020 0x0000000000000020 R 0x4
GNU_PROPERTY 0x00000000000012b0 0x00000000004012b0 0x00000000004012b0
0x0000000000000040 0x0000000000000040 R 0x8
GNU_EH_FRAME 0x00000000000011a0 0x00000000004011a0 0x00000000004011a0
0x000000000000003c 0x000000000000003c R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000001df8 0x0000000000402df8 0x0000000000402df8
0x0000000000000208 0x0000000000000208 R 0x1
Section to Segment mapping:
段节...
00
01 .interp
02 .note.gnu.build-id .init .plt .text .fini
03 .interp .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .rodata .eh_frame_hdr .eh_frame .note.gnu.property .note.ABI-tag
04 .init_array .fini_array .dynamic .got .got.plt .data .bss
05 .dynamic
06 .note.gnu.build-id
07 .note.gnu.property
08 .note.ABI-tag
09 .note.gnu.property
10 .eh_frame_hdr
11
12 .init_array .fini_array .dynamic .got
查看那可执行文件的secion
bash
readelf -S test_main
There are 32 section headers, starting at offset 0x2928:
节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .note.gnu.bu[...] NOTE 0000000000400318 00000318
0000000000000024 0000000000000000 A 0 0 4
[ 2] .init PROGBITS 000000000040033c 0000033c
000000000000001b 0000000000000000 AX 0 0 4
[ 3] .plt PROGBITS 0000000000400360 00000360
0000000000000020 0000000000000010 AX 0 0 16
[ 4] .text PROGBITS 0000000000400380 00000380
0000000000000183 0000000000000000 AX 0 0 16
[ 5] .fini PROGBITS 0000000000400504 00000504
000000000000000d 0000000000000000 AX 0 0 4
[ 6] .interp PROGBITS 0000000000401000 00001000
000000000000001c 0000000000000000 A 0 0 1
[ 7] .gnu.hash GNU_HASH 0000000000401020 00001020
000000000000001c 0000000000000000 A 8 0 8
[ 8] .dynsym DYNSYM 0000000000401040 00001040
0000000000000060 0000000000000018 A 9 1 8
[ 9] .dynstr STRTAB 00000000004010a0 000010a0
000000000000004a 0000000000000000 A 0 0 1
[10] .gnu.version VERSYM 00000000004010ea 000010ea
0000000000000008 0000000000000002 A 8 0 2
[11] .gnu.version_r VERNEED 00000000004010f8 000010f8
0000000000000030 0000000000000000 A 9 1 8
[12] .rela.dyn RELA 0000000000401128 00001128
0000000000000030 0000000000000018 A 8 0 8
[13] .rela.plt RELA 0000000000401158 00001158
0000000000000018 0000000000000018 AI 8 23 8
[14] .rodata PROGBITS 0000000000401170 00001170
0000000000000030 0000000000000000 A 0 0 8
[15] .eh_frame_hdr PROGBITS 00000000004011a0 000011a0
000000000000003c 0000000000000000 A 0 0 4
[16] .eh_frame PROGBITS 00000000004011e0 000011e0
00000000000000cc 0000000000000000 A 0 0 8
[17] .note.gnu.pr[...] NOTE 00000000004012b0 000012b0
0000000000000040 0000000000000000 A 0 0 8
[18] .note.ABI-tag NOTE 00000000004012f0 000012f0
0000000000000020 0000000000000000 A 0 0 4
[19] .init_array INIT_ARRAY 0000000000402df8 00001df8
0000000000000008 0000000000000008 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000000402e00 00001e00
0000000000000008 0000000000000008 WA 0 0 8
[21] .dynamic DYNAMIC 0000000000402e08 00001e08
00000000000001d0 0000000000000010 WA 9 0 8
[22] .got PROGBITS 0000000000402fd8 00001fd8
0000000000000010 0000000000000008 WA 0 0 8
[23] .got.plt PROGBITS 0000000000402fe8 00001fe8
0000000000000020 0000000000000008 WA 0 0 8
[24] .data PROGBITS 0000000000403008 00002008
0000000000000004 0000000000000000 WA 0 0 1
[25] .bss NOBITS 000000000040300c 0000200c
0000000000000004 0000000000000000 WA 0 0 1
[26] .comment PROGBITS 0000000000000000 0000200c
000000000000002e 0000000000000001 MS 0 0 1
[27] .annobin.notes PROGBITS 0000000000000000 0000203a
000000000000014f 0000000000000001 MS 0 0 1
[28] .gnu.build.a[...] NOTE 0000000000405010 0000218c
0000000000000144 0000000000000000 0 0 4
[29] .symtab SYMTAB 0000000000000000 000022d0
0000000000000360 0000000000000018 30 18 8
[30] .strtab STRTAB 0000000000000000 00002630
00000000000001bd 0000000000000000 0 0 1
[31] .shstrtab STRTAB 0000000000000000 000027ed
000000000000013b 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),
D (mbind), l (large), p (processor specific)
保存有动态链接信息的section有.dynsym, .dynstr , .interp, .hash, .dynamic, .rel,
.rela, .got and.plt
Text segment

Data segment

Dynamic linking
有一个PT_INTERP 类型的segment 保存了program interpreter 的路径,在加载一个使用动态链接的可执行文件时,program interpreter 就是dynamic linker, 在加载可执行文件时经历这几个过程:
- 加载可执行文件的segment到进程地址空间
- 加载共享库到进程地址空间
- 关闭可执行文件的文件描述符
- 将执行控制权交给进程
Dynamic section
动态链接的程序有一个Dynamic segment, segment只有一个section 叫做Dynamic section, 它由一组dynamic structure 组成,每个structure 有不同的类型记录动态链接信息提供给dynamic linker 使用.

bash
readelf -d test_main
Dynamic section at offset 0x1e08 contains 24 entries:
标记 类型 名称/值
0x0000000000000001 (NEEDED) 共享库:[libc.so.6]
0x000000000000000c (INIT) 0x40033c
0x000000000000000d (FINI) 0x400504
0x0000000000000019 (INIT_ARRAY) 0x402df8
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x402e00
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x401020
0x0000000000000005 (STRTAB) 0x4010a0
0x0000000000000006 (SYMTAB) 0x401040
0x000000000000000a (STRSZ) 74 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x402fe8
0x0000000000000002 (PLTRELSZ) 24 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x401158
0x0000000000000007 (RELA) 0x401128
0x0000000000000008 (RELASZ) 48 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffffe (VERNEED) 0x4010f8
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x4010ea
0x0000000000000000 (NULL) 0x0
- DT_NEEDED 依赖的共享库名称
- DT_RELA 记录动态链接重定位表 可以看到0x401128 就是section rela.dyn的地址
- DT_JMPREL 记录PLT 的重定位表, 0x401158 是rela.plt的地址. 上边这些重定位表都排列在一起,组成一个大表
- DT_SONAME 记录动态链接库的soname, 这个soname也是依赖它的可执行程序NEEDED中要记录的名字
- DT_SYMTAB 记录动态链接符号表 0x401040 就是section .dynsym 的地址
- DT_PLTGOT 记录plt 的got 地址,0x402fe8就是section .got.plt 的地址