【问题标题】:g++: In what order should static and dynamic libraries be linked?g++:静态库和动态库应该以什么顺序链接?
【发布时间】:2010-10-04 06:58:35
【问题描述】:

假设我们有一个名为“my_app”的主可执行文件,它使用了其他几个库:3 个库是静态链接的,另外 3 个是动态链接的。 它们应该按什么顺序与“my_app”相关联?

但是这些应该按什么顺序链接呢?

假设我们得到了依赖于 libSB 的 libSA(如在静态 A 中)和依赖于 libSB 的 libSC:

libSA -> libSB -> libSC

还有三个动态库:libDA -> libDB -> libDClibDA是基础,libDC是最高)

这些应该按什么顺序链接?基本的第一个还是最后一个?

g++ ... -g libSA libSB libSC -lDA -lDB -lDC -o my_app

似乎是正确的顺序,但是是这样吗?如果任何动态库与静态库或其他方式之间存在依赖关系怎么办?

【问题讨论】:

    标签: c++ linker shared-libraries static-libraries dynamic-linking


    【解决方案1】:

    在静态情况下,这并不重要,因为您实际上并没有链接静态库 - 您所做的只是将一些目标文件打包到一个存档中。您只需编译目标文件,即可立即创建静态库。

    动态库的情况比较复杂,有两个方面:

    1. 共享库的工作方式与静态库完全相同(共享段除外,如果它们存在的话),这意味着,您可以做同样的事情 - 只要您拥有目标文件。这意味着例如来自 libDA 的符号将在 libDB 中显示为未定义

    2. 在链接共享对象时,您可以在命令行上指定要链接的库。这与 1. 的效果相同,但将 libDB 标记为需要 libDA。

    不同之处在于,如果使用前一种方式,则在链接可执行文件时,必须在命令行中指定所有三个库(-lDA、-lDB、-lDC)。如果您使用后者,您只需指定 -lDC ,它会在链接时自动拉取其他人。请注意,链接时间就在您的程序运行之前(这意味着您可以获得不同版本的符号,甚至来自不同的库)。

    这一切都适用于 UNIX; Windows DLL 的工作方式完全不同。

    澄清问题后编辑:

    引用自 ld 信息手册。

    链接器将仅搜索存档 一次,在它所在的位置 在命令行中指定。如果 存档定义了一个符号,它是 在某些对象中未定义 出现在存档之前 命令行,链接器将包括 适当的文件(S)从 档案。但是,未定义的符号 在稍后出现的对象中 命令行不会导致链接器 再次搜索存档。

    查看 `-(' 选项以了解强制方法 用于搜索多个档案的链接器 次。

    您可以列出多个相同的存档 命令行的次数。

    这种类型的档案搜索是 Unix 链接器的标准。然而,如果 您在 AIX 上使用“ld”,请注意 它与行为不同 AIX 链接器。

    这意味着:

    在命令行中,任何依赖于其他库的静态库或对象都应该放在它之前。如果静态库相互依赖循环,你可以例如。使用-( 命令行选项,或将库放在命令行上两次 (-lDA -lDB -lDA)。动态库的顺序无关紧要。

    【讨论】:

    • +1 :我经常看到这个问题。由于静态库现在没有被广泛使用,原因/解决方案并不是众所周知的......另一个解决方案是使用链接器整体存档选项。它包括所有存档对象。它用于从静态库创建动态库,但适用于档案链接顺序问题...
    • 对不起,第一部分完全是错误的!静态库被链接,至少在符号解析是在它们捆绑在一起时完成的意义上。因此,顺序至关重要!
    • @Noldorin 我同意你的看法。我找到了另一个关于这个主题的帖子。 Linker order - GCC
    • @kevin:是的,我也看过那篇文章......这是一个很好的信息概述。确保你也阅读了 cmets 的细节。 :)
    【解决方案2】:

    这类问题最好通过一个简单的例子来解决。真的!花 2 分钟,编写一个简单的示例,然后尝试一下!你会学到一些东西,而且比问更快。

    例如,给定文件:

    a1.cc

    #include <stdio.h>
    void a1() { printf("a1\n"); }
    

    a2.cc

    #include <stdio.h>
    extern void a1();
    void a2() { printf("a2\n");  a1(); }
    

    a3.cc

    #include <stdio.h>
    extern void a2();
    void a3() { printf("a3\n"); a2(); }
    

    aa.cc

    extern void a3();
    int main()
    {
      a3();
    }
    

    跑步:

    g++ -Wall -g -c a1.cc
    g++ -Wall -g -c a2.cc
    g++ -Wall -g -c a3.cc
    ar -r liba1.a a1.o
    ar -r liba2.a a2.o
    ar -r liba3.a a3.o
    g++ -Wall -g aa.cc -o aa -la1 -la2 -la3 -L.
    

    演出:

    ./liba3.a(a3.o)(.text+0x14): In function `a3()':
    /tmp/z/a3.C:4: undefined reference to `a2()'
    

    鉴于:

    g++ -Wall -g -c a1.C
    g++ -Wall -g -c a2.C
    g++ -Wall -g -c a3.C
    ar -r liba1.a a1.o
    ar -r liba2.a a2.o
    ar -r liba3.a a3.o
    g++ -Wall -g aa.C -o aa -la3 -la2 -la1 -L.
    

    成功。 (只是 -la3 -la2 -la1 参数顺序改变了。)

    PS:

    nm --demangle liba*.a
    
    liba1.a:
    a1.o:
                     U __gxx_personality_v0
                     U printf
    0000000000000000 T a1()
    
    liba2.a:
    a2.o:
                     U __gxx_personality_v0
                     U printf
                     U a1()
    0000000000000000 T a2()
    
    liba3.a:
    a3.o:
                     U __gxx_personality_v0
                     U printf
                     U a2()
    0000000000000000 T a3()
    

    来自 man nm

    • 如果是小写,符号是本地的;如果是大写,则符号是全局(外部)。

    • “T”符号在文本(代码)部分。

    • “U”符号未定义。

    【讨论】:

    • 写一个例子比找 7 年后找人要慢得多。
    【解决方案3】:

    我在一个项目中使用了一堆内部库,不幸的是这些库相互依赖(随着时间的推移,情况变得更糟)。我们最终通过设置 SCons 以在链接时指定所有库两次来“解决”这个问题:

    g++ ... -la1 -la2 -la3 -la1 -la2 -la3 ...
    

    【讨论】:

    • -Wl,--start-group 和 -Wl,--end-group 也可能派上用场。
    【解决方案4】:

    链接库或可执行文件的依赖项必须在链接时存在,因此您不能在 libXB 存在之前链接 libXC。不管是静态的还是动态的。

    从最基本的开始,它没有(或只是在您的项目之外)依赖项。

    【讨论】:

      【解决方案5】:

      保持库相互独立以避免链接顺序问题是一种很好的做法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-16
        • 1970-01-01
        • 2017-05-24
        • 2010-09-15
        • 1970-01-01
        • 1970-01-01
        • 2011-09-05
        相关资源
        最近更新 更多