ELF 文件格式深度解析:从节区到段,链接视图与运行视图的统一理解

引言

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,即是掌握程序从磁盘走向内存的生命脉络。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lcreek

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值