通过将同一个库链接两次来解决循环依赖关系?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过将同一个库链接两次来解决循环依赖关系?相关的知识,希望对你有一定的参考价值。
我们将代码库分解为静态库。不幸的是,这些库具有循环依赖性;例如,libfoo.a
依赖于libbar.a
,反之亦然。
我知道处理这个问题的“正确”方法是使用链接器的--start-group
和--end-group
选项,如下所示:
g++ -o myApp -Wl,--start-group -lfoo -lbar -Wl,--end-group
但在我们现有的Makefile中,问题通常是这样处理的:
g++ -o myApp -lfoo -lbar -lfoo
(想象一下,这扩展到约20个具有复杂相互依赖性的库。)
我一直在通过我们的Makefiles将第二种形式更改为第一种形式,但现在我的同事们问我为什么......除了“因为它更清洁”以及另一种形式存在风险的模糊感,我不这样做有一个很好的答案。
那么,可以多次链接同一个库会产生问题吗?例如,如果相同的.o被拉入两次,链接是否会因多重定义的符号而失败?或者是否有任何风险,我们可以结束相同的静态对象的两个副本,创建微妙的错误?
基本上,我想知道链接时间或运行时失败是否有可能多次链接同一个库;如果是的话,如何触发它们。谢谢。
我能提供的只是缺乏反例。我以前从未见过第一种形式(尽管它显然更好),并且总是看到这种形式用第二种形式解决,并且没有观察到问题。
即便如此,我仍然建议改为第一种形式,因为它清楚地显示了库之间的关系,而不是依赖于链接器以特定方式运行。
也就是说,我建议至少考虑是否有可能重构代码以将常见的部分提取到其他库中。
这个问题
g++ -o myApp -lfoo -lbar -lfoo
是没有保证,两次通过libfoo
和一次通过libbar
就足够了。
使用Wl,--start-group ... -Wl,--end-group
的方法更好,因为更强大。
请考虑以下场景(所有符号都在不同的对象文件中):
myApp
需要在fooA
中定义的符号libfoo
。- 符号
fooA
需要在barB
中定义的符号libbar
。 - 符号
barB
需要在fooC
中定义的符号libfoo
。这是循环依赖,可以由-lfoo -lbar -lfoo
处理。 - 符号
fooC
需要在barD
中定义的符号libbar
。
为了能够在上面的情况下构建,我们需要将-lfoo -lbar -lfoo -lbar
传递给链接器。为什么?
- 链接器第一次看到
libfoo
并使用符号fooA
但不是fooC
的定义,因为到目前为止它没有必要将fooC
也包含在二进制文件中。然而,链接器开始寻找barB
的定义,因为它的定义是fooA
运行所必需的。 - 链接器看到
-libbar
,包括barB
(但不是barD
)的定义,并开始寻找fooC
的定义。 fooC
的定义可以在libfoo
中找到,当它第二次处理时。现在很明显,也需要barD
的定义 - 但为时已晚,命令行上没有libbar
了!
上面的例子可以扩展到任意的依赖深度(但这在现实生活中很少发生)。
因此使用
g++ -o myApp -Wl,--start-group -lfoo -lbar -Wl,--end-group
是一种更健壮的方法,因为链接器根据需要经常在库组上传递 - 只有当传递没有更改符号表时,链接器才会移动到命令行上的下一个库。
然而,要付出的性能损失很小:在第一个例子中,与手动命令行-lbar
相比,-lfoo -lbar -lfoo
再次被扫描。不确定是否值得一提/思考。
由于它是一个遗留应用程序,我敢打赌,库的结构继承了一些可能无关紧要的安排,比如用于构建你不再做的另一个产品。
即使仍有结构原因仍然存在于继承的库结构中,几乎可以肯定,从传统的安排中再构建一个库仍然是可以接受的。只需将20个库中的所有模块放入一个新的库liballofthem.a
中。然后每个应用程序只是g++ -o myApp -lallofthem ...
以上是关于通过将同一个库链接两次来解决循环依赖关系?的主要内容,如果未能解决你的问题,请参考以下文章