[链接器的世界-实验篇13] 地址也有语义:安全 ICF 与代码顺序
本实验在 class12 的 ET_EXEC 与静态 PIE 后端上实现保守 ICF 和代码排序。允许折叠的代码节共享内容,需要保留地址身份的节保持独立;符号、重定位、展开表和调试信息都必须与折叠后的布局一致。等价类细分与地址身份见原理篇 12,展开信息的比较依据见展开兼容性。
配套项目:class13 代码与测试,需要 private 仓库访问权限。复用 class8 的前端和存活性分析、class10 或 class11 的布局,以及 class12 的调试输出。折叠先于布局,保留输入身份和合并分片映射。提供的 link 与 link_pie 选择两种后端,CLI 用 --pie 选择静态 PIE。
实现任务
在 class13 crate 的库接口中完成三个接口,完整比较规则与诊断见 class13 README。
| 任务 | 接口 | 结果 |
|---|---|---|
| C13.1 | fold | 细分候选的等价类,生成别名到代表节的 Aliases 映射 |
| C13.2 | unwind_descriptions | 为存活代码构建按规定排序的规范化 CIE/FDE 描述 |
| C13.3 | link_with | 收集候选与地址显著性,串接折叠、排序、布局与 class12::finish |
候选仅为存活的 Text 输入节,折叠单位是整个节。每组以最小 SectionId 为代表;符号保留原节内偏移,别名的重定位不重复应用。order 在折叠后处理,指定别名等于指定代表节,重复请求稳定去重。
本实验采用明确的保守边界:比较原始字节、重定位引用与规范化展开描述,不归一化重定位字段;keep 和除 PLT32 外的存活非 NONE 重定位会固定目标候选。不读取 .llvm_addrsig,因此 PC32 调用也会阻止目标折叠。这些是项目合同,不等同于其他链接器的 safe ICF 策略。
展开比较只消除 FDE 中的 CIE 指针与初始地址字段;范围、augmentation、CFI 和填充仍参与比较。默认 ET_EXEC 与 PIE 流程保留展开信息,未支持的 personality 或 LSDA 编码必须报错。仅不需要运行时展开和回溯的程序,才可显式选择 AbortOnly;该策略仍处理地址显著性和调试数据。
例如,候选函数 A、B 的机器码相同,但 A 的调用目标落在候选类 X,B 的调用目标落在候选类 Y。第一次分区时它们可能暂时相同;把目标所在的等价类编号纳入签名后,下一轮就会把 A、B 分开。若 X、Y 后来又合并,仍需重新计算分区,直到编号不再变化。这个过程说明 fixed point 是输入引用图的一部分,不是对机器码做一次 memcmp 就能得到的结果。
先求稳定等价类,再改布局
fold 的输出是输入节身份之间的别名关系,不是地址表。先按字节、对齐、引用形状和展开描述建立初始分区,再反复按目标所在分区细分,直到达到不再变化的 fixed point;只有这时才能选代表并交给 link_with 删除副本、重定向引用和调试记录。把一次比较得到的“看起来相同”直接折叠,会漏掉递归目标或不同 CFI。
验收把地址显著性、FDE 兼容性、符号重定向和运行结果分开检查:函数指针/keep 保持不同地址,纯调用目标可以共享,折叠副本的 DWARF 变成 tombstone。无折叠样本还必须复现 class12,证明 ICF 层没有悄悄改变基础后端。
从任务到测试
| 任务 | 关键测试 | 测试所证明的性质 |
|---|---|---|
C13.1 fold | folding_propagates_through_calls、recursion_reaches_the_greatest_fixed_point | 等价类随引用图迭代到 fixed point,不能停在一次字节比较。 |
C13.2 unwind_descriptions | folding_requires_equal_runtime_unwind_descriptions | 机器码相同但展开语义不同的函数保持独立。 |
C13.3 link_with | address_taken_functions_stay_distinct、folding_preserves_merged_constants_and_compatible_unwind | 地址显著性、合并常量、GOT/TLS、展开和调试坐标在折叠后仍一致。 |
先看合同测试的等价关系,再看原生测试的地址、调试器和运行结果;“文件变小”不是 ICF 正确性的判据。
验收
在仓库根目录、原生 x86-64 Linux 上执行:
cargo test --locked -p class13cargo test --locked -p class13 --releasepython3 scripts/grade.py class13验收将代码身份与程序行为一起检查:
- 折叠关系:代表选择、引用差异、递归等价和候选排列独立性;允许折叠的函数共享地址,取地址或
keep保留的函数保持不同地址。 - 元数据一致性:折叠副本的调试范围写入 tombstone,只保留代表的有效描述;同字节、不同 CFI 的函数保持独立,未知异常编码被拒绝。
- 排序与组合:显式顺序、别名和无效请求,ET_EXEC 与 PIE 中的合并常量、GOT、TLS、展开表和调试信息,以及 CLI 与库输出一致性。
专门的无折叠、无排序样本要求复现 class12 对应后端的完整输出。空 order 不会关闭 ICF;有可折叠候选时仍会折叠。测试不要求任意输入文件排列都生成相同字节。