文章目录
引言
ELF(Executable and Linkable Format)是 Linux 及类 Unix 系统中用于可执行文件、目标文件、共享库和核心转储的标准二进制格式。作为连接编译、链接、加载与运行的关键桥梁,ELF 的设计兼顾灵活性与效率。对于从事操作系统内核、系统编程、二进制安全或嵌入式开发的工程师而言,深入理解 ELF 结构至关重要。本文将从整体布局出发,系统阐述 ELF 的核心组成部分,并重点厘清“节区”(Section)与“段”(Segment)这两个易混淆但本质不同的概念。
一、ELF 文件的整体布局
ELF 文件在磁盘上的结构遵循“头-内容-表”的组织范式,具体如下:
+------------------------+
| ELF 文件头 | ← 偏移 0,固定大小(64位为64字节)
+------------------------+
| 程序头表(可选/必需) | ← 由 e_phoff 指定,通常紧接文件头
+------------------------+
| 节区内容(Sections) | ← 多个节区按任意顺序分布,由 sh_offset 定位
+------------------------+
| 节区头表(可选/必需) | ← 由 e_shoff 指定,通常位于文件末尾
+------------------------+

该布局支持两类使用场景:
链接视图(Linking View):面向链接器,以节区为单位组织数据;
运行视图(Execution View):面向加载器,以段为单位映射内存。
不同类型的 ELF 文件侧重不同:
可重定位文件(.o):必须包含节区与节区头表,通常无程序头表;
可执行文件/共享库(a.out, .so):必须包含程序头表,节区头表可被剥离。
二、ELF 文件头(ELF Header)
位于文件起始,是解析整个 ELF 的入口。关键字段包括:

可通过 readelf -h 查看。
三、节区(Section)与节区头表(Section Header Table)
1. 节区:链接视角的逻辑单元
节区是 ELF 中承载特定类型数据的逻辑块,常见节区包括:
.text:可执行指令
.data:已初始化全局/静态变量
.bss:未初始化变量(SHT_NOBITS,不占文件空间)
.rodata:只读数据(如字符串常量)
.symtab / .dynsym:符号表
.strtab / .dynstr:字符串表
.rela.*:重定位信息
.dynamic:动态链接元数据
.shstrtab:节区名称字符串表
2. 节区头表:节区的元数据目录
节区头表是一个 Elf64_Shdr 结构数组,每个条目描述一个节区的属性:

节区头表是链接器工作的基础,但对加载器非必需。strip 命令可删除它而不影响程序运行。
四、段(Segment)与程序头表(Program Header Table)
1. 段:加载视角的内存映射单元
段是操作系统加载程序时的基本单位,由一个或多个节区按内存属性聚合而成。关键类型包括:
PT_LOAD:需加载到内存的段(通常有两个:代码段与数据段)
PT_INTERP:指定动态链接器路径(如 /lib64/ld-linux-x86-64.so.2)
PT_DYNAMIC:包含 .dynamic 节区,供动态链接器使用
PT_NOTE / PT_TLS / PT_GNU_STACK:辅助信息、TLS 模板、栈权限控制
2. 程序头表:段的加载说明书
程序头表是一个 Elf64_Phdr 结构数组,每个条目描述一个段的加载方式:

程序头表是加载器工作的依据,不可剥离。readelf -l 可查看。
五、节区与段的关系:从链接到加载

示例:一个简单 C 程序的映射
int global = 42; // → .data
int uninitialized; // → .bss
int main() { return global; } // → .text
节区:.text, .data, .bss
段:
Segment 1(R-X):包含 .text
Segment 2(RW-):包含 .data + .bss
内存布局(/proc/pid/maps):
00400000-00401000 r-xp ... /a.out ← Segment 1
00600000-00601000 rw-p ... /a.out ← Segment 2
注意:.bss 在文件中不占空间(sh_type = SHT_NOBITS),但 p_memsz > p_filesz,加载时由系统清零。
六、动态链接相关结构
对于共享库或 PIE 可执行文件,以下结构至关重要:
.dynamic 节区:包含 DT_NEEDED(依赖库)、DT_SYMTAB(符号表地址)等;
.dynsym / .dynstr:动态符号表与字符串表;
.got / .plt:全局偏移表与过程链接表,实现延迟绑定;
PT_DYNAMIC 段:包裹 .dynamic 节区,供动态链接器读取。
动态链接器(如 ld-linux.so)在程序启动时解析这些结构,完成符号重定位与库加载。
七、安全与优化机制
ASLR(地址空间布局随机化):依赖 ET_DYN 类型的 PIE 可执行文件;
NX(不可执行栈):通过 PT_GNU_STACK 段标记栈为不可执行;
RELRO(重定位只读):链接时启用 -z relro,使 .got.plt 在初始化后变为只读;
Strip 剥离:strip --strip-all 删除 .symtab, .debug_* 等节区,减小体积。
八、实用工具与命令

结语
ELF 文件格式通过“节区”与“段”的双重抽象,巧妙分离了链接时的逻辑组织与运行时的物理映射。理解这一设计,不仅能帮助我们分析二进制文件、调试程序崩溃、优化启动性能,更是深入操作系统内核(如 execve 系统调用、binfmt_elf.c 实现)、进行漏洞利用或逆向工程的基石。对于致力于系统底层技术的开发者,掌握 ELF,即是掌握程序从磁盘走向内存的生命脉络。
969

被折叠的 条评论
为什么被折叠?



