Answers:
(请参阅此答案的历史记录以获得更详尽的文字,但我现在认为读者更容易看到真实的命令行)。
以下所有命令共享的公用文件
$ cat a.cpp
extern int a;
int main() {
return a;
}
$ cat b.cpp
extern int b;
int a = b;
$ cat d.cpp
int b;
$ g++ -c b.cpp -o b.o
$ ar cr libb.a b.o
$ g++ -c d.cpp -o d.o
$ ar cr libd.a d.o
$ g++ -L. -ld -lb a.cpp # wrong order
$ g++ -L. -lb -ld a.cpp # wrong order
$ g++ a.cpp -L. -ld -lb # wrong order
$ g++ a.cpp -L. -lb -ld # right order
链接器从左到右搜索,并记录未解析的符号。如果库解析符号,它将使用该库的目标文件来解析符号(在这种情况下,它们来自libb.a)。
静态库彼此之间的依赖关系是相同的-必须首先使用需要符号的库,然后是解析符号的库。
如果静态库依赖于另一个库,但是另一个库又依赖于前一个库,则存在一个循环。您可以通过用-(
和包围循环依赖的库来解决此问题-)
,例如-( -la -lb -)
(您可能需要转义括号,例如-\(
和-\)
)。链接器然后多次搜索那些包含的lib,以确保解决循环依赖性。另外,您可以多次指定库,因此每个库都位于另一个库之前-la -lb -la
。
$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -L. -ld -o libb.so # specifies its dependency!
$ g++ -L. -lb a.cpp # wrong order (works on some distributions)
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong order
$ g++ -Wl,--as-needed a.cpp -L. -lb # right order
这里是相同的-库必须遵循程序的目标文件。与静态库相比,这里的区别在于您不必在意彼此之间的依赖关系,因为动态库本身会解决它们的依赖关系。
某些最新发行版显然默认使用--as-needed
链接器标志,该标志强制程序的目标文件位于动态库之前。如果传递了该标志,则链接器将不会链接到可执行文件实际上并不需要的库(并且从左到右检测到此链接)。我最近的archlinux发行版默认情况下不使用此标志,因此它没有给出未遵循正确顺序的错误。
这是不正确的省略的依赖b.so
对d.so
创建前时。a
然后,在链接时将需要指定库,但a
实际上并不需要整数b
本身,因此不应考虑整数本身b
的依赖关系。
如果您错过为以下项指定依赖项,则这是一个示例。 libb.so
$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -o libb.so # wrong (but links)
$ g++ -L. -lb a.cpp # wrong, as above
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong, as above
$ g++ a.cpp -L. -lb # wrong, missing libd.so
$ g++ a.cpp -L. -ld -lb # wrong order (works on some distributions)
$ g++ -Wl,--as-needed a.cpp -L. -ld -lb # wrong order (like static libs)
$ g++ -Wl,--as-needed a.cpp -L. -lb -ld # "right"
如果现在查看二进制文件具有哪些依赖关系,您会注意到二进制文件本身也依赖于libd
,而不只是依赖于libb
它。如果libb
以后使用另一个库,则二进制文件将需要重新链接。如果别人负荷libb
使用dlopen
在运行时(认为加载插件动态的),呼叫也将失败。所以"right"
真的也应该如此wrong
。
lorder
+ tsort
所做的。但是,如果您有循环引用,则有时没有顺序。然后,您只需要循环浏览库列表,直到解决所有问题。
GNU ld链接器是所谓的智能链接器。它将跟踪先前的静态库使用的功能,并永久性地将其查找表中未使用的那些功能扔掉。结果是,如果您过早地链接静态库,则该静态库中的函数以后在链接行上将不再对静态库可用。
典型的UNIX链接器从左到右起作用,因此将所有依赖库放在左侧,而将满足这些依赖关系的库放在链接行的右侧。您可能会发现某些库依赖于其他库,而同时其他库也依赖于它们。这是复杂的地方。当涉及循环引用时,请修复您的代码!
这是一个示例,用于明确说明涉及静态库时GCC的工作方式。因此,假设我们有以下情形:
myprog.o
-包含main()
功能,取决于libmysqlclient
libmysqlclient
-静态的,为了举例说明(当然,您更喜欢共享库,因为libmysqlclient
它很大);在/usr/local/lib
; 并依赖于来自libz
libz
(动态)我们该如何链接?(注意:使用gcc 4.3.4在Cygwin上编译的示例)
gcc -L/usr/local/lib -lmysqlclient myprog.o
# undefined reference to `_mysql_init'
# myprog depends on libmysqlclient
# so myprog has to come earlier on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# we have to link with libz, too
gcc myprog.o -lz -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# libz is needed by libmysqlclient
# so it has to appear *after* it on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient -lz
# this works
您可以使用-Xlinker选项。
g++ -o foobar -Xlinker -start-group -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a -Xlinker -end-group
ALMOST等于
g++ -o foobar -Xlinker -start-group -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a -Xlinker -end-group
小心点!
gcc
最近(相对)已更改为更严格的行为。