linker cc not found / failed to find tool
主线位置:三条主线之外,在构建阶段。握手主线里的每一次签名、密钥交换,记录主线里的每一次 AEAD,都落到
CryptoProvider背后的 C 和汇编代码上。aws-lc-sys 的构建脚本怎么在 cc 和 cmake 之间选、为什么默认路径不要 cmake,见设计取舍;本页只讲构建失败时看到什么、怎么修。
flowchart TB
b["cargo build"] --> bs["链接构建脚本"]
bs --> lk{"宿主有 cc"}
lk -->|没有| e1["linker cc not found"]
lk -->|有| cc["ring 或 aws-lc-sys"]
cc --> tc{"目标的 C 编译器"}
tc -->|找不到| e2["failed to find tool"]
tc -->|有| ar{"目标的 ar"}
ar -->|宿主 ar| e3["undefined symbol"]
ar -->|对| ok["构建成功"]
实验 crate:exp-rustls/pitb-nocc-ring(rustls 只开 std、tls12、ring)和 exp-rustls/pitb-nocc-aws(rustls 默认 feature,即 aws-lc-rs)。main 只建一个 ClientConfig,打印它的密钥交换组。每次都用新的目标目录从头构建。M2 Max、macOS 27、rustc 1.99.0,release,cc 1.10.0。
现象
本机没有 C 编译器。 用 env -i 清掉环境,PATH 只留 ~/.cargo/bin,两个 crate 各构建一次。ring 这边:
error: linker `cc` not found
|
= note: No such file or directory (os error 2)
error: could not compile `rustls` (build script) due to 1 previous error
warning: build failed, waiting for other jobs to finish...
error: could not compile `libc` (build script) due to 1 previous error
aws-lc-rs 那边一样,只是多了一行 error: could not compile `aws-lc-rs` (build script) due to 1 previous error。两边都没走到 ring 或 aws-lc-sys 编 C 那一步:失败的是构建脚本的链接,第一个报出来的是 rustls 自己的构建脚本。
交叉编译,缺目标的 C 编译器。 本机有 cc,但 --target x86_64-unknown-linux-gnu。ring:
warning: ring@0.17.14: Compiler family detection failed due to error: ToolNotFound: failed to find tool "x86_64-linux-gnu-gcc": No such file or directory (os error 2)
...
error: failed to run custom build command for `ring v0.17.14`
...
cargo:rerun-if-env-changed=CC_x86_64-unknown-linux-gnu
CC_x86_64-unknown-linux-gnu = None
cargo:rerun-if-env-changed=CC_x86_64_unknown_linux_gnu
CC_x86_64_unknown_linux_gnu = None
cargo:rerun-if-env-changed=TARGET_CC
TARGET_CC = None
cargo:rerun-if-env-changed=CC
CC = None
cargo:rerun-if-env-changed=CROSS_COMPILE
CROSS_COMPILE = None
...
--- stderr
error occurred in cc-rs: failed to find tool "x86_64-linux-gnu-gcc": No such file or directory (os error 2)
aws-lc-sys 0.47.0 先打印 Building with: CC,然后把同一句 Compiler family detection failed 刷了 42 遍(它在正式编译前要先编几个探测用的小文件,c11.c、stdalign_check.c、linux_random_h.c……每个都失败一次),最后同样停在 error occurred in cc-rs: failed to find tool "x86_64-linux-gnu-gcc"。没有一处提到 cmake。
原因
宿主侧:构建脚本本身要链接。 构建脚本是一个为宿主编译、再在宿主上运行的 Rust 程序,在 macOS 和 Linux 上 rustc 默认用 cc 当链接器。rustls 的 Cargo.toml 写着 build = "build.rs",这个脚本只为在 nightly 上打开 read_buf,平时 main 是空的,但它照样要编译、链接。libc 也有构建脚本。所以在一台完全没有 C 工具链的机器上,换一个纯 Rust 的 provider 也躲不过这一步。
目标侧:ring 和 aws-lc-sys 都用 cc-rs 编 C。 两个 provider 的 feature 各自打开一个后端:ring = ["dep:ring", "webpki/ring"],aws_lc_rs = [..., "aws-lc-rs/aws-lc-sys", ...]。cargo tree -e build,normal -i cc 看谁需要 C 编译器:
cc v1.10.0
[build-dependencies]
└── ring v0.17.14
├── rustls v0.23.46
│ └── pitb-nocc-ring v0.1.0 (~/exp-rustls/pitb-nocc-ring)
└── rustls-webpki v0.103.16
└── rustls v0.23.46 (*)
cc v1.10.0
└── cmake v0.1.58
[build-dependencies]
└── aws-lc-sys v0.47.0
...
[build-dependencies]
└── aws-lc-sys v0.47.0 (*)
aws-lc-sys 把 cmake crate 列为构建依赖,这个 crate 会被编译,但默认路径下不会去调 cmake 程序,构建脚本打印的 Building with: CC 说的就是这件事(判断条件见设计取舍)。rustls-webpki 也依赖同一个后端(树里的第二条路径),它验证书签名用的就是这个后端。
cc-rs 找编译器的顺序就在 ring 的输出里:CC_<目标>(连字符和下划线两种写法)、TARGET_CC、CC、CROSS_COMPILE,都没设就按目标三元组猜一个名字,x86_64-unknown-linux-gnu 猜的是 x86_64-linux-gnu-gcc。本机没有这个程序,于是 ToolNotFound。
修法
宿主侧:装上系统的 C 工具链。 同一条 env -i 命令,只把 /usr/bin:/bin 加回 PATH(macOS 上 /usr/bin/cc 是 Xcode 命令行工具的 clang):
Finished `release` profile [optimized] target(s) in 8.33s
kx groups: [X25519, secp256r1, secp384r1]
目标侧:编译器、归档工具、链接器三样都要给目标配。 本机没有 x86_64 的 Linux 交叉 gcc,实验借用 zig:tools/zigcc-x86_64-linux-gnu 是一个十几行的包装脚本,去掉 cc-rs 传的 --target=...,执行 zig cc -target x86_64-linux-gnu。只配 CC_x86_64_unknown_linux_gnu 和链接器 CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER,ring 的构建脚本这次成功了,最终链接却报了 85 个未定义符号:
ld.lld: error: undefined symbol: ring_core_0_17_14__x25519_scalar_mult_adx
...
error: could not compile `pitb-nocc-ring` (bin "pitb-nocc-ring") due to 1 previous error
C 和汇编都编成了 ELF 目标文件,但打包静态库时 cc-rs 找不到 x86_64-linux-gnu-ar,退回了宿主的 ar 和 ranlib。macOS 的 ranlib 不认 ELF,生成的 .a 没有符号索引。它的抱怨只写在构建脚本的输出文件里:
cargo:warning=ranlib: warning: archive member '4f701d2e06800872-p256-x86_64-asm-elf.o' not a mach-o file
构建脚本成功时,cargo 不显示依赖 crate 的 cargo:warning,所以终端上看不到这一行。再加上 AR_x86_64_unknown_linux_gnu(指向 zig ar),清掉目标目录重来,两个 crate 都能构建:
pitb-nocc-ring: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.0.0, stripped
pitb-nocc-aws: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.0.0, stripped
aws-lc-sys 这次用了 29 秒,仍是 Building with: CC。两个二进制都是 Linux 程序,本机没法运行,这里只证明了「能构建」。真实的交叉构建环境(Linux 发行版的交叉 gcc 包、容器里的目标工具链)提供的是同样三样东西,环境变量名按 cc-rs 的规则换成对应目标。
换 provider 不是修法:ring 和 aws-lc-sys 都要 C 编译器,从一个换到另一个只改变编多少 C(aws-lc-sys 慢 10 秒左右,见设计取舍)。
怎么检查
- 谁要 C 编译器:
cargo tree -e build,normal -i cc。列出 ring 或 aws-lc-sys 就说明需要;cmakecrate 出现在里面不代表要装 cmake 程序。 - 构建脚本到底用了哪个编译器: 看
target/<目标>/release/build/<crate>-*/output,cc-rs 把它查过的每个环境变量和值都写在那里,cargo:warning=开头的行也只在这里能完整看到。 - 交叉构建的产物对不对: 对最终二进制跑
file。链接阶段出现大量ring_core_*或aws_lc_*的未定义符号,先查AR_<目标>有没有设。
小结
- 本机没有
cc:最先失败的是构建脚本的链接(rustls、libc 都有构建脚本),报linker `cc` not found,和选哪个 provider 无关。 - 交叉编译缺目标的 C 编译器:ring 和 aws-lc-sys 都停在 cc-rs 的
failed to find tool "<三元组>-gcc";aws-lc-sys 0.47.0 默认路径不调 cmake。 - 修法是给目标配齐
CC_<目标>、AR_<目标>和 cargo 的链接器;只配 CC 时 macOS 的ranlib会打出没有索引的静态库,链接时报一串未定义符号。 - 构建脚本成功时 cargo 隐藏依赖的
cargo:warning,排查要去看build/*/output。