DT_SYMTAB 详解(含符号类型深度解析)

DT_SYMTAB.dynamic 段中的关键条目,其核心作用是定位动态符号表

以下是关于它的详细解析,重点深入剖析符号的类型与属性

1. 定义与指向
  • 定义DT_SYMTAB 条目存储的是一个虚拟内存地址。
  • 指向目标:该地址指向 .dynsym 段。
  • 本质:它告诉动态链接器:“所有的动态符号信息(如导入的函数名、导出的函数名)都在这个地址开始的地方”。
2. 核心结构:Elf64_Sym

.dynsym 段是一个数组,数组中的每一项都是一个 Elf64_Sym 结构体。

结构体定义:

typedef struct {
    Elf64_Word  st_name;  // 符号名在字符串表(.dynstr)中的偏移
    unsigned char st_info; // 【核心】符号类型与绑定属性(详见下文)
    unsigned char st_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

深度解读:

  1. 第 0 项(空符号)

    • Type: NOTYPE, Bind: LOCAL。这是符号表的第 0 个索引,作为未定义符号的“空指针”引用,强制为空。
  2. 第 1 项

    • Name: malloc:函数名。
    • Bind: GLOBAL:全局符号。
    • Ndx: UND (Undefined):说明这是一个导入符号ls 程序需要使用 malloc,但自己没定义,需要动态链接器去 libc.so 找。
    • Type: FUNC:它是一个函数。
  3. 第 4 项

    • Name: stderr:标准错误流。
    • Type: OBJECT:它是一个变量(数据对象),不是函数。
    • Bind: GLOBAL, Ndx: UND:这是一个导入的全局变量。
  4. 第 80 项

    • Name: main:主函数。
    • Ndx: 13:定义在本文件的第 13 号段(通常是 .text)。
    • Value: 0x4a30:有具体的地址值。
    • 作用:这是一个导出符号。虽然 ls 是可执行文件,但在某些调试或监控场景下,动态链接器需要知道 main 的地址。
  5. 第 81 项

    • Bind: WEAK:这是一个弱符号。如果链接时发现了同名的强符号,这个 usage 函数就会被覆盖。
5. 总结:DT_SYMTAB 的全貌

DT_SYMTAB 指向的符号表,通过 st_info 字段清晰地划分了符号的疆域:

  • 区分数据与代码:通过 STT_OBJECTSTT_FUNC 告诉链接器,解析后填入 GOT 的是数据指针还是函数入口地址。
  • 区分导入与导出:通过 Ndx (段索引) 配合 GLOBAL 绑定。UND 是进口,具体段号是出口。
  • 区分强弱:通过 GLOBALWEAK 决定符号覆盖的优先级规则。

这套机制构成了动态链接器查找和修正符号的“法理依据”。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐