[链接器的世界-实验篇03] 同一个名字,谁说了算:全局符号解析
本实验选择每个全局名称的定义,再将实际引用绑定到选中身份。强、弱、COMMON 的通用规则与局部名称隔离见原理篇 03。
起点:class3 代码与测试,位于 private 仓库,需要访问权限。输入 files 的每项是一个对象已验证的符号表,保留原始顺序和第 0 项。使用 class2 的记录,在 class3 crate 的库接口中实现两个接口。
实现任务
| 任务 | 接口 | 要达到的结果 |
|---|---|---|
| C3.1 | build_globals | 按输入顺序验证符号并选择全局定义,返回字节名称为键的 Globals |
| C3.2 | bind | 将一个 SymbolId 绑定到 Selection、未解析 WEAK 的零或明确错误 |
完整选择矩阵、类型和诊断规则见 class3 README,其中给出从两份输入表到 Globals、再到 bind 结果的示例。SymbolId 是文件序号与符号表下标,Selection 仍是输入身份,不是地址。
LOCAL 不参与同名全局竞争。强定义胜过 COMMON,COMMON 胜过弱定义;两个强定义报冲突,弱定义同级保留先出现者。COMMON 的 size 与 alignment 分别取最大值,保留首项身份,分配存储留到布局。每个符号先校验再参与选择,即使已有强定义也不能跳过后续损坏元数据。
建表允许没有定义的名称存在;只有实际调用 bind 时,未解析的必要引用才报错。bind 必须使用建表时同一组、未经改动的符号表。支持的 binding 与静态选择限制以手册为准,不实现动态 interposition。
一个最小矩阵是:先读到 COMMON(size=8, align=8),再读到 WEAK,仍保留 COMMON;随后读到 GLOBAL 节定义,才替换 COMMON。若两个 GLOBAL 定义先后出现,第二个立即产生重复定义错误。Globals 保存的仍是“第几个输入文件、第几个符号”的 SymbolId,不是已经计算出的虚拟地址;只有后续布局完成后,bind 的结果才会参与地址换算。
验收及其含义
在克隆后的仓库根目录、原生 x86-64 Linux 上执行:
cargo test --locked -p class0 -p class1 -p class2 -p class3cargo test --locked -p class0 -p class1 -p class2 -p class3 --releasepython3 scripts/grade.py class3合同测试检查强弱竞争、首个冲突、局部名称隔离、COMMON 独立最大值、按使用诊断、字节名称与错误元数据。交换输入顺序不改变强胜弱,但可能改变同级弱定义的选择,不能将确定性理解为对任意排列完全相同。
原生测试分别汇编调用方和定义方,通过 class1/class2 解析,再将调用方重定位引用绑定到提供方身份。它验证解析链能够选择正确对象,但尚未布局或执行链接产物。