mold 3.0.0 是这款高速链接器的新大版本。官方在 2.42.1 发布说明中已预告,mold 从 C++ 重写为 Rust,这是 Rust 版本的首个发布,而 2.42.1 则是 C++ 版本的最后一版。mold 3.x 的目标是补齐与 GNU ld 之间剩余的兼容性差距,尤其是链接脚本支持,并为 mold 被 Linux 发行版采用为默认链接器铺路。
正文
mold 3.0.0 是高速链接器的新大版本。2.42.1 的发布说明中已经宣布,mold 从 C++ 重写为 Rust,这是 Rust 版本的第一个发布;2.42.1 则是 C++ 版本的最后一个发布。mold 3.x 的目标是补齐与 GNU ld 的剩余兼容性差距,尤其是链接器脚本支持,并为 mold 被 Linux 发行版采纳为默认链接器铺路。
mold 3.0 旨在直接替代 2.42.1。它接受相同的命令行选项,支持相同的目标架构,除下列 bug 修复外产生相同输出。链接性能与 2.42.1 相当。兼容性验证方式包括:在每个支持目标上运行测试套件,在大量真实负载和选项组合下比较链接器输出,以及构建所有 Gentoo 软件包。没有发现回归。
改用 Rust 也让 mold 在输入文件损坏时更安全。C++ 版本可能在这种输入上越界读取内存并以段错误崩溃;在 mold 3.0 中,这些读取会做边界检查,因此 mold 会在出错访问处 panic 停止。
自行构建 mold 时需要注意构建系统已变化,见下文。
构建变化
mold 现在用 Cargo 而不是 CMake 构建。需要 Rust 1.95 或更高版本以及 C 编译器。运行
cargo build --release构建 mold,运行./install-mold.sh安装;安装脚本接受PREFIX和DESTDIR。CMake 选项已移除。如果把 mold 的库安装到
$PREFIX/lib以外的目录,例如/usr/lib64,需要在构建和安装 mold 时都把MOLD_LIBDIR环境变量设为该目录,这样mold -run才能找到mold-wrapper.so。mold 不再依赖 oneTBB。和以前一样静态链接 mimalloc 3.5.3;使用
--features system-allocator可改为使用系统 malloc。mold 在可用时链接系统 zlib,并内置 zstd 和 BLAKE3;设置ZSTD_SYS_USE_PKG_CONFIG=1可链接系统 zstd。CMake 变量
MOLD_TARGETS已被 Cargo features 取代。和以前一样,发行版应为所有目标构建 mold。测试套件现在用
cargo test而不是ctest运行。install-build-deps.sh已由install-test-deps.sh取代,后者只在运行测试时需要。
Bug 修复与兼容性改进
修复了使用版本脚本或
--default-symver创建静态链接可执行文件时的崩溃。修复了输出文件同时也是输入文件时的崩溃或输出损坏,例如
mold -r -o foo.o foo.o bar.o。--gc-sections不再移除--init和--fini指定的函数。同名但大小不同的 common 符号现在采用最大大小和最严格对齐,与 GNU ld 和 lld 一致。
--icf=safe不再折叠从共享库导出的函数,--icf=all不再因--emit-relocs崩溃。共享库中的版本化引用如
foo@VER,不再拉入定义未版本化foo的归档成员。(#1657)修复了若干非确定性输出,涉及
--dependency-file、--repro、带大量同名 COMDAT 组的-r,以及一些动态重定位。修复了多个
-rbug:来自第二个及之后输入文件的代码中 C++ 异常出错;-x、-X和-s移除了重定位引用的符号;--gc-sections丢弃了重定位引用的数据;.ARM.exidx等SHF_LINK_ORDER段的sh_link未设置;--strip-debug与 DWARF type unit 一起使用时 mold 崩溃。R_X86_64_GOTOFF64、R_ARM_REL32等针对共享库中定义符号的 GOT 相对重定位曾静默得到错误地址,现在会正确处理或报错。(#1668)对于不能用于位置无关输出的 PC 相对重定位,错误信息现在会解释原因和修复方法。
以下情况现在会报错,而不是产生损坏输出或崩溃:
--defsym给共享库中定义的符号起别名;共享库中未定义的强隐藏符号,或与--unresolved-symbols=ignore-all一起出现时;--defsym的值无法放入 64 位;已分配段同时被压缩;--strip-all与--emit-relocs同时使用。--noinhibit-exec不再因引用被丢弃 COMDAT 组中的符号而崩溃。以单个短划线开头的选项现在按 GNU ld 的方式读取。例如
-entry=main曾被读作-e ntry=main。(#1671、#1670)--dynamic-list-data、--lto-pseudo-probe-for-profiling和--thinlto-index-only不再吞掉下一个参数,并且现在接受--no-color-diagnostics。如果版本脚本在多个版本节点中列出同一个符号,现在第一个优先,与 GNU ld 和 lld 一致。
修复了
-C、--chroot和--repro的相对路径问题。mold 不再在启动时预留 8 GiB 虚拟地址空间,因此可在
ulimit -v下工作。当
XDG_RUNTIME_DIR为空字符串时,MOLD_JOBS现在可以工作。--separate-debug-file创建的文件不再包含.gnu_debuglink段。(#1656)mold 不再创建无意义的
__start_EHDR、__stop_EHDR、__start_PHDR和__stop_PHDR符号。[AArch64][PPC32][ARM32] 修复了通过范围扩展 thunk 调用其他段中的静态函数的问题,这些调用在大型程序中会跳到错误地址,例如 Chromium 的 ARM64 debug 构建。
[AArch64] 增加了对更多重定位类型的支持,例如
R_AARCH64_TSTBR14和R_AARCH64_GOT_LD_PREL19。[ARM32] 增加了对更多重定位类型的支持,例如
R_ARM_ALU_PC_G0和R_ARM_THM_PC12,并修复了大端 ARM 的-r输出。[ARM32][i386]
-r不再破坏某些分支和 TLS 重定位引用的指令。[RISC-V][LoongArch] 修复了 relaxation 缩短
--icf=all折叠后的函数时的调用,以及 32 位对象中的R_RISCV_64和R_LARCH_64重定位。[RISC-V] 修复了 Clang 18 创建的目标文件中的 TLSDESC、带 TLSDESC relaxation 的
--emit-relocs、EF_RISCV_TSO标志、-r输出中的R_RISCV_ALIGN,以及 32 位 PC 相对重定位的溢出检查。[LoongArch] 增加了 extreme code model 中的 TLSDESC 支持,并修复了
*64_PC_HI12重定位的错误值和 TLS 重定位的虚假溢出错误。[PPC32][m68k] 对
-fpic代码使用的 16 位偏移来说过大的 GOT,现在会报错,而不是被静默截断。[PPC64] 修复了静态链接可执行文件中的 IFUNC 调用和手写汇编中的
@got引用,mold 现在也会为 PPC64V1 合成_savegpr0_*系列函数。[SH4] 修复了通过 PLT 调用返回结构体的函数时的崩溃,以及从
-r输出链接的程序中的 C++ 异常。[SPARC64] 修复了使用
R_SPARC_WDISP16的分支指令,以及-r输出中的R_SPARC_OLO10。
评论
讨论整体偏怀疑,主轴是这算 agent 时代的一次高效升级,还是为了追 Rust 潮流而放弃一个久经考验的 C 实现;反对者最实在的担忧集中在发行版 bootstrap 场景——mold 的 C 版本能极早构建并被全发行版复用,改成 Rust 后这条路就断了。
认为修好 C/C++ 的输入处理比重写更划算
- 质疑重写动机:边界检查、损坏输入处理这类问题,在 C/C++ 代码库里修起来应该不算太难,成本可能低于整体重写;同时追问重写过程中是否使用了 AI 工具。(较多人) > 对损坏输入做边界检查听上去是个理由,但修 C/C++ 代码库里的这类处理应该不会太难,为什么选择重写?(@uncle_kostya)
担心 C 版本消失会伤及发行版 bootstrap
- 有发行版维护者用 mold 的 C 版本作为 bootstrap 链接器来引导 Rust 自身编译,省下数小时,并能很早就把它设为全发行版默认链接器;现在只能 fork 出 C 版本永久维护。结论是 Rust 并非所有问题的正确工具。(高赞) > Rust 实际上并不是所有问题的正确工具。(@lrvick)
强调这是转译而非手工重写
- 指出「重写」暗示由人重新实现,而这次是转译,两者不是一回事。(较多人) > 这是转译,不是重写,重写意味着由人来写。(@WhereIsTheTruth)
认为「用 Rust 重写」正在变成一种推销话术
- 讽刺这已经成了新的销售话术,把语言迁移当作卖点。(少数人) > 软件 X 现在用 Rust 重写了,这就是新的销售话术。(@rvz)
对 AI 参与导致软件质量下滑感到悲观
- 有人直言不想要粗制滥造的链接器,质问为什么要把项目卖掉;也有人感叹可信、合乎伦理的软件数量正趋近于零。(较多人) > 我们生活的这个世界真不幸,可信、合乎伦理的软件数量正趋近于零。(@acedTrex)
惊讶于三周完成,但指出项目其实酝酿已久
- 原以为要花数月,差点忘了现在处于 agent 时代;随后编辑补充,从首次提交看项目已经开发了一段时间,并非三周从零开始。(较多人) > 我原本预期这是数月的重写,而不是三周。(@Aissen)
追问 mold 与 wild 的区别,以及为何不用 Zig
- 有人问重写后 mold 和只支持 Linux 的快速链接器 wild 还有何差异,wild 是否用了不同的数据结构;也有人遗憾没用 Zig,认为它在很多方面是更好的 C。(少数人) > 我以为 Zig 会是 mold 完美的重写语言,因为它在很多方面是更好的 C。(@mi_lk)
有价值的补充
- wild 是另一个只支持 Linux 的快速链接器,讨论中被拿来与 mold 对比,但没人给出两者数据结构的实质差异。
- 首次提交的时间点说明 Rust 版并非三周内从零写成,「三周重写」这个印象需要打折理解。