假设我们有一个名为"my_app"的主要可执行文件,它使用了其他几个库:3个库是静态链接的,其他3个是动态链接的.它们应该以哪种顺序与"my_app"相关联?
但这些顺序应该以哪种顺序联系起来?
假设我们得到了依赖于libSB的libSA(如在静态A中),以及依赖于libSB的libSC:
libSA -> libSB -> libSC
和三个动态链接库:libDA -> libDB -> libDC
(libDA
是基本的,libDC
是最高的)
这些顺序应该链接在哪?第一个还是最后一个?
g++ ... -g libSA libSB libSC -lDA -lDB -lDC -o my_app
看起来像现在的顺序,但是这样吗?如果任何动态库与静态库或其他方式之间存在依赖关系,该怎么办?
在静态情况下,它并不重要,因为您实际上并没有链接静态库 - 您只需将一些目标文件打包在一个存档中.您只需编译目标文件,就可以立即创建静态库.
动态库的情况比较复杂,有两个方面:
共享库的工作方式与静态库的工作方式完全相同(共享段除外),这意味着您可以执行相同的操作 - 只需在拥有目标文件后立即链接共享库.这意味着例如来自libDA的符号在libDB中将显示为未定义
链接共享对象时,可以在命令行上指定要链接到的库.这与1.具有相同的效果,但是,将libDB标记为需要libDA.
不同之处在于,如果使用前一种方式,则必须在链接可执行文件时在命令行上指定所有三个库(-lDA,-lDB,-lDC).如果您使用后者,则只需指定-lDC,它将在链接时自动提取其他内容.请注意,链接时间就在程序运行之前(这意味着您可以获得不同版本的符号,甚至来自不同的库).
这一切都适用于UNIX; Windows DLL的工作方式完全不同.
从ld
信息手册中引用.
链接器将仅在命令行上指定的位置搜索一次存档.如果存档定义了在命令行上存档之前出现的某个对象中未定义的符号,则链接器将包含存档中的相应文件.但是,稍后在命令行中出现的对象中的未定义符号将不会导致链接器再次搜索存档.
有关强制链接器多次搜索存档的方法,请参阅` - ('选项.
您可以在命令行上多次列出相同的存档.
这种类型的归档搜索是Unix链接器的标准.但是,如果您在AIX上使用`ld',请注意它与AIX链接器的行为不同.
这意味着:
依赖于其他库的任何静态库或对象都应放在命令行之前.如果静态库循环相互依赖,您可以例如.使用-(
命令行选项,或将库放在命令行上两次(-lDA -lDB -lDA
).动态库的顺序无关紧要.
这是一个通过一个简单的例子最好地解决的问题.真!花2分钟,编写一个简单的例子,然后尝试一下!你会学到一些东西,而且比求问要快.
例如,给定文件:
a1.cc
#includevoid a1() { printf("a1\n"); }
a2.cc
#includeextern void a1(); void a2() { printf("a2\n"); a1(); }
a3.cc
#includeextern 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"符号未定义.