当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《为什么库链接的顺序有时会导致GCC错误?》经验,为你挑选了6个好方法。

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



1> Johannes Sch..:

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


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

$ 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.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.


重复,直到所有符号都解决了,呃 - 你认为他们可以管理拓扑排序.LLVM有自己的78个静态库,谁知道什么是依赖项.是的,它还有一个脚本来确定编译/链接选项 - 但你不能在所有情况下使用它.
@Johannes - 确定最大强连通分量(例如Tarjans算法),然后拓扑排序组件的(固有非循环)图.可以将每个组件视为一个库 - 如果需要组件中的任何一个库,则依赖性循环将导致需要该组件中的所有库.所以不,没有必要循环遍历所有库以解决所有问题,并且不需要笨拙的命令行选项 - 使用两个众所周知的算法的一种方法可以正确处理所有情况.
@Steve这就是程序`lorder` +`tsort`的作用.但是,如果你有循环引用,有时候没有顺序.然后你只需循环遍历库列表,直到一切都解决了.
我想在这个优秀的答案中添加一个重要细节:使用" - (archives - )"或"--start-group archives --end-group"**是解决循环依赖关系的唯一可靠方法**因为每次链接器访问一个存档时,它就会拉入(并注册未解析的符号)_only目标文件,解析当前未解析的符号_.因此,CMake在依赖图中重复连接组件的算法可能偶尔会失败.(有关详细信息,请参阅[Ian Lance Taylor的优秀博客文章](http://www.airs.com/blog/archives/48)关于链接器.)
您的回答帮助我解决了我的链接错误,并且您已经非常清楚地解释了如何避免陷入困境,但您是否知道为什么它设计为以这种方式工作?
神奇的写作:它得到了我的投票.我将添加到关于_resolving cyclic dependencies_的部分,如果他们想要修改,我将把它留给原始的海报,当使用gcc传递`--start-group`和`--end-group`选项时,你必须在`-Wl,
推荐阅读
郑谊099_448
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有