GCC 中的 whole-archive 参数解析

本文由 AI 加提示词大纲生成

提示词如下:

帮我写一篇博客,介绍 gcc 中的 whole-archive 这个参数,顺序如下:

  1. 背景:使用全局变量动态注册类到 RegisterFactory 中的时候,发现这个库打包为静态库时,可执行文件去链接这个静态库,会出现未注册成功的情况
  2. 表现:1、保存注册结果的 map 大小为 0;2、文件非常小,仅有十几KB,看起来并不包含整个静态库;3、访问空指针时会导致段错误;4、使用动态链接库时没有问题
  3. 原因:当全局参数被注册在静态库中时,如果这个静态库被链接,会导致静态库中的全局参数被优化掉了,也就没有注册成功
  4. 解决方案:使用 -Wl,--whole-archive lib -Wl,--no-whole-archive 使得链接器在链接时不要优化掉全局参数

在使用全局变量动态注册类到 RegisterFactory 时,常常会遇到这样一个问题:当库打包为静态库后,最终生成的可执行文件链接这个静态库,却无法正常注册。这篇文章将详细介绍这一问题的背景、表现、原因以及解决方案。

1. 背景

在很多项目中,我们会在全局作用域中注册类到工厂(RegisterFactory)中,以便在程序启动时就能动态获得所有类的信息。然而,当将这些注册逻辑放在静态库中时,链接到可执行文件时可能会出现注册失败的现象。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// static_lib.cpp
#include "register_factory.h"

static RegisterFactory factory;

void register_classes() {
factory.register_class("MyClass", []() { return new MyClass(); });
}

// main.cpp
#include "register_factory.h"

int main() {
register_classes(); // 调用注册函数
// 其他代码...
return 0;
}

2. 表现

可能会观察到下面几种现象:

  • 注册失败:保存注册结果的 map 大小为 0,没有任何注册信息。
  • 文件体积异常小:生成的可执行文件仅有十几KB,看起来好像并没有包含整个静态库的内容。
  • 段错误:访问空指针时会导致段错误,说明注册逻辑没有被正确执行。
  • 动态链接库正常:使用动态链接库时,注册逻辑能够正常工作,没有上述问题。

3. 原因

当全局变量(例如用来自动注册类的变量)被定义在静态库中时,链接器在构建可执行文件时,会对静态库进行优化。如果静态库中某些目标文件看起来没有被显式引用,链接器可能会选择将其优化掉,这就导致了全局注册逻辑没有被调用,从而注册失败。

4. 解决方案

为了解决这个问题,我们可以使用链接器参数 -Wl,--whole-archive-Wl,--no-whole-archive,该参数的作用是在链接时确保整个静态库的目标文件都被包含进最终的可执行文件,从而避免全局变量注册被优化掉。

例如,在编译链接时,可以这样指定参数:

1
gcc -o my_app main.o -Wl,--whole-archive libmystatic.a -Wl,--no-whole-archive

这条命令的含义是:

-Wl,--whole-archive:告诉链接器不要对之后的静态库进行优化,确保所有目标文件都被包含。

libmystatic.a:需要完整链接的静态库。

-Wl,--no-whole-archive:结束对静态库的完整链接模式,之后的库将恢复正常优化行为。

这样设置后,静态库中所有包含全局变量和注册信息的目标文件都会被链接,确保注册逻辑能够正常执行。

总结

全局变量在静态库中的自动注册机制可能因为链接器优化而失效,造成注册信息丢失。通过使用 -Wl,–whole-archive 可以确保静态库的所有内容都被链接进来,从而解决该问题。希望这篇文章能帮助你理解并解决类似的问题。


GCC 中的 whole-archive 参数解析
http://hebangwen.github.io/2025/06/17/whole-archive/
作者
何榜文
发布于
2025年6月17日
许可协议