lld链接时undefined reference不一定是循环依赖;若liba.a与libb.a真正互相调用函数,必须用--start-group/--end-group反复扫描解决,否则单次从左到右扫描必失败。

LLD 链接时遇到 undefined reference,是不是循环依赖?
不是所有 undefined reference 都是循环依赖,但若两个静态库 libA.a 和 libB.a 互相调用对方的函数(比如 A.o 中调用了 B_foo(),B.o 中又调用了 A_bar()),而你用 lld 直接写成 -lA -lB 或 -lB -lA,大概率会失败——因为 LLD 默认按从左到右单次扫描,不会回溯已处理过的库。
LLD 不支持 --start-group/--end-group?
它支持,而且行为和 GNU ld 一致。LLD 的 --start-group / --end-group 是解决静态库循环依赖的**标准且唯一可靠手段**:
- 必须显式启用:
lld -flavor gnu ... --start-group -lA -lB --end-group - 不能简写为
-Wl,--start-group(LLD 不识别-Wl,前缀,除非你用clang++调用它并让 clang 转发) - 组内库会被反复扫描,直到所有未定义符号都被满足;但注意:这不解决符号重复定义问题(ODR violation),只解决“找不到”
- 若使用
clang++调用 LLD,推荐写法:clang++ main.cpp -fuse-ld=lld -Wl,--start-group -lA -lB -Wl,--end-group
为什么不用 --start-group 有时也能过?
那通常是因为依赖关系其实**不是真正循环**,只是你没看清调用链。比如:
-
libA.a里有A_impl.o依赖B_func(),但libB.a实际只提供B_func(),而它的实现又不依赖libA的任何符号(即头文件里声明了,但 .o 文件没引用 A) - 或者其中一个库被编译时加了
-fvisibility=hidden或函数被static修饰,导致符号根本没导出——这时nm -g --defined-only libB.a | grep B_func会发现它压根不在导出表里 - LLD 在某些版本中对单个 archive 内部的 object 排序更激进(比如按 symbol table 排),可能偶然“碰巧”解析成功,但这不可靠,别依赖
循环依赖背后真正该动刀的地方
用 --start-group 是链接层补救,不是设计解法。真实项目里反复靠它过关,说明代码结构有问题:
- 检查是否真需要双向调用:日志库调数学库算大小?不如把“格式化字节数”逻辑抽成独立
libutil.a,让两者都依赖它 - 确认头文件没相互
#include:循环包含常导致误判为循环依赖,实际编译阶段就该报错 - 用
llvm-ar -t libA.a看里面有哪些 .o,再用nm -C libA.a | grep ' U '查未定义符号,交叉比对两库的 U/D 符号,才能确认是否真循环
最易被忽略的一点:LLD 的 --start-group 不会帮你检测 ABI 不兼容。如果 libA.a 是用 -std=c++17 编的,libB.a 是 -std=c++20,即使链接成功,运行时也可能崩溃——符号名修饰不同,函数签名对不上。











