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

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

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

g++-o myApp-Wl,--开始组-lfoo-lbar-Wl,--结束组

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

g++-omyapp-lfoo-lbar-lfoo

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

我一直在查看我们的Makefiles,将第二个表单更改为第一个表单,但现在我的同事问我为什么。。。除了“因为它更干净”和模糊地感觉到另一种形式有风险之外,我没有一个好的答案

那么,多次链接同一个库是否会产生问题?例如,如果相同的.o被拉入两次,链接是否会因多个定义的符号而失败?或者我们是否有可能最终得到同一静态对象的两个副本,从而产生微妙的bug

基本上,我想知道如果多次链接同一个库,是否存在链接时间或运行时故障的可能性;如果是,如何触发它们。谢谢

问题在于

g++-omyapp-lfoo-lbar-lfoo

就是不能保证两次通过libfoo和一次通过libbar就足够了

采用Wl、-start组的方法-Wl,-end group更好,因为它更健壮

考虑以下场景(所有符号都位于不同的对象文件中):

  • myApp需要在libfoo中定义符号fooA
  • 符号fooA需要在libbar中定义符号barB
  • SymbolbarB需要在libfoo中定义SymbolfooC。这是循环依赖项,可由-lfoo-lbar-lfoo处理
  • SymbolfooC需要在libbar中定义SymbolbarD

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

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

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

因此使用

g++-o myApp-Wl,--开始组-lfoo-lbar-Wl,--结束组

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

然而,要支付的性能损失很小:在第一个示例中,与手动命令行相比,再次扫描了-lbar。不确定是否值得一提/仔细考虑

发表评论