我们将代码库分解为静态库。不幸的是,这些库具有循环依赖性;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 - Symbol
barB需要在libfoo中定义SymbolfooC。这是循环依赖项,可由-lfoo-lbar-lfoo处理 - Symbol
fooC需要在libbar中定义SymbolbarD
为了能够在上述情况下构建,我们需要将-lfoo-lbar-lfoo-lbar传递给链接器。为什么?
- 链接器第一次看到了
libfoo,并使用了symbolfooA的定义,但没有使用fooC,因为到目前为止,它认为没有必要将fooC也包含到二进制文件中。然而,链接器开始寻找barB的定义,因为它的定义是fooA运行所必需的 - 链接器看到
-libbar,包含倒钩的定义(但不barD),并开始查找fooC的定义 - 当第二次处理
libfoo时,可以在libfoo中找到fooC的定义。现在很明显,barD的定义也是需要的-但是现在命令行上没有libbar了,太晚了
上面的例子可以扩展到任意的依赖深度(但这在现实生活中很少发生)
因此使用
g++-o myApp-Wl,--开始组-lfoo-lbar-Wl,--结束组
是一种更健壮的方法,因为链接器根据需要经常通过库组-只有当通过没有更改符号表时,链接器才会移动到命令行上的下一个库
然而,要支付的性能损失很小:在第一个示例中,与手动命令行相比,再次扫描了-lbar。不确定是否值得一提/仔细考虑