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

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


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


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

$ 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 -) (你可能需要转义parens,比如-(-) )。 链接器然后多次搜索那些封闭的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.sod.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 (想到动态加载插件),调用也会失败。 所以"right"应该是wrong


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

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


下面是一个例子,以说明在涉及静态库时GCC如何与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
    
    链接地址: http://www.djcxy.com/p/15035.html

    上一篇: Why does the order in which libraries are linked sometimes cause errors in GCC?

    下一篇: what is this technique called?