为什么库的链接顺序有时会导致GCC中的错误?

为什么库的链接顺序有时会导致GCC中的错误?,gcc,linker,Gcc,Linker,为什么库的链接顺序有时会导致GCC中的错误?我已经看到了很多,我们的一些模块链接了超过100个库的代码加上系统和第三方库 根据不同的链接器HP/Intel/GCC/SUN/SGI/IBM/etc,您可以获得未解析的函数/变量等,在某些平台上,您必须列出两次库 在大多数情况下,我们使用库、核心、平台和不同抽象层的结构化层次结构,但对于某些系统,您仍然需要处理link命令中的顺序 一旦你找到了一个解决方案文档,那么下一个开发人员就不必再把它写出来了 我的老讲师曾经说过,“高内聚低耦合”,今天仍然是这

为什么库的链接顺序有时会导致GCC中的错误?

我已经看到了很多,我们的一些模块链接了超过100个库的代码加上系统和第三方库

根据不同的链接器HP/Intel/GCC/SUN/SGI/IBM/etc,您可以获得未解析的函数/变量等,在某些平台上,您必须列出两次库

在大多数情况下,我们使用库、核心、平台和不同抽象层的结构化层次结构,但对于某些系统,您仍然需要处理link命令中的顺序

一旦你找到了一个解决方案文档,那么下一个开发人员就不必再把它写出来了


我的老讲师曾经说过,“高内聚低耦合”,今天仍然是这样。

我经常看到这一点,我们的一些模块链接了超过100个代码库以及系统和第三方库

根据不同的链接器HP/Intel/GCC/SUN/SGI/IBM/etc,您可以获得未解析的函数/变量等,在某些平台上,您必须列出两次库

在大多数情况下,我们使用库、核心、平台和不同抽象层的结构化层次结构,但对于某些系统,您仍然需要处理link命令中的顺序

一旦你找到了一个解决方案文档,那么下一个开发人员就不必再把它写出来了


我的老讲师曾经说过,“高内聚低耦合”,这一说法今天仍然成立。

GNU ld链接器是一种所谓的智能链接器。它将跟踪前面的静态库所使用的函数,从其查找表中永久丢弃那些未使用的函数。结果是,如果过早链接静态库,则该库中的函数将不再可用于链接行中的静态库


典型的UNIX链接器从左到右工作,因此将所有依赖库放在左侧,满足这些依赖关系的库放在链接行的右侧。您可能会发现一些库依赖于其他库,而同时其他库依赖于它们。这就是问题变得复杂的地方。当涉及到循环引用时,请修复您的代码

GNU ld链接器是所谓的智能链接器。它将跟踪前面的静态库所使用的函数,从其查找表中永久丢弃那些未使用的函数。结果是,如果过早链接静态库,则该库中的函数将不再可用于链接行中的静态库

典型的UNIX链接器从左到右工作,因此将所有依赖库放在左侧,满足这些依赖关系的库放在链接行的右侧。您可能会发现一些库依赖于其他库,而同时其他库依赖于它们。这就是问题变得复杂的地方。当涉及到循环引用时,请修复您的代码

(请参阅此答案的历史记录以获得更详细的文本,但我现在认为读者更容易看到真正的命令行)


以下所有命令共享的公共文件

$ cat a.cpp
extern int a;
int main() {
  return a;
}

$ cat b.cpp
extern int b;
int a = b;

$ cat d.cpp
int b;
链接到静态库 链接器从左到右搜索,并在搜索过程中记录未解析的符号。如果库解析符号,它将使用该库的对象文件来解析符号(在本例中是libb.a中的b.o)

静态库之间的依赖关系是一样的——首先需要符号的库,然后是解析符号的库

如果一个静态库依赖于另一个库,而另一个库又依赖于前一个库,则存在一个循环。您可以通过使用
-(
-)
封装循环依赖库来解决此问题,例如
-(-la-lb-)
(您可能需要转义参数,例如
-\(
-\)
)。链接器然后多次搜索这些封闭的库,以确保循环依赖关系得到解决。或者,您可以多次指定库,使每个库都位于另一个库之前:
-la-lb-la

链接到动态库 这里也是一样-库必须遵循程序的目标文件。与静态库相比,这里的区别在于,您不必关心库之间的依赖关系,因为动态库会自行排序它们的依赖关系

最近的一些发行版显然默认使用
--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
以后依赖于另一个库,则需要重新链接二进制文件,如果您这样做的话。如果其他人在运行时使用
dlopen
加载
libb
(考虑动态加载插件),调用也会失败。因此,
“正确的”
实际上也应该是错误的
$ 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
$ 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"
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
g++ -o foobar  -Xlinker -start-group  -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a  -Xlinker -end-group 
g++ -o foobar  -Xlinker -start-group  -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a  -Xlinker -end-group 
gcc prog.o libA.a libB.a libA.a libB.a -o prog.x
QMAKE_LFLAGS += -Wl,--start-group