目标文件的段结构及核心组件详解
目标文件(如 .o
或 .obj
)是编译器生成的中间文件,其结构遵循 ELF(Linux)或 COFF(Windows)格式。以下是其核心段(Section)和关键机制的详细解析:
1. 目标文件的典型段结构
段名 | 存储内容 | 作用 |
---|---|---|
.text | 可执行机器指令(函数代码) | 存放编译后的二进制指令(如成员函数、静态函数) |
.data | 已初始化的全局/静态变量 | 存储显式初始化的数据(如 int x = 5; ) |
.bss | 未初始化的全局/静态变量(清零) | 仅记录变量大小,不占磁盘空间(运行时由系统初始化为0) |
.rodata | 只读数据(字符串常量、虚表、RTTI) | 存储不可修改的数据(如 const char* str = "Hello"; ) |
.symtab | 符号表(函数、变量名及地址) | 记录符号的元信息,供链接器解析跨文件引用 |
.rel.text | 代码段的重定位信息 | 标记 .text 中需要修正的指令(如 call 的目标地址) |
.rel.data | 数据段的重定位信息 | 标记 .data 中需要修正的指针(如跨文件的全局变量引用) |
.debug | 调试信息(DWARF 格式) | 供调试器使用(如行号、变量类型),发布版可剥离 |
.eh_frame | 异常处理信息(栈展开) | 支持 C++ 异常机制(如 try/catch ) |
2. 符号表(.symtab)详解
(1) 符号表的组成
符号表记录所有 函数、全局变量、静态变量 的信息,每条记录包含:
-
符号名(如
_ZN7MyClass8funcEv
,C++ 名称修饰后的名称) -
符号值(临时地址,通常是段内偏移)
-
符号类型(函数
FUNC
、数据OBJECT
、未定义UND
) -
绑定属性(全局
GLOBAL
、局部LOCAL
) -
所属段(如
.text
、.data
)
(2) 符号表示例
bash
readelf -s example.o
输出:
Num: Value Size Type Bind Vis Ndx Name1: 00000000 10 FUNC GLOBAL DEFAULT 1 _ZN7MyClass8funcEv2: 00000000 0 NOTYPE GLOBAL DEFAULT UND _Z3foov
-
Value=00000000
:临时地址(链接后修正)。 -
Ndx=1
:符号属于.text
段(通过节头表索引确定)。 -
UND
:未定义符号(需链接时解析)。
(3) 符号表的作用
-
链接阶段:解析跨文件引用(如
main.o
调用lib.o
的函数)。 -
调试阶段:映射机器码到源码符号(如
addr2line
工具)。
3. 重定位表(.rel.text / .rel.data)详解
(1) 重定位的用途
-
修正代码段(
.text
)和数据段(.data
)中的 临时地址,使其指向正确的最终地址。 -
解决跨文件引用问题(如调用其他
.o
文件的函数)。
(2) 重定位条目结构
每个条目包含:
-
偏移量(Offset):需修正的指令/数据在段内的位置。
-
类型(Type):如何修正(如绝对地址、相对偏移)。
-
符号(Symbol):目标符号名。
(3) 重定位表示例
bash
readelf -r example.o
输出:
Relocation section '.rel.text': OFFSET TYPE SYMBOL 00000005 R_X86_64_PC32 _Z3foov
-
OFFSET=0x05
:call
指令的操作数位于.text
段偏移0x05
处。 -
TYPE=R_X86_64_PC32
:需修正为 相对地址(目标地址 - 下条指令地址)。 -
SYMBOL=_Z3foov
:修正目标是函数foo()
。
(4) 重定位过程
-
链接器 合并所有
.text
段,分配最终地址(如foo()
的地址为0x401200
)。 -
计算修正值:
-
对于
R_X86_64_PC32
:修正值 = 目标地址(0x401200) - 下条指令地址(0x401005 + 5 = 0x40100A) = 0x1F6
-
-
修改指令:
asm
; 修正前(目标文件) call 0x00000000 ; 修正后(可执行文件) call 0x1F6 ; 实际编码为 E8 F6 01 00 00
4. 代码段(.text)与数据段(.data/.bss)
(1) 代码段(.text)
-
内容:所有函数的机器码(如
main()
、MyClass::func()
)。 -
特点:
-
只读可执行(防代码注入)。
-
函数调用通过
call
指令实现(地址由重定位修正)。
-
(2) 数据段(.data)
-
内容:已初始化的全局/静态变量(如
int x = 42;
)。 -
特点:
-
可读写,占用磁盘空间(存储初始值)。
-
加载到内存的 数据段。
-
(3) BSS 段(.bss)
-
内容:未初始化的全局/静态变量(如
int y;
)。 -
特点:
-
不占用磁盘空间(仅记录大小)。
-
运行时由系统初始化为0,加载到内存的 BSS 段。
-
5. 动态链接的特殊处理
(1) 全局偏移表(GOT)
-
存储动态库中符号的绝对地址(如
printf
)。 -
首次访问时由动态链接器填充。
(2) 过程链接表(PLT)
-
实现 延迟绑定,首次调用函数时触发动态链接器解析地址。
asm
复制
下载
call printf@plt ; 首次调用跳转到 PLT
6. 关键总结
组件 | 作用 | 示例工具命令 |
---|---|---|
.text | 存储可执行指令(函数代码) | objdump -d example.o |
.data | 存储已初始化的全局/静态变量 | readelf -x .data example.o |
.bss | 存储未初始化的全局/静态变量(运行时清零) | size example.o |
.symtab | 记录符号名、类型、地址(供链接器使用) | readelf -s example.o |
.rel.text | 标记代码段中需要修正的指令(如 call ) | readelf -r example.o |
.rel.data | 标记数据段中需要修正的指针(如跨文件变量引用) | objdump -r -j .data example.o |