【问题标题】:ld linker question: the --whole-archive optionld 链接器问题: --whole-archive 选项
【发布时间】:2010-10-22 18:38:58
【问题描述】:

我见过的--whole-archive 链接器选项的唯一真正用途是从静态库创建共享库。最近我遇到了 Makefile(s),它在与内部静态库链接时总是使用这个选项。这当然会导致可执行文件不必要地拉入未引用的目标代码。我对此的反应是,这是完全错误的,我在这里遗漏了什么吗?

第二个问题与我读到的有关整体存档选项但无法完全解析的内容有关。如果可执行文件还与共享库链接,则在与静态库链接时应使用--whole-archive 选项,而共享库又(部分)具有与静态库相同的目标代码。那就是共享库和静态库在目标代码方面有重叠。使用此选项将强制在可执行文件中解析所有符号(无论是否使用)。这应该避免目标代码重复。这很令人困惑,如果在程序中引用了一个符号,它必须在链接时唯一地解析,这个关于复制的业务是什么? (如果这一段不是很清楚的缩影,请原谅我)

谢谢

【问题讨论】:

    标签: build-process linker static-libraries


    【解决方案1】:

    在将可执行文件与静态库链接时,--whole-archive 的使用是合法的。一个示例是构建 C++ 代码,其中全局实例在其构造函数中“注册”自己(警告:未经测试的代码):

    handlers.h

    typedef void (*handler)(const char *data);
    void register_handler(const char *protocol, handler h);
    handler get_handler(const char *protocol);
    

    handlers.cc(libhandlers.a 的一部分)

    typedef map<const char*, handler> HandlerMap;
    HandlerMap m;
    void register_handler(const char *protocol, handler h) {
       m[protocol] = h;
    }
    handler get_handler(const char *protocol) {
       HandlerMap::iterator it = m.find(protocol);
       if (it == m.end()) return nullptr;
       return it->second;
    }
    

    http.cc(libhttp.a 的一部分)

    #include <handlers.h>
    class HttpHandler {
        HttpHandler() { register_handler("http", &handle_http); }
        static void handle_http(const char *) { /* whatever */ }
    };
    HttpHandler h; // registers itself with main!
    

    main.cc

    #include <handlers.h>
    int main(int argc, char *argv[])
    {
        for (int i = 1; i < argc-1; i+= 2) {
            handler h = get_handler(argv[i]);
            if (h != nullptr) h(argv[i+1]);
        }
    }
    

    请注意,http.cc 中没有 main.cc 需要的符号。如果您将其链接为

    g++ main.cc -lhttp -lhandlers
    

    您将不会获得链接到主可执行文件的 http 处理程序,并且将无法调用 handle_http()。将此与链接为时发生的情况进行对比:

    g++ main.cc -Wl,--whole-archive -lhttp -Wl,--no-whole-archive -lhandlers
    

    在纯 C 中也可以使用相同的“自我注册”样式,例如带有__attribute__((constructor)) GNU 扩展。

    【讨论】:

    • Russion 如果可以构建 libhttp.a 则证明该 libhttp.a 中存在 register_handler 函数。那么这个函数如何引用main.cc中的register_handler呢?所以这种情况我们必须用另一种方式来实现你的想法。
    • @longbkit 我已经更新了答案,以便将处理程序分解到较低级别的库中,这是需要的。我拒绝将handler 类型从 C 函数指针更改为 C++ std::function"
    • 有没有办法将整个存档功能定位到特定的全局注册符号,而不是整个库?
    • @David 如果您只想提取一组特定的符号,而不是整个存档,--whole-archive(显然)不合适。请改用-Wl,-u,needed_symbol
    【解决方案2】:

    --whole-archive 的另一个合法用途是供工具包开发人员在单个静态库中分发包含多个功能的库。在这种情况下,提供者不知道消费者将使用库的哪些部分,因此必须包含所有内容。

    【讨论】:

    • 静态库包含所有内容而不使用 --whole-archive,这似乎是一件毫无意义的事情
    • 绝对不正确。当你创建一个库时,无论是静态的还是动态的,它都会包含所有命名的目标文件。
    【解决方案3】:

    另一个很好的使用--whole-archive的场景是处理静态库增量链接。

    让我们假设:

    1. libA 实现了 a()b() 函数。
    2. 程序的某些部分只能与libA 链接,例如由于使用--wrap 进行了一些函数包装(一个经典的例子是malloc
    3. libC 实现 c() 函数并使用 a()
    4. 最终程序使用a()c()

    增量链接步骤可能是:

    ld -r -o step1.o module1.o --wrap malloc --whole-archive -lA
    ld -r -o step2.o step1.o module2.o --whole-archive -lC
    cc step3.o module3.o -o program
    

    未能插入 --whole-archive 将剥离函数 c() 无论如何都被 program 使用,从而阻止正确的编译过程。

    当然,这是一个特殊的极端情况,必须进行增量链接以避免在所有模块中包装对malloc 的所有调用,但--whole-archive 成功支持这种情况。

    【讨论】:

      【解决方案4】:

      我同意使用 —whole-archive 来构建可执行文件可能不是您想要的(由于链接了不需要的代码并创建了臃肿的软件)。如果他们有充分的理由这样做,他们应该将其记录在构建系统中,因为现在您只能猜测了。

      关于你问题的第二部分。如果一个可执行文件同时链接了一个静态库和一个动态库,该动态库具有(部分)与静态库相同的目标代码,那么 —whole-archive 将确保在链接时来自静态库的代码是首选。这通常是您在进行静态链接时想要的。

      【讨论】:

      • 使用—whole-archive的一个理由:这通常用于将存档文件(.o/.a)转换为共享库,强制将每个对象包含在生成的共享库中,在为了在首先编译静态库时组合多个动态库。参考:Using LD, the GNU linker - Options
      【解决方案5】:

      旧查询,但在您的第一个问题(“为什么”)上,我看到 --whole-archive 也用于内部库,主要是为了回避这些库之间的循环引用。它往往会隐藏图书馆的不良架构,所以我不推荐它。然而,这是一种快速试用的快速方法。

      对于您的第二个查询,如果共享对象和静态库中存在相同的符号,则链接器将满足它首先遇到的任何库的引用。
      如果共享库和静态库具有精确的代码共享,那么这一切都可以正常工作。但是在共享库和静态库对相同符号有不同实现的情况下,您的程序仍然可以编译,但会根据库的顺序表现出不同的行为。

      强制从静态库中加载所有符号是消除关于从何处加载的混淆的一种方法。但总的来说,这听起来像是解决了错误的问题;您通常不希望在不同的库中使用相同的符号。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-08-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-21
        • 2019-02-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多