Hacker News 中文摘要

RSS订阅

Mold Linker 3.0.0 发布,用 Rust 重写 -- Mold Linker Version 3.0.0 Release – Rewritten in Rust

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,以及一些动态重定位。

  • 修复了多个 -r bug:来自第二个及之后输入文件的代码中 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 版并非三周内从零写成,「三周重写」这个印象需要打折理解。