25

假设我们有一个名为“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

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

4

5 回答 5

27

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

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

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

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

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

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

澄清问题后编辑:

ld信息手册中引用。

链接器只会在命令行中指定的位置搜索存档一次。如果存档定义了在命令行上出现在存档之前的某个对象中未定义的符号,则链接器将包含存档中的适当文件。但是,稍后在命令行上出现的对象中的未定义符号不会导致链接器再次搜索存档。

有关强制链接器多次搜索档案的方法,请参见 `-(' 选项。

您可以在命令行上多次列出同一个存档。

这种类型的归档搜索是 Unix 链接器的标准。但是,如果您在 AIX 上使用 `ld',请注意它与 AIX 链接器的行为不同。

这意味着:

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

于 2009-01-29T17:02:23.873 回答
24

这种问题最好通过一个简单的例子来解决。真的!花 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" 符号未定义。

于 2009-01-29T19:36:38.303 回答
2

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

g++ ... -la1 -la2 -la3 -la1 -la2 -la3 ...
于 2009-01-29T21:51:45.910 回答
0

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

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

于 2009-01-29T16:42:03.367 回答
0

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

于 2009-01-29T17:01:51.753 回答