DT_SYMTAB 详解(含符号类型深度解析)
DT_SYMTAB是.dynamic段中的关键条目,其核心作用是定位动态符号表。
以下是关于它的详细解析,重点深入剖析符号的类型与属性。
1. 定义与指向
- 定义:
DT_SYMTAB条目存储的是一个虚拟内存地址。 - 指向目标:该地址指向
.dynsym段。 - 本质:它告诉动态链接器:“所有的动态符号信息(如导入的函数名、导出的函数名)都在这个地址开始的地方”。
2. 核心结构:Elf64_Sym
.dynsym段是一个数组,数组中的每一项都是一个Elf64_Sym结构体。
结构体定义:
typedefstruct{Elf64_Word st_name;// 符号名在字符串表(.dynstr)中的偏移unsignedcharst_info;// 【核心】符号类型与绑定属性(详见下文)unsignedcharst_other;// 符号可见性(Visibility)Elf64_Section st_shndx;// 符号所在的段索引(如 .text, .data)Elf64_Addr st_value;// 符号的值(地址或偏移)Elf64_Xword st_size;// 符号的大小(字节为单位)}Elf64_Sym;3. 重点详解:st_info(符号类型与绑定)
st_info是一个 8 位的字段,它打包了两个极其重要的信息:绑定属性和类型。
系统通过宏来拆解这个字段:
#define ELF64_ST_BIND(i) ((i) >> 4)// 高 4 位:绑定属性#define ELF64_ST_TYPE(i) ((i) & 0xf)// 低 4 位:符号类型
3.1 符号绑定属性:高 4 位
这决定了符号的可见性和链接行为。
| 值 | 名称 | 含义 | 动态链接中的作用 |
|---|---|---|---|
| 0 | STB_LOCAL | 局部符号 | 不参与动态链接。这些符号仅在目标文件内部可见(如static函数)。动态链接器会忽略它们。.dynsym中通常不包含此类符号。 |
| 1 | STB_GLOBAL | 全局符号 | 动态链接的核心。对其他目标文件可见。如果在当前文件中定义,则它是导出符号;如果未定义(st_shndx == SHN_UNDEF),则它是导入符号。 |
| 2 | STB_WEAK | 弱符号 | 弱引用/弱定义。类似于全局符号,但优先级较低。如果链接器找到了同名的GLOBAL符号,则使用全局符号;否则使用弱符号。常用于允许覆盖的默认实现。 |
3.2 符号类型:低 4 位
这决定了符号代表什么实体。
| 值 | 名称 | 含义 | 动态链接中的处理 |
|---|---|---|---|
| 0 | STT_NOTYPE | 未指定类型 | 未知类型。有时用于未定义的绝对符号或特殊目的。 |
| 1 | STT_OBJECT | 数据对象 | 变量(如全局变量int global_var)。动态链接器解析后,会将变量的真实地址填入 GOT 表,代码通过 GOT 访问该变量。 |
| 2 | STT_FUNC | 函数 | 函数或可执行代码(如printf)。这是最常见的情况。解析后,函数的真实地址会被填入 PLT/GOT 表。 |
| 3 | STT_SECTION | 段符号 | 代表某个段(如.text段本身)。主要用于重定位,通常不出现在.dynsym中。 |
| 4 | STT_FILE | 文件符号 | 源文件名。用于调试,链接器会忽略它。 |
| 10 | STT_GNU_IFUNC | 间接函数 | 高级特性。符号的值不是函数地址,而是一个“解析函数”的地址。运行时先调用这个解析函数,返回值才是真正的函数地址。常用于实现多版本函数调度(如 glibc 的memcpy根据CPU型号选择最优实现)。 |
4. 实例分析:readelf --dyn-syms
让我们通过readelf看看真实的符号表,验证上述理论。
命令:readelf --dyn-syms /bin/ls
输出示例:
Symbol table '.dynsym' contains 85 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND malloc 2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND free 3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND abort 4: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND stderr ... 80: 0000000000004a30 312 FUNC GLOBAL DEFAULT 13 main 81: 0000000000004b20 32 FUNC WEAK DEFAULT 13 usage深度解读:
第 0 项(空符号):
Type: NOTYPE,Bind: LOCAL。这是符号表的第 0 个索引,作为未定义符号的“空指针”引用,强制为空。
第 1 项:
Name: malloc:函数名。Bind: GLOBAL:全局符号。Ndx: UND(Undefined):说明这是一个导入符号。ls程序需要使用malloc,但自己没定义,需要动态链接器去 libc.so 找。Type: FUNC:它是一个函数。
第 4 项:
Name: stderr:标准错误流。Type: OBJECT:它是一个变量(数据对象),不是函数。Bind: GLOBAL,Ndx: UND:这是一个导入的全局变量。
第 80 项:
Name: main:主函数。Ndx: 13:定义在本文件的第 13 号段(通常是.text)。Value: 0x4a30:有具体的地址值。- 作用:这是一个导出符号。虽然
ls是可执行文件,但在某些调试或监控场景下,动态链接器需要知道main的地址。
第 81 项:
Bind: WEAK:这是一个弱符号。如果链接时发现了同名的强符号,这个usage函数就会被覆盖。
5. 总结:DT_SYMTAB 的全貌
DT_SYMTAB指向的符号表,通过st_info字段清晰地划分了符号的疆域:
- 区分数据与代码:通过
STT_OBJECT和STT_FUNC告诉链接器,解析后填入 GOT 的是数据指针还是函数入口地址。 - 区分导入与导出:通过
Ndx(段索引) 配合GLOBAL绑定。UND是进口,具体段号是出口。 - 区分强弱:通过
GLOBAL与WEAK决定符号覆盖的优先级规则。
这套机制构成了动态链接器查找和修正符号的“法理依据”。