静态链接

将可重定位目标文件链接为可执行目标文件。 两个主要任务:

  1. 符号解析:每个符号对应于一个函数、一个全局变量或一个静态变量。将每个符号引用正好与一个符号定义关联起来。
  2. 重定位:编译器和汇编器生成从地址 0 开始的代码和数据节,链接器将多个文件的数据和代码集成为单一的数据段和代码段,把每个符号定义与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,使得它们指向这个内存位置。

目标文件

三种形式:

  • 可重定位目标文件 .o:包含二进制代码和数据,可以与其他 .o 文件集成为可执行目标文件。
  • 可执行目标文件 .out:包含二进制代码和数据,可直接被复制到内存中执行。
  • 共享目标文件 .so:特殊类型的可重定位目标文件,可在运行时被动态地加载进内存并链接。

可重定位目标文件

ELF 文件格式

  • ELF Header:描述基本信息,如机器字长度、字节序、机器类型、目标文件类型等
  • .text:已编译程序的机器代码
  • .rodata:只读数据,常量字符串和跳转表等
  • .data:已初始化的全局和静态变量
  • .bss:未初始化或被初始化为 0 的全局和静态变量
  • .symtab:符号表,存放在程序中定义和引用的函数和全局变量的信息
  • .rel.text.text 段的重定位信息。 一个 .text 节中位置的列表,当链接器把这个目标文件和其他文件组合时,需要修改这些位置,可执行目标文件中通常省略
  • .rel.data.data 段的重定位信息。
  • .debug:调试符号表
  • 节头部表:描述不同节的位置和大小

符号和符号表

  • 全局符号:某一个模块定义,可以被其他模块引用的变量或函数符号,比如非静态函数和全局变量
  • 外部符号:由其他模块定义,该模块引用的全局符号
  • 局部符号:由某个模块定义,且仅由该模块引用的符号,比如静态全局变量或函数

符号解析

多重定义的全局符号的解析

  • 强符号:过程和初始化的全局变量
  • 弱符号:未初始化的全局变量
  • 不允许使用多个同名强符号
  • 存在一个强符号和多个弱符号同名,选择强符号
  • 有多个弱符号同名,任意选择一个

与静态库的链接

将一系列目标模块打包为一个静态库文件,链接时,链接器只复制被程序引用的目标模块

链接时命令行上的库和目标文件的顺序非常重要,在遇到库时,只会搜索前缀中未定义的引用,因此最好把库放在命令末尾,若多个库不相互独立,还需要对它们进行排序。

重定位

  • 重定位节和符号定义:将所有相同类型的节合并为同一类型的新的节,将运行时内存地址赋给新的节
  • 重定位节中的符号引用:修改代码节和数据节中对每个符号的引用,使它们指向正确的运行时地址,这依赖于重定位条目

重定位条目

  • offset:需要被修改的引用的偏移 (对应指令中的占位符的起始地址)
  • type:重定位类型
  • symbol:被修改引用指向的符号
  • addend:偏移调整 重定位类型 type
  • R_X86_64_PC32:重定位一个使用 32 位 PC 相对地址的引用
  • R_X86_64_32:重定位一个使用 32 位绝对地址的引用