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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9380363/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 04:43:28  来源:igfitidea点击:

Resolving circular dependencies by linking the same library twice?

c++linuxlinkerldcircular-dependency

提问by Nemo

We have a code base broken up into static libraries. Unfortunately, the libraries have circular dependencies; e.g., libfoo.adepends on libbar.aand vice-versa.

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

I know the "correct" way to handle this is to use the linker's --start-groupand --end-groupoptions, like so:

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

g++ -o myApp -Wl,--start-group -lfoo -lbar -Wl,--end-group

But in our existing Makefiles, the problem is typically handled like this:

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

g++ -o myApp -lfoo -lbar -lfoo

(Imagine this extended to ~20 libraries with complex interdependencies.)

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

I have been going through our Makefiles changing the second form to the first, but now my co-workers are asking me why... And other than "because it's cleaner" and a vague sense that the other form is risky, I do not have a good answer.

我一直在通过我们的 Makefiles 将第二种形式更改为第一种形式,但现在我的同事问我为什么......除了“因为它更干净”和另一种形式有风险的模糊感觉,我不有一个很好的答案。

So, can linking the same library multiple times evercreate a problem? For example, could the link fail with multiply-defined symbols if the same .o gets pulled in twice? Or is there any risk we could wind up with two copies of the same static object, creating subtle bugs?

因此,可以连接同一个库多次曾经创造出了问题?例如,如果相同的 .o 被拉入两次,链接是否会因多重定义的符号而失败?或者我们是否有可能得到同一个静态对象的两个副本,从而产生细微的错误?

Basically, I want to know if there is any possibility of link-time or run-time failures from linking the same library multiple times; and if so, how to trigger them. Thanks.

基本上,我想知道是否有可能因多次链接同一个库而导致链接时或运行时失败;如果是这样,如何触发它们。谢谢。

采纳答案by Mark B

All I can offer is a lack of counter-example. I've actually never seen the first form before (even though it's clearly better) and always seen this solved with the second form, and haven't observed problems as a result.

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

Even so I would still suggest changing to the first form because it clearly shows the relationship between the libraries rather than relying on the linker behaving in a particular way.

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

That said, I would suggest at least considering if there's a possibility of refactoring the code to pull out the common pieces into additional libraries.

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

回答by wallyk

Since it is a legacy application, I bet the structure of the libraries is inherited from some arrangement which probably does not matter any more, such as being used to build another product which you no longer do.

由于它是一个遗留应用程序,我敢打赌库的结构是从一些可能不再重要的安排中继承的,例如用于构建您不再做的另一个产品。

Even if still structural reasons remain for the inherited library structure, almost certainly, it would still be acceptable to build one more library from the legacy arrangement. Just put all the modules from the 20 libraries into a new library, liballofthem.a. Then every single application is simply g++ -o myApp -lallofthem ...

即使继承的库结构仍然存在结构性原因,几乎可以肯定,从遗留安排中再构建一个库仍然是可以接受的。只需将 20 个库中的所有模块放入一个新库中,liballofthem.a. 那么每一个应用程序都是简单的g++ -o myApp -lallofthem ...

回答by ead

The problem with

问题在于

g++ -o myApp -lfoo -lbar -lfoo

is that there is no guarantee, that two passes over libfooand one pass over libbarare enough.

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

The approach with Wl,--start-group ... -Wl,--end-groupis better, because more robust.

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

Consider the following scenario (all symbols are in different object-files):

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

  • myAppneeds symbol fooAdefined in libfoo.
  • Symbol fooAneeds symbol barBdefined in libbar.
  • Symbol barBneeds symbol fooCdefined in libfoo. This is the circular dependency, which can be handled by -lfoo -lbar -lfoo.
  • Symbol fooCneeds symbol barDdefined in libbar.
  • myApp需要fooA在 中定义的符号libfoo
  • 符号fooA需要barBlibbar.
  • 符号barB需要fooClibfoo. 这是循环依赖,可以通过-lfoo -lbar -lfoo.
  • 符号fooC需要barDlibbar.

To be able to build in the case above, we would need to pass -lfoo -lbar -lfoo -lbarto the linker. Why?

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

  1. The linker sees libfoofor the first time and uses definitions of symbol fooAbut not fooC, because so far it doesn't see a necessity to include also fooCinto the binary. The linker however starts to look for definition of barB, because its definition is needed for fooAto function.
  2. The linker sees -libbar, includes the definition of barB(but notbarD) and starts to look for definition of fooC.
  3. The definition of fooCis found in libfoo, when it processed for the second time. Now it becomes evident, that also the definition of barDis needed - but too late there is no libbaron the command line anymore!
  1. 链接器libfoo第一次看到并使用符号的定义fooA而不是fooC,因为到目前为止它认为没有必要将也包含fooC到二进制文件中。然而,链接器开始寻找 的定义barB,因为它的定义是运行所必需的fooA
  2. 链接器看到-libbar,包含barB(但包含)的定义barD并开始寻找fooC.
  3. 当它第二次处理时,在 中fooC找到的定义libfoo。现在很明显,也barD需要定义- 但太晚libbar了,命令行上没有了!

The example above can be extended to an arbitrary dependency depth (but this happens seldom in real life).

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

Thus using

因此使用

g++ -o myApp -Wl,--start-group -lfoo -lbar -Wl,--end-group

is a more robust approach, because linker passes as often over the library group as needed - only when a pass didn't change the symbol table will the linker move on to the the next library on the command line.

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

There is however a small performance penalty to pay: in the first example -lbarwere scanned once more compared with the manual command line -lfoo -lbar -lfoo. Not sure whether it is worth mentioning/thinking about through.

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