当静态库存在循环依赖且无法调整链接顺序时,必须使用--start-group和--end-group,因其强制链接器循环扫描组内所有.a文件直至符号全部解析完成。

什么时候必须用 --start-group 和 --end-group
当静态库之间存在**循环依赖**,且你无法或不愿手动调整链接顺序时,--start-group 就不是可选项,而是必需项。
典型现象是:链接时报大量 undefined reference to 'xxx',但确认函数确实定义在某个 .a 文件里,且 nm -C libX.a | grep xxx 能查到符号;同时你发现 libA.a 用到了 libB.a 的符号,而 libB.a 又反过来依赖 libA.a 中的初始化函数或注册表机制(比如 C++ 全局对象构造、插件注册宏)。
- 常见于含 Register/Factory 模式的 C++ 库(如 ONNX Runtime、Caffe、某些插件框架)
- MKL、OpenBLAS 等数学库与自定义 wrapper 库混链时也高频出现
- CMake 中直接写
target_link_libraries(myapp libA libB)会失败,因为 ld 默认只扫一遍归档文件
--start-group 怎么写才有效
它不是独立开关,必须成对出现,且所有待循环解析的静态库必须严格夹在中间 —— 这一点极易漏掉或错位。
- 正确写法:
g++ main.o -Wl,--start-group libA.a libB.a libC.a -Wl,--end-group -o app - 错误写法:
-Wl,--start-group libA.a -Wl,--end-group libB.a(libB.a不在 group 内,不参与反复扫描) - 错误写法:
-Wl,--start-group libA.a libB.a -Wl,--end-group -lstdc++(-lstdc++是动态库,无需也不应放进 group) - 在 CMake 中要用完整字符串:
target_link_libraries(myapp "-Wl,--start-group" libA.a libB.a "-Wl,--end-group"),不能拆开或加空格
用了 --start-group 但还是报 undefined,接下来查什么
说明问题不在顺序,而在符号本身未暴露或被优化掉了 —— 这时 --start-group 已经“尽力”了,但没用。
- 检查是否用了
-ffunction-sections -fdata-sections -Wl,--gc-sections:这些选项会让链接器丢弃未引用的函数/变量,导致注册函数被删,即使进了 group 也找不到 - 检查 C++ 符号是否因 ABI 不一致被 mangling 错:比如
-D_GLIBCXX_USE_CXX11_ABI=0和=1混用,nm -C libX.a看到的符号名和调用处不匹配 - 检查是否误把动态库路径当静态库传进 group:
libfoo.so放进--start-group无效,ld 只对归档文件(.a)做多次扫描 - 用
readelf -d app | grep NEEDED确认最终二进制是否真包含对应库,避免链接阶段静默跳过
--start-group 的代价和替代思路
它让链接器反复遍历 group 内每个 .a,直到无新未定义符号产生,性能开销明显 —— 大型项目中可能多花数秒甚至十几秒。
- 优先尝试重构依赖:把循环依赖拆成三层 —— core(无依赖)、plugin(依赖 core)、loader(依赖 plugin + core)
- 用
--whole-archive强制拉入特定库的所有符号(如注册表所在的库),再配普通顺序链接其余库:-Wl,--whole-archive libreg.a -Wl,--no-whole-archive libA.a libB.a - 生成中间 .o 合并静态库:
ar x libA.a && ar x libB.a && gcc -r *.o -o merged.o,再链接merged.o,彻底绕过归档文件解析逻辑
真正难搞的从来不是怎么写 --start-group,而是怎么证明它确实该用,以及用完之后为什么还不行 —— 这时候得回到符号表和 ABI 层去盯细节。











