ELF文件格式
ELF文件格式
可执行与可链接格式(ELF,Executable and Linkable Format)
此前的写法是Extensible Linking Format,常被称为ELF格式
ELF格式是用于可执行文件、目标代码、共享库和核心转储的标准文件格式,用于类Unix系统(Unix-like)
参考资料
ELF标准官方网站:https://gabi.xinuos.com/ (opens new window)
Linux基金会参考规范:https://refspecs.linuxfoundation.org/ (opens new window)
参考视频链接:https://www.bilibili.com/video/BV1mR4y1g7s1/ (opens new window)
参考书籍:《程序员的自我修养——链接、装载与库》
ELF文件类型
| 中文名 | 英文名 | 后缀名 | 来源 | 功能 |
|---|---|---|---|---|
| 可重定位文件 | Relocatable Files | .o/.a | 编译器,汇编器 | 包含代码与数据,但地址未定,需要与其他目标文件链接才能形成可执行文件 |
| 可执行文件 | Executable File | 链接器 | 可直接加载到内存并运行的程序 | |
| 共享目标文件 | Shared Object File | .so | 动态库文件,在程序运行时被动态加载和链接 | |
| 核心转储文件 | Core Dump File | core | 操作系统 | 程序崩溃时内存映像,用于事后调试 |
ELF文件内部结构
ELF文件内部四大组成部分
| 英文名 | 中文名 | 内容概述 | 用途 |
|---|---|---|---|
| ELF Header | ELF文件头 | ELF文件基本信息 | |
| Program Header Table | 程序头表 | 描述系统应如何创建进程的内存映像 | 运行时加载 |
| Section Header Table | 节/段头表 | 描述Sections的位置,大小,类型,链接信息 | 链接时处理 |
| Sections | 节/段 | 实际代码,数据,符号等信息 |
备注
ELF Header必须位于文件最开始,其中包含Program Header Table和Section Header Table在ELF文件中偏移量用于寻址。因此其他部分在文件中的顺序不是绝对固定的,可通过ELF文件头中的字段找到各段的偏移量。
寻址关系:
- ELF Header --> Program Header Table和Section Header Table
- Section Header Table --> 各个Sections
程序头表,Program Header Table简写为PHT
节/段头表,Section Header Table简写为SHT
ELF文件类型与ELF内部四大组成部分的包含关系
| 文件类型 | ELF头 | PHT | SHT | Sections |
|---|---|---|---|---|
| 可重定位文件 | 必须 | 无 | 必须 | 必须 |
| 可执行文件 | 必须 | 必须 | 可选 | 可选 |
| 共享目标文件 | 必须 | 必须 | 可选 | 可选 |
| 核心转储文件 | 必须 | 必须 | 无 | 无 |
Q & A
- 为什么可执行文件和共享目标文件的SHT和Sections是可选的?而PHT是必须的?
- Section Header Table定义了Sections大小和位置,没有Section Header Table也就无法对应、区分各个Sections
- 但Sections并未消失,只是换了一种形式存在与ELF文件中
对于可执行文件或共享库,链接完成后通常会用strip工具移除Section Header Table,以减小文件体积;但Sections对应的数据仍然存在于文件中,通过Program Header Table映射到内存段,用于程序变为进程加载到内存。 - 可执行文件和共享目标文件SHT和Sections也可以保留以方便调试
- 为什么可重定位文件的SHT和Sections是必须的?而不需要PHT?
- 可重定位文件只有一个任务就是链接,链接需要若干可重定位文件的SHT和Sections提供各个段的详细信息以便将Sections合并为Segment
- 可重定位文件不需要也不能加载到内存中执行,不需要PHT
ELF header
ELF header首段16Bytes

| 英文名 | 中文名 | 概述 |
|---|---|---|
| Magic Number | 魔数 | 最开始的4个字节是7f 45 4c 46,用于标识这是ELF文件 |
| Class | 文件类型 | ELF32(32 位)/ELF64(64 位) |
| Data | 数据编码 | 字节序,LSB(小端序),MSB(大端序) |
| Version | ELF版本 | 通常是 1 |
| OS/ABI | 操作系统/ABI | 目标操作系统和ABI |
| ABI Version | ABI版本 | ABI的版本 |
| Type | 文件类型 | 标识文件的具体类型 ET_REL:可重定位文件(.o) ET_EXEC:可执行文件 ET_DYN:共享目标文件(.so)/位置无关可执行文件(PIE) ET_CORE:核心转储文件 |
| Machine | 机器类型 | 目标指令集架构 x86_64,ARM,MIPS,RISC-V |
| Entry point address | 入口点地址 | 程序开始执行的虚拟内存地址 对于可执行文件,这是 _start 函数的地址 (通常由C运行时库crt0.o提供) |
| Start of program headers | 程序头表偏移 | 程序头表(Program Header Table)在文件中的起始位置 |
| Start of section headers | 节头表偏移 | 节头表(Section Header Table)在文件中的起始位置 |
| Flags | 标志 | 与特定处理器架构相关的标志 |
| Size of this header | ELF 头大小 | |
| Size of program headers | 程序头表条目大小 | |
| Number of program headers | 程序头表条目数量 | |
| Size of section headers | 节头表条目大小 | |
| Number of section headers | 节头表条目数量 | |
| Section header string table index | 节名字符串表索引 | 指向.shstrtab 节的索引,该节存储了所有节名称的字符串。 |
Program Header Table
程序头表(PHT,Program Header Table)
运行时必需:描述了如何将文件的段(Segments)加载到内存,包括段的类型(如可加载段LOAD、动态链接段DYNAMIC)、在文件中的偏移、虚拟地址和内存中的对齐要求。操作系统用它来创建进程的内存映像。
将多个Sections组合成运行时的Segment
Section header table
节头表(SHT,Section Header Table)
链接时必需:提供合并和重定位的所有信息,指导链接器将多个.o文件组装成可执行文件或共享库。
输出表格中的关键字段
| 英文名 | 概述 | 备注 |
|---|---|---|
| Name | 节区名称 | |
| Type | 节区的类型 | |
| Address | 节区加载到内存时的虚拟地址 | |
| Off | 节区在 ELF 文件中的偏移量 | |
| Size | 节区的总大小 | |
| ES | 条目大小(Entry Size) | |
| Flg | 定义该节区的访问权限和属性 A (ALLOC):该节区在进程运行时需要占用内存。 X (EXEC):该节区的内容是可执行的,通常指代码。 W (WRITE):该节区在运行时内容是可修改的。 | |
| Lk | 链接(Link) | |
| Inf | 额外信息(Info) | |
| Al | 对齐(Alignment) |
Sections
节/段(Sections)
概述作用:包含程序的实际代码、数据、符号表、重定位信息、调试信息等。节是链接器 (ld ) 在编译链接阶段主要操作的对象。链接器将不同目标文件中的同名节(如 .text, .data) 合并,解析符号引用,进行重定位等。
常见Sections
| Sections | 功能 | 英文名 | 中文名 |
|---|---|---|---|
| .text | 程序机器指令代码 | ||
| .data | 已初始化全局变量,静态变量 | ||
| .bss | 未初始化的静态变量,初始化为0的全局变量和静态变量 只是占位符,不占据实际空间,当程序运行时,会在内存中分配这些变量,并初始化为0 记忆口诀:(bss,Better Save Space,更好节省空间) | ||
| .rodata | 只读数据(const常量,字符串) | ||
| .comment | 编译工具信息 | 注释信息节 | |
| .symtab | 存储函数和全局变量的信息 | Symbol Table | 符号表 |
| .strtab | 存储.symtab 中符号名称的字符串 | String Table | 字符串表 |
| .dynsym | 动态链接所需的符号子集 | 动态符号表 | |
| .dynstr | 存储.dynsym 中符号名称的字符串 | 动态字符串表 | |
| .debug | 调试信息 | ||
| .line | 原始C程序的行号和.text section中机器指令之间的映射 | ||
| .rel.text | .text节中需要重定位的位置信息 用于静态链接的目标文件 | Relocation Table | 代码重定位表 |
| .rel.data | data 节中需要重定位的位置信息 用于静态链接的目标文件 | 数据重定位表 | |
| .rel.dyn | 用于动态链接的运行时重定位信息 | 动态重定位表 | |
| .rel.plt | 用于动态链接的运行时重定位信息 | PLT 重定位表 | |
| .dynamic | 包含动态链接器所需的关键信息 | 动态信息节 | |
| .got | 动态链接时存储外部变量地址。 | Global Offset Table | 全局偏移表 |
| .plt | 用于动态链接时调用外部函数。包含跳转到动态链接器解析地址的存根代码。 | Procedure Linkage Table | 过程链接表 |
| .init | 包含程序初始化时(在main之前)执行的代码 | 初始化代码节 | |
| .fini | 包含程序退出时(在main返回后)执行的代码 | 终止代码节 | |
| .ctors | 指向全局构造函数(C++ 全局对象构造器,C 的 __attribute__((constructor)) 函数)的指针数组。 | 构造函数指针表 | |
| .dtors | 指向全局析构函数(C++ 全局对象析构器,C 的 __attribute__((destructor)) 函数)的指针数组。 | 析构函数指针表 | |
| .shstrtab | 存储所有节名称的字符串 由ELF头的e_shstrndx字段索引 | 节名称字符串表 |
GNU Binutils
GNU Binutils是用来处理许多格式的目标文件(包括elf文件)一整套的编程语言工具程序
常见工具如下表
| 工具名称 | 说明 |
|---|---|
| as | GNU汇编器 |
| ld | GNU链接器 |
| ar | 归档工具(.o文件和.a文件转换) |
| stripe | 去除符号表与调试信息 |
| nm | 列出目标文件中的符号表 |
| objcopy | 转换成二进制代码 |
| objdump | 反汇编 |
| readelf | 显示ELF文件详细信息 |
| string | 提取文件中的可打印字符串 |
其他工具
ldd:列出可执行文件或共享库所依赖的共享库
readelf
readelf -h
readelf -h
readelf -SW
readelf -s
readelf -d
示例
#查看ELF header
readelf -h main.o
#输出
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: ARM
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 720 (bytes into file)
Flags: 0x5000000, Version5 EABI
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 11
Section header string table index: 10
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#使用 -W (--wide) 参数,每行显示完整信息,避免截断
readelf -SW rmpc
#输出
There are 35 section headers, starting at offset 0xff6bc0:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .note.gnu.build-id NOTE 0000000000000350 000350 000024 00 A 0 0 4
[ 2] .init PROGBITS 0000000000000374 000374 00001b 00 AX 0 0 4
[ 3] .plt PROGBITS 0000000000000390 000390 000040 10 AX 0 0 16
[ 4] .text PROGBITS 00000000000003d0 0003d0 9661a2 00 AX 0 0 16
[ 5] .fini PROGBITS 0000000000966574 966574 00000d 00 AX 0 0 4
[ 6] .interp PROGBITS 0000000000967000 967000 00001c 00 A 0 0 1
[ 7] .gnu.hash GNU_HASH 0000000000967020 967020 00001c 00 A 8 0 8
[ 8] .dynsym DYNSYM 0000000000967040 967040 000fa8 18 A 9 1 8
[ 9] .dynstr STRTAB 0000000000967fe8 967fe8 00091b 00 A 0 0 1
[10] .gnu.version VERSYM 0000000000968904 968904 00014e 02 A 8 0 2
[11] .gnu.version_r VERNEED 0000000000968a58 968a58 0001f0 00 A 9 4 8
[12] .rela.dyn RELA 0000000000968c48 968c48 091428 18 A 8 0 8
[13] .rela.plt RELA 00000000009fa070 9fa070 000048 18 AI 8 26 8
[14] .rodata PROGBITS 00000000009fa100 9fa100 12512c 00 A 0 0 128
[15] .eh_frame_hdr PROGBITS 0000000000b1f22c b1f22c 0212a4 00 A 0 0 4
[16] .eh_frame PROGBITS 0000000000b404d0 b404d0 0f3d94 00 A 0 0 8
[17] .gcc_except_table PROGBITS 0000000000c34264 c34264 0562dc 00 A 0 0 4
[18] .note.gnu.property NOTE 0000000000c8a540 c8a540 000020 00 A 0 0 8
[19] .note.ABI-tag NOTE 0000000000c8a560 c8a560 000020 00 A 0 0 4
[20] .tdata PROGBITS 0000000000c8ba10 c8aa10 000028 00 WAT 0 0 8
[21] .tbss NOBITS 0000000000c8ba38 c8aa38 000258 00 WAT 0 0 8
[22] .init_array INIT_ARRAY 0000000000c8ba38 c8aa38 000010 08 WAo 0 0 8
[23] .fini_array FINI_ARRAY 0000000000c8ba48 c8aa48 000008 08 WA 0 0 8
[24] .data.rel.ro PROGBITS 0000000000c8ba50 c8aa50 0709b8 00 WA 0 0 8
[25] .dynamic DYNAMIC 0000000000cfc408 cfb408 000220 10 WA 9 0 8
[26] .got PROGBITS 0000000000cfc628 cfb628 0039d8 08 WA 0 0 8
[27] .data PROGBITS 0000000000d00000 cff000 000ed8 00 WA 0 0 8
[28] .bss NOBITS 0000000000d00f00 cffed8 002410 00 WA 0 0 128
[29] .comment PROGBITS 0000000000000000 cffed8 00009f 01 MS 0 0 1
[30] .annobin.notes PROGBITS 0000000000000000 cfff77 0000f7 01 MS 0 0 1
[31] .gnu.build.attributes NOTE 0000000000d05310 d00070 000144 00 0 0 4
[32] .symtab SYMTAB 0000000000000000 d001b8 0bf568 18 33 21048 8
[33] .strtab STRTAB 0000000000000000 dbf720 23733d 00 0 0 1
[34] .shstrtab STRTAB 0000000000000000 ff6a5d 00015e 00 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)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
objdump
objdump -d
objdump -S
objcopy
objcopy -O binary input.elf output.bin ELF文件转为纯二进制bin文件