链接

一、链接的第一性原理(总纲)

1.1 一个根本问题

CPU 只理解地址,不理解名字。

而人类程序员编写程序时,只能使用:

链接的本质任务,就是在这两种世界之间建立一个一致、可维护、可演进的映射体系。

1.2 链接的本质抽象

链接 = 符号空间 → 地址空间 → 运行时内存空间 的映射与一致性维护

链接器始终围绕三件事工作:

核心职责抽象含义
符号解析“这个名字指向谁?”
地址分配“它最终放在哪里?”
重定位“所有引用如何同步更新?”

后文中出现的 ELF、GOT、PLT、PIC、延迟绑定,都只是这三件事在不同约束条件下的实现策略


二、从程序文本到可执行体:全局视角

2.1 程序生命周期的稳定分层

从系统视角看,一个 C 程序会经历以下稳定阶段

  1. **文本阶段**:源代码(人类可读)
  2. **中间表示阶段**:汇编(架构相关)
  3. **目标文件阶段**:可重定位目标文件(局部一致性)
  4. **链接阶段**:全局一致性建立
  5. **加载阶段**:地址空间实例化
  6. **执行阶段**:CPU 按地址取指

链接器存在的根本原因:编译器只能保证“单个编译单元内”的正确性,而系统需要的是“整个程序”的一致性。


三、链接器的核心工作模型

3.1 两遍扫描模型(稳定抽象)

无论是静态链接还是动态链接,链接器都遵循一个高度稳定的模型:

第一遍:收集信息(建立全局视图)

第二遍:修正引用(维持一致性)

一句话总结:第一遍“决定世界长什么样”,第二遍“让所有人达成共识”。


四、目标文件:链接器的工作对象

4.1 目标文件的三种稳定形态

类型抽象角色
可重定位目标文件 (.o)局部世界
可执行目标文件完整世界
共享目标文件 (.so)可复用世界

4.2 Section 与 Segment 的抽象分工

同一个 ELF 文件,同时服务于两个视角


五、符号:名字如何变成地址

5.1 符号的稳定分类

5.2 符号解析的核心规则

本质目标:在存在歧义时,维持系统的确定性。


六、重定位:一致性如何被维护

6.1 为什么需要重定位

编译阶段:

链接阶段:

6.2 重定位的抽象公式

最终地址 = 符号地址 + 修正量 − 引用位置

这一定义与具体架构无关,是一种地址一致性约束


七、静态链接:一次性解决问题

7.1 定义

静态链接:在程序生成时,完成所有符号解析与重定位。

7.2 特点(系统视角)

静态链接追求的是确定性与封闭性


八、动态链接:将问题推迟

8.1 动态链接的核心思想

不是不链接,而是“晚一点再链接”。

链接被拆分为:

8.2 动态链接成立的前提


九、PIC / GOT / PLT:为不确定性而生

9.1 不确定性的来源

9.2 位置无关代码(PIC)

可以在任意地址执行而无需修改的代码。

核心策略:

9.3 GOT 与 PLT 的分工

机制抽象角色
GOT地址表(数据)
PLT跳转入口(控制)

十、延迟绑定:性能与灵活性的权衡

10.1 动机

10.2 本质机制

第一次调用时解析,之后直接使用结果。

通过运行时修改 GOT 实现。


十一、加载与运行:链接的终点

11.1 加载器的职责

11.2 最终状态

在 CPU 看来:一切都是已经解析好的内存地址


十二、系统级总结(稳定认知)

  1. 链接是编译系统中**最后一个全局视角阶段**
  2. 符号解析、地址分配、重定位是永恒三要素
  3. ELF 同时服务链接视角与执行视角
  4. 动态链接本质是"推迟一致性建立"
  5. PIC 是动态链接与 ASLR 的基础
  6. 虚拟内存是现代链接机制的根基
  7. 所有复杂性,都是为了在不确定性中维持秩序

关联内容(自动生成)