1. 报错现场和第一反应
如果你也跟我一样,在编译 LLVM 的时候撞上这一行——collect2: fatal error: ld terminated with signal 9 [Killed]——先别急着怀疑源码或者编译器坏了。我最近在一台内存只有 4G 的小机器上交叉编译 LLVM 15.0.7,make -j4 跑了二十多分钟,所有编译目标差不多都结束了,最后链接 clang 可执行文件的时候,屏幕突然蹦出这条,终端好像被什么东西掐了一下,构建直接中断。
这个报错我踩过不止一次。第一次遇到时还以为是 ld 被某个脚本杀了,翻了不少资料,最后在 dmesg 里看到真正的元凶:内核的 OOM killer 把 ld 进程 Killed 了。简单说,不是 LLVM 源码有 bug,也不是 ld 本身坏了,而是物理内存加交换分区加在一起也不够用了。围绕 LLVM、编译报错、collect2、ld、signal 9 这几个关键词,网上能找到很多零散回答,但能一次性讲清原理、排查链路和解决方法的并不多,所以我把自己的实战过程整理出来。
1.1 这行报错到底是谁在报
很多刚接触 LLVM 源码编译的人会误以为 collect2 是某个编译器插件,其实它是 GCC 内部的一个链接辅助程序。GCC 在编译完目标文件之后,并不会直接调用 ld,而是先启动 collect2,由 collect2 去收集构造函数、析构函数等全局信息,再在背后调用真正的系统链接器 ld。
所以当你看到 collect2: fatal error: ld terminated with signal 9 [Killed],实际发生的是:collect2 启动了子进程 ld,但 ld 在链接过程中被外部信号杀掉了。collect2 从子进程退出状态里发现 ld 是被信号 9 终止的,于是把这条错误打出来。
为什么不是 ld: fatal error 直接报?因为 ld 根本没机会自己写错误消息,它不是正常退出,而是被强制杀死。这也是这个报错最迷惑人的地方:别人看到的错误信息包含具体文件、符号、内存不足等提示,而你的日志里只有孤零零一行 [Killed]。
1.2 signal 9 是“杀”,不是“崩”
signal 9 就是 SIGKILL,这条信号的特点是:进程不能拦截、不能忽略、不能优雅清理。无论是 kill -9 12345,还是内核 OOM killer 选择某个进程作为牺牲品,最终表现都一样——进程瞬间终止,不留任何 handler 执行机会。
如果链接器是自身逻辑错误或者遇到畸形输入,大概率会收到 signal 11(段错误)或者 signal 6(中止),而 signal 9 意味着有人在外部强制终止了它。在绝大多数编译 LLVM 的场景里,这个“外部”就是 Linux 内核的内存保护机制,也就是大家常说的 OOM killer。
OOM killer 筛选“牺牲品”时,一般会优先挑占用内存大、oom_score 高的进程。在链接阶段,ld 的 RSS 能轻松跑到 1GB 到 3GB 以上,一旦系统内存耗尽,它几乎每次都是第一个被盯上的目标。
1.3 先看一眼这几个最直接的证据
遇到 [Killed] 时,我建议先做三个动作,而不是马上换参数重编:
bash复制sudo dmesg -T | grep -Ei 'oom|killed process'
bash复制free -h
bash复制journalctl -k --since "10 minutes ago" | grep -Ei 'oom|killed'
第一段命令能直接看到内核有没有记录 OOM 事件。第二段命令看当前内存和 swap 剩余量。第三段命令是某些没有 dmesg 权限的发行版上的替代方案。
一般只要输出里有 Killed process 1234 (ld) ...,就可以把问题定性为内存不足。这时再往下查,比反复重编要高效得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 内存压力从哪来:LLVM 链接阶段的内存模型
很多人第一次编 LLVM 时会很困惑:我的机器有 8 核 CPU,make -j8 编译其他项目都很正常,为什么一到 LLVM 就崩?原因在于 LLVM 的链接阶段对内存的消耗是“脉冲式”的,而不是均匀的。
2.1 为什么 LLVM 的链接比普通 C 项目更耗内存
普通 C 项目编译出来是一堆很小的 .o 文件,链接成一个可执行文件时,符号量有限,ld 的工作量不大。但 LLVM 不一样,clang 可执行文件本质上要链接十几个大型静态库,像 libLLVMCore.a、libLLVMCodeGen.a、libLLVMTarget.a、libclangAST.a 等,每一个库都有成百上千个对象文件。
系统链接器 ld 要读取这些 .a 库中的符号索引和重定位信息,构建全局符号表、节区布局、重定位
