假设我们有一个名为“my_app”的主要可执行文件,它使用了其他几个库:3个库是静态链接的,其他3个是动态链接的。 它们应该以哪种顺序与“my_app”链接?
但是这些应该以哪种顺序联系起来?
假设我们有libSA(如在静态A中)依赖于libSB,而libSC依赖于libSB:
libSA -> libSB -> libSC
和三个动态库:libDA -> libDB -> libDC
(libDA
是基本的,libDC
是最高的)
这些链接的顺序是什么?最基本的还是最后一个?
g++ ... -g libSA libSB libSC -lDA -lDB -lDC -o my_app
似乎是当前的顺序,但是这样吗?如果任何动态库与静态库或其他方式之间存在依赖关系,该怎么办?
答案 0 :(得分:26)
在静态情况下,它并不重要,因为您实际上并没有链接静态库 - 您只需将一些目标文件打包在一个存档中。您只需编译目标文件,就可以立即创建静态库。
动态库的情况更复杂,有两个方面:
共享库的工作方式与静态库完全相同(共享段除外,如果它们存在),这意味着,您也可以这样做 - 只需链接您的共享库即可目标文件。这意味着例如来自libDA的符号在libDB中将显示为未定义
链接共享对象时,可以在命令行上指定要链接的库。这与1具有相同的效果,但是,将libDB标记为需要libDA。
不同之处在于,如果使用前一种方式,则必须在链接可执行文件时在命令行上指定所有三个库(-lDA,-lDB,-lDC)。如果您使用后者,则只需指定-lDC,它将在链接时自动提取其他内容。请注意,链接时间就在程序运行之前(这意味着您可以获得不同版本的符号,即使是来自不同的库)。
这一切都适用于UNIX; Windows DLL的工作方式完全不同。
来自ld
信息手册。
链接器仅搜索存档 一次,在它所在的位置 在命令行上指定。如果 archive定义了一个符号 在某些对象中未定义 出现在档案馆之前 命令行,链接器将包含 来自的相应文件 存档。但是,一个未定义的符号 在后来出现的对象中 命令行不会导致链接器 再次搜索档案。
请参阅` - ('选项以获取强制方法 链接器搜索多个存档 次。
您可以列出相同的存档倍数 命令行上的次数。
这种类型的档案搜索是 Unix链接器的标准。但是,如果 你在AIX上使用`ld',请注意 它与行为不同 AIX链接器。
这意味着:
依赖于其他库的任何静态库或对象都应放在命令行之前。如果静态库循环相互依赖,您可以例如。使用-(
命令行选项,或将库放在命令行上两次(-lDA -lDB -lDA
)。动态库的顺序无关紧要。
答案 1 :(得分: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”符号未定义。
答案 2 :(得分:2)
我参与了一个项目,其中包含许多内部库,不幸的是彼此依赖(随着时间的推移它变得更糟)。我们最终通过设置SCons来解决这个问题,以便在链接时指定所有的lib两次:
g++ ... -la1 -la2 -la3 -la1 -la2 -la3 ...
答案 3 :(得分:0)
链接库或可执行文件的依赖项必须在链接时出现,因此在libXB出现之前无法链接libXC。静态或动态无关紧要。
从最基本的一个开始,它没有(或只是你的项目之外)依赖。
答案 4 :(得分:0)
保持库彼此独立以避免链接顺序问题是一种很好的做法。