必须用--whole-archive时:当将静态库打包进动态库且存在运行时才需调用(如dlopen)、但编译时未显式引用的符号,如插件注册、constructor函数或模块化服务类;否则链接器会丢弃未直接引用的符号。

什么时候必须用 --whole-archive?
当你把多个静态库(.a)打包进一个动态库(.so 或 .dll),且其中某些符号在构建时未被直接引用,但又需要在运行时由 dlopen/dlsym 动态加载——这时候不加 --whole-archive,那些“未被直接调用”的函数/变量会被链接器静默丢弃。
典型场景包括:
- 插件系统:主程序只 dlopen 插件
.so,不直接调用其内部函数,靠注册表或虚函数表发现能力 - 回调注册机制:静态库中定义了
__attribute__((constructor))函数或全局对象构造器,但主程序没显式引用该库的任何符号 - 模块化服务:多个
libxxx.a提供各自的服务类,统一被封装进libservice.so,但主程序只通过接口指针使用,不链接具体实现符号
--whole-archive 和 -Wl, 的写法陷阱
它不是 gcc 参数,是 ld(或 lld)原生命令;gcc 不认识,必须用 -Wl, 转义过去。错写成 --whole-archive 直接传给 gcc 会报错:unrecognized command-line option '--whole-archive'。
正确写法要点:
- 必须成对出现:
-Wl,--whole-archive后跟静态库名,再紧跟-Wl,--no-whole-archive - 静态库必须写在
--whole-archive和--no-whole-archive之间,顺序不能颠倒 - 不能写成
-la -lb这种简写形式——lld 在--whole-archive模式下不走库搜索逻辑,必须写全路径或确保-L+-la能准确定位到.a文件 - 如果混用动态库(
.so)和静态库,--whole-archive只影响其间的.a,对.so无作用
示例(正确):g++ -shared -o libplugin.so -Wl,--whole-archive libcore.a libutil.a -Wl,--no-whole-archive
用了 --whole-archive 却 still undefined symbol?
常见原因不是参数没生效,而是更底层的符号可见性或重复定义问题:
- 静态库内部有同名函数(比如两个
.a都定义了init_module()),lld 会报duplicate symbol错误,而非 undefined - 目标函数被编译时加了
-fvisibility=hidden,即使进了动态库,外部也看不到——得配合__attribute__((visibility("default")))显式导出 - 静态库本身是用
ar打包但没带索引(ar rs缺少s),lld 可能无法解析符号表;建议统一用llvm-ar -rcs - LLD 默认不处理传统 Unix 归档格式中的 “thin archive”(
.a内部存的是相对路径),遇到这类库会跳过——检查是否用了ar -T生成
LLD 下 --whole-archive 的内存与性能代价
启用后,LLD 会强制把整个 .a 中所有 .o 的符号表、重定位项、节内容全部 mmap 进内存,不做按需加载。这对大型静态库(尤其是含 LTO bitcode 或深度模板实例化的)压力极大。
实测影响:
- 一个 200MB 的
libbase.a(含 15k 个.o)开启后,LLD 内存峰值可能暴涨 3–4GB - 若同时启用了
--threads=8(默认),每个线程都会独立加载一份符号哈希表副本 - 相比 GNU ld,LLD 的符号解析更激进,
--whole-archive下更容易触发std::bad_alloc
建议搭配使用:-Wl,--threads=1 -Wl,--strip-debug -Wl,--no-incremental,否则容易在 CI 或低配机器上失败。
真正要警惕的不是“要不要用”,而是“有没有意识到它会让整个静态库变成不可剪裁的原子块”——一旦加上,你就放弃了链接时的 dead code elimination,后续优化空间基本锁死。

















