通过将同一个库链接两次来解决循环依赖关系?

Nem*_*emo 33 c++ linux linker circular-dependency ld

我们将代码库分解为静态库.不幸的是,这些库具有循环依赖性; 例如,libfoo.a取决于,libbar.a反之亦然.

我知道处理这个的"正确"方法是使用链接器--start-group--end-group选项,如下所示:

g++ -o myApp -Wl,--start-group -lfoo -lbar -Wl,--end-group
Run Code Online (Sandbox Code Playgroud)

但在我们现有的Makefile中,问题通常是这样处理的:

g++ -o myApp -lfoo -lbar -lfoo
Run Code Online (Sandbox Code Playgroud)

(想象一下,这扩展到约20个具有复杂相互依赖性的库.)

我一直在通过我们的Makefiles将第二种形式更改为第一种形式,但现在我的同事们问我为什么......除了"因为它更清洁"以及另一种形式存在风险的模糊感,我不这样做有一个很好的答案.

因此,可以连接同一个库多次曾经创造出了问题?例如,如果同一个.o被拉入两次,链接是否会失败?或者是否有任何风险,我们可以结束相同的静态对象的两个副本,创建微妙的错误?

基本上,我想知道链接时间或运行时失败是否有可能多次链接同一个库; 如果是的话,如何触发它们.谢谢.

ead*_*ead 9

问题在于

g++ -o myApp -lfoo -lbar -lfoo
Run Code Online (Sandbox Code Playgroud)

就是不能保证,两过libfoo一过libbar就够了。

方法Wl,--start-group ... -Wl,--end-group更好,因为更健壮。

考虑以下场景(所有符号都在不同的目标文件中):

  • myApp需要fooA在 中定义的符号libfoo
  • 符号fooA需要barBlibbar.
  • 符号barB需要fooClibfoo. 这是循环依赖,可以通过-lfoo -lbar -lfoo.
  • 符号fooC需要barDlibbar.

为了能够在上述情况下构建,我们需要传递-lfoo -lbar -lfoo -lbar给链接器。为什么?

  1. 链接器libfoo第一次看到并使用符号的定义fooA而不是fooC,因为到目前为止它认为没有必要将也包含fooC到二进制文件中。然而,链接器开始寻找 的定义barB,因为它的定义是运行所必需的fooA
  2. 链接器看到-libbar,包含barB(但 包含)的定义barD并开始寻找fooC.
  3. 当它第二次处理时,在 中fooC找到的定义libfoo。现在很明显,也barD需要定义- 但太晚libbar了,命令行上没有了!

上面的例子可以扩展到任意的依赖深度(但这在现实生活中很少发生)。

因此使用

g++ -o myApp -Wl,--start-group -lfoo -lbar -Wl,--end-group
Run Code Online (Sandbox Code Playgroud)

是一种更健壮的方法,因为链接器根据需要经常通过库组 - 只有当传递没有更改符号表时,链接器才会移动到命令行上的下一个库。

然而,有一个小的性能损失:-lbar与手动命令行相比,在第一个示例中被再次扫描-lfoo -lbar -lfoo。不确定是否值得一提/思考。


Mar*_*k B 6

我能提供的只是缺乏反例.我以前从未见过第一种形式(尽管它显然更好),并且总是看到这种形式用第二种形式解决,并且没有观察到问题.

即便如此,我仍然建议改为第一种形式,因为它清楚地显示了库之间的关系,而不是依赖于链接器以特定方式运行.

也就是说,我建议至少考虑是否有可能重构代码以将常见的部分提取到其他库中.

  • 谢谢,马克.虽然我觉得有趣的是我的问题中有一半的评论说"修复你的代码库!" 另一半说"你为什么要篡改工作代码库?" :-) (5认同)