Build process ld链接器问题:整个归档选项

Build process ld链接器问题:整个归档选项,build-process,linker,static-libraries,Build Process,Linker,Static Libraries,我所看到的--whole archive链接器选项的唯一真正用途是从静态库创建共享库。最近我遇到了Makefile,它在链接内部静态库时总是使用这个选项。这当然会导致可执行文件不必要地引入未引用的对象代码。我对此的反应是,这显然是错误的,我是不是遗漏了什么 第二个问题与我读到的关于整个归档选项的内容有关,但无法完全解析。与静态库链接时,如果可执行文件也与共享库链接,而共享库又(部分)具有与静态库相同的目标代码,则应使用--whole archive选项。即共享库和静态库在目标代码方面有重叠。使用

我所看到的
--whole archive
链接器选项的唯一真正用途是从静态库创建共享库。最近我遇到了Makefile,它在链接内部静态库时总是使用这个选项。这当然会导致可执行文件不必要地引入未引用的对象代码。我对此的反应是,这显然是错误的,我是不是遗漏了什么

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

谢谢

我同意使用整个归档来构建可执行文件可能不是您想要的(因为链接了不需要的代码并创建了臃肿的软件)。如果他们有一个很好的理由这样做,他们应该在构建系统中记录它,因为现在你只能猜测了


至于问题的第二部分。如果一个可执行文件链接了一个静态库和一个动态库,而动态库的(部分)目标代码与静态库相同,那么整个归档文件将确保在链接时首选静态库中的代码。这通常是您在进行静态链接时想要的。

在将可执行文件与静态库链接时,可以合法使用
--整个归档文件。一个例子是构建C++代码,其中全局实例在它们的构造函数中登记(警告:未经测试的代码):

h

typedef void(*处理程序)(常量字符*数据);
无效寄存器处理程序(常量字符*协议,处理程序h);
handler get_handler(const char*协议);
handlers.cc(libhandlers.a的一部分)

typedef映射HandlerMap;
handlerm;
无效寄存器处理程序(常量字符*协议,处理程序h){
m[协议]=h;
}
处理程序get_处理程序(const char*协议){
HandlerMap::迭代器it=m.find(协议);
if(it==m.end())返回nullptr;
返回->秒;
}
http.cc(libhttp.a的一部分)

#包括
类HttpHandler{
HttpHandler(){register_handler(“http”,&handle_http);}
静态无效句柄\u http(const char*){/*whatever*/}
};
httph;//注册到main!
main.cc

#包括
int main(int argc,char*argv[])
{
对于(int i=1;i
请注意,
http.cc
中没有
main.cc
需要的符号。如果您将此链接为

g++main.cc-lhttp-lhandlers
您将无法将http处理程序链接到主可执行文件中,也无法调用
handle\u http()
。将此与链接为时发生的情况进行对比:

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

在plain-C中也可以使用相同的“自注册”样式,例如使用
\uuu属性((构造函数))
GNU扩展。

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

老问题,但关于你的第一个问题(“为什么”),我看到了——整个档案库也用于内部图书馆,主要是为了避开这些图书馆之间的循环引用。它往往隐藏了图书馆糟糕的架构,所以我不推荐它。然而,这是一种快速试用的快速方法

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


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

在处理静态库和增量链接时,可以很好地使用
--whole archive

让我们假设:

  • libA
    实现
    a()
    b()
    函数
  • 程序的某些部分必须仅与
    libA
    链接,例如,由于使用
    --wrap
    进行某些函数包装(典型示例是
    malloc
  • libC
    实现
    c()
    函数并使用
    a()
  • 最后一个程序使用
    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
    
    未能插入--整个存档将剥离函数
    c()
    ,该函数无论如何都由
    程序使用,从而阻止正确的编译过程

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