链接
一、链接的第一性原理(总纲)
1.1 一个根本问题
CPU 只理解地址,不理解名字。
而人类程序员编写程序时,只能使用:
- 函数名
- 变量名
- 模块名
链接的本质任务,就是在这两种世界之间建立一个一致、可维护、可演进的映射体系。
1.2 链接的本质抽象
链接 = 符号空间 → 地址空间 → 运行时内存空间 的映射与一致性维护
链接器始终围绕三件事工作:
| 核心职责 | 抽象含义 |
|---|---|
| 符号解析 | “这个名字指向谁?” |
| 地址分配 | “它最终放在哪里?” |
| 重定位 | “所有引用如何同步更新?” |
后文中出现的 ELF、GOT、PLT、PIC、延迟绑定,都只是这三件事在不同约束条件下的实现策略。
二、从程序文本到可执行体:全局视角
2.1 程序生命周期的稳定分层
从系统视角看,一个 C 程序会经历以下稳定阶段:
- **文本阶段**:源代码(人类可读)
- **中间表示阶段**:汇编(架构相关)
- **目标文件阶段**:可重定位目标文件(局部一致性)
- **链接阶段**:全局一致性建立
- **加载阶段**:地址空间实例化
- **执行阶段**:CPU 按地址取指
链接器存在的根本原因:编译器只能保证“单个编译单元内”的正确性,而系统需要的是“整个程序”的一致性。
三、链接器的核心工作模型
3.1 两遍扫描模型(稳定抽象)
无论是静态链接还是动态链接,链接器都遵循一个高度稳定的模型:
第一遍:收集信息(建立全局视图)
- 扫描所有输入目标文件
- 合并相同类型的段(Section)
- 收集符号定义与符号引用
- 构建**全局符号表**
- 为每个符号分配虚拟地址
第二遍:修正引用(维持一致性)
- 遍历所有符号引用点
- 根据符号表进行地址替换
- 完成重定位
一句话总结:第一遍“决定世界长什么样”,第二遍“让所有人达成共识”。
四、目标文件:链接器的工作对象
4.1 目标文件的三种稳定形态
| 类型 | 抽象角色 |
|---|---|
| 可重定位目标文件 (.o) | 局部世界 |
| 可执行目标文件 | 完整世界 |
| 共享目标文件 (.so) | 可复用世界 |
4.2 Section 与 Segment 的抽象分工
Section(节):
- 面向链接器
- 逻辑组织单位(.text / .data / .bss)
Segment(段):
- 面向加载器
- 运行时内存映射单位
同一个 ELF 文件,同时服务于两个视角:
- 链接视角(Section)
- 执行视角(Segment)
五、符号:名字如何变成地址
5.1 符号的稳定分类
- 全局符号:跨文件可见
- 外部符号:引用但未定义
- 局部符号:文件内部使用
5.2 符号解析的核心规则
- 强符号不能重名
- 强符号优先于弱符号
- 多个弱符号允许存在
本质目标:在存在歧义时,维持系统的确定性。
六、重定位:一致性如何被维护
6.1 为什么需要重定位
编译阶段:
- 编译器不知道最终地址
- 所有跨文件引用只能“占位”
链接阶段:
- 链接器确定最终布局
- 必须同步修正所有引用
6.2 重定位的抽象公式
最终地址 = 符号地址 + 修正量 − 引用位置
这一定义与具体架构无关,是一种地址一致性约束。
七、静态链接:一次性解决问题
7.1 定义
静态链接:在程序生成时,完成所有符号解析与重定位。
7.2 特点(系统视角)
- 启动快
- 独立完整
- 内存无法共享
- 升级与安全成本高
静态链接追求的是确定性与封闭性。
八、动态链接:将问题推迟
8.1 动态链接的核心思想
不是不链接,而是“晚一点再链接”。
链接被拆分为:
- 编译期:记录“依赖关系”
- 加载期 / 运行期:完成最终绑定
8.2 动态链接成立的前提
- 虚拟内存
- 地址空间隔离
- 共享代码段
九、PIC / GOT / PLT:为不确定性而生
9.1 不确定性的来源
- 共享库加载地址不固定
- 不同进程虚拟地址不同
- ASLR 引入随机性
9.2 位置无关代码(PIC)
可以在任意地址执行而无需修改的代码。
核心策略:
- 内部引用:相对偏移
- 外部引用:间接寻址
9.3 GOT 与 PLT 的分工
| 机制 | 抽象角色 |
|---|---|
| GOT | 地址表(数据) |
| PLT | 跳转入口(控制) |
十、延迟绑定:性能与灵活性的权衡
10.1 动机
- 避免启动时解析全部符号
- 降低冷启动成本
10.2 本质机制
第一次调用时解析,之后直接使用结果。
通过运行时修改 GOT 实现。
十一、加载与运行:链接的终点
11.1 加载器的职责
- 创建进程虚拟地址空间
- 映射可执行文件与共享库
- 启动动态链接器
11.2 最终状态
在 CPU 看来:一切都是已经解析好的内存地址。
十二、系统级总结(稳定认知)
- 链接是编译系统中**最后一个全局视角阶段**
- 符号解析、地址分配、重定位是永恒三要素
- ELF 同时服务链接视角与执行视角
- 动态链接本质是"推迟一致性建立"
- PIC 是动态链接与 ASLR 的基础
- 虚拟内存是现代链接机制的根基
- 所有复杂性,都是为了在不确定性中维持秩序
关联内容(自动生成)
- [/计算机系统/在系统上运行程序/异常控制流.html](/计算机系统/在系统上运行程序/异常控制流.html) 链接过程与异常控制流在系统级执行模型中密切相关,特别是在处理动态链接和运行时符号解析时涉及异常处理机制
- [/操作系统/内存管理.html](/操作系统/内存管理.html) 内存管理与链接过程密切相关,特别是重定位和虚拟内存的概念,是理解链接机制的重要基础
- [/编程语言/C.html](/编程语言/C.html) C语言的编译和链接过程是理解链接机制的重要实践基础,其中详细介绍了链接相关的概念
- [/编程语言/C++.html](/编程语言/C++.html) C++的链接特性(如符号解析)比C语言更复杂,是深入理解链接机制的重要参考
- [/操作系统/linux/内核.html](/操作系统/linux/内核.html) Linux内核支持链接与共享,了解内核层面的实现有助于理解链接机制
- [/操作系统/linux/Linux性能优化.html](/操作系统/linux/Linux性能优化.html) 动态链接库相关的性能优化,与链接过程密切相关