其他
配置 GNU/LLVM 链接器、排查链接错误和启用 LTO…
当你需要选择 ld/gold/lld、排查链接期 undefined/重复/循环符号问题、启用 GCC/Clang LTO 或 ThinLTO、剥离死代码和控制符号可见性时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 48/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 55/100 ×0.15
适用场景
配置 GNU/LLVM 链接器、排查链接通用
解决思路
- 选择链接器:GNU ld (BFD) 是 Linux 默认,通用稳定;gold 用 `-fuse-ld=gold`,对 C++ 比 ld 快且支持 LTO 插件;lld (LLVM) 用 `-fuse-ld=lld`,最快、并行,Clang LTO 必需。
- 用 lld 配合 GCC 或 Clang:`gcc -fuse-ld=lld -o prog ...`,`clang -fuse-ld=lld -o prog ...`。
- 检查实际使用哪个链接器:`gcc -v -o prog main.c 2>&1 | grep 'Invoking'`。
- 通过编译器驱动传链接器参数:用 `-Wl,flag1,flag2`(逗号分隔、无空格),或用多个 `-Wl,flag1 -Wl,flag2`。
- 常用链接器 flags 示例:`gcc main.c -o prog -Wl,-rpath,/opt/mylibs/lib -Wl,--as-needed -Wl,--gc-sections -Wl,-z,relro -Wl,-z,now -L/opt/mylibs/lib -lfoo`。
- `-Wl,-rpath,/opt/mylibs/lib`:设置运行时库搜索路径。
- `-Wl,--as-needed`:只链接实际用到的库。
- `-Wl,--gc-sections`:移除未使用 section,需要编译时配合 `-ffunction-sections -fdata-sections`。
- `-Wl,-z,relro`:启动后将 relocation 标记为只读。
- `-Wl,-z,now`:启动时解析所有符号,实现 full RELRO。
- GNU ld 的链接顺序:从左到右处理 archives;库必须放在需要它的目标文件之后。
- 错误顺序示例:`gcc main.o -lfoo libdep.a -o prog` 可能失败,如果 `libdep.a` 需要 `libfoo`。
- 正确顺序示例:`gcc main.o -lfoo -ldep -o prog`,依赖项放在依赖者后面。
- archives 之间循环依赖:`gcc main.o -Wl,--start-group -lfoo -lbar -Wl,--end-group -o prog`;`--start-group`/`--end-group` 会重复搜索直到没有新符号被解析。
- GCC LTO 编译:`gcc -O2 -flto -ffunction-sections -fdata-sections -c foo.c -o foo.o`,并对 `bar.c` 执行同样命令。
- GCC LTO 链接时必须再次传 `-flto`:`gcc -O2 -flto -Wl,--gc-sections foo.o bar.o -o prog`。
- GCC LTO 的静态库必须用 `gcc-ar` / `gcc-ranlib`,不能用普通 `ar`:`gcc-ar rcs libfoo.a foo.o`,`gcc-ranlib libfoo.a`。
- GCC 并行 LTO:`gcc -O2 -flto=auto foo.o bar.o -o prog` 使用 jobserver;`gcc -O2 -flto=4 foo.o bar.o -o prog` 使用 4 个并行任务。
- Clang/lld Full LTO:`clang -O2 -flto -fuse-ld=lld foo.c bar.c -o prog`。
- Clang/lld ThinLTO:`clang -O2 -flto=thin -fuse-ld=lld foo.c bar.c -o prog`,速度更快且质量接近 Full LTO。
- CMake 全局启用 LTO:`set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)`,会启用 `-flto`。
- ThinLTO 会缓存工作:复用未改动模块可加速后续构建;缓存位置用 `-Wl,--thinlto-cache-dir=/tmp/thinlto-cache` 指定。
- 死代码剥离编译:`gcc -O2 -ffunction-sections -fdata-sections -c foo.c -o foo.o`。
- 死代码剥离链接:`gcc -Wl,--gc-sections foo.o -o prog`。
- 验证被移除的 section:`gcc -Wl,--gc-sections -Wl,--print-gc-sections foo.o -o prog 2>&1 | head -20`。
- macOS 上链接器默认剥离死代码(`-dead_strip`)。
- 符号可见性:默认隐藏所有符号并显式导出:`gcc -fvisibility=hidden -O2 -shared -fPIC foo.c -o libfoo.so`。
- 在源码中标记导出:`__attribute__((visibility("default"))) int my_public_function(void);`。
- 使用 version script 控制导出:`foo.ver` 内容为 `{ global: my_public_function; my_other_public; local: *; };`,然后用 `gcc -Wl,--version-script=foo.ver -shared -fPIC -o libfoo.so foo.o`。
- `undefined reference to 'foo'`:缺少库或顺序错误;添加 `-lfoo`,并把它移到需要它的 object 后面。
- `multiple definition of 'foo'`:同一个符号在两个 TU 中定义;删除重复定义,使用 `static` 或 `extern`。
- `cannot find -lfoo`:库不在搜索路径;添加 `-L/path/to/lib`,或安装 dev package。
- `relocation truncated`:relocation 地址溢出;使用 `-mcmodel=large`,或重构镜像。
- `version 'GLIBC_2.33' not found`:二进制需要更新版本的 glibc;静态链接,或在旧主机上重建。
- `circular reference`:archives 相互依赖;使用 `--start-group`/`--end-group`。
- 生成 linker map 文件:`gcc -Wl,-Map=prog.map -o prog foo.o bar.o`,用 `less prog.map` 查看,调试二进制大小和符号放置。
- 需要完整链接器和 LTO flags 参考时,查看 `references/flags.md`。
适用边界 · 注意事项
- 不适用于 MSVC/Windows link.exe 的 `/LTCG` 等具体配置;本技能聚焦 GNU ld/gold/lld 与 GCC/Clang。
- 当链接时间非常敏感或模块很少时,不要盲目启用 Full LTO;可优先用 ThinLTO 或关闭 LTO。
- 目标工具链不支持 LTO 插件、没有对应 `gcc-ar`/`gcc-ranlib`/lld 时,不要照搬 GCC LTO 静态库流程。