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
Resolving circular dependencies by linking the same library twice?
提问by Nemo
We have a code base broken up into static libraries. Unfortunately, the libraries have circular dependencies; e.g., libfoo.a
depends on libbar.a
and vice-versa.
我们有一个分解成静态库的代码库。不幸的是,这些库有循环依赖;例如,libfoo.a
取决于,libbar.a
反之亦然。
I know the "correct" way to handle this is to use the linker's --start-group
and --end-group
options, 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 libfoo
and one pass over libbar
are enough.
就是不能保证,两过libfoo
一过libbar
就够了。
The approach with Wl,--start-group ... -Wl,--end-group
is better, because more robust.
方法Wl,--start-group ... -Wl,--end-group
更好,因为更健壮。
Consider the following scenario (all symbols are in different object-files):
考虑以下场景(所有符号都在不同的目标文件中):
myApp
needs symbolfooA
defined inlibfoo
.- Symbol
fooA
needs symbolbarB
defined inlibbar
. - Symbol
barB
needs symbolfooC
defined inlibfoo
. This is the circular dependency, which can be handled by-lfoo -lbar -lfoo
. - Symbol
fooC
needs symbolbarD
defined inlibbar
.
myApp
需要fooA
在 中定义的符号libfoo
。- 符号
fooA
需要barB
在libbar
. - 符号
barB
需要fooC
在libfoo
. 这是循环依赖,可以通过-lfoo -lbar -lfoo
. - 符号
fooC
需要barD
在libbar
.
To be able to build in the case above, we would need to pass -lfoo -lbar -lfoo -lbar
to the linker. Why?
为了能够在上述情况下构建,我们需要传递-lfoo -lbar -lfoo -lbar
给链接器。为什么?
- The linker sees
libfoo
for the first time and uses definitions of symbolfooA
but notfooC
, because so far it doesn't see a necessity to include alsofooC
into the binary. The linker however starts to look for definition ofbarB
, because its definition is needed forfooA
to function. - The linker sees
-libbar
, includes the definition ofbarB
(but notbarD
) and starts to look for definition offooC
. - The definition of
fooC
is found inlibfoo
, when it processed for the second time. Now it becomes evident, that also the definition ofbarD
is needed - but too late there is nolibbar
on the command line anymore!
- 链接器
libfoo
第一次看到并使用符号的定义fooA
而不是fooC
,因为到目前为止它认为没有必要将也包含fooC
到二进制文件中。然而,链接器开始寻找 的定义barB
,因为它的定义是运行所必需的fooA
。 - 链接器看到
-libbar
,包含barB
(但不包含)的定义barD
并开始寻找fooC
. - 当它第二次处理时,在 中
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 -lbar
were 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
。不确定是否值得一提/思考。