使用链接时的--start-group和--end-group比创建静态库更快吗?

12

如果在构建脚本中构建静态库,并且想要在链接最终可执行文件时使用这些静态库,则提到.a文件的顺序很重要:

g++ main.o hw.a gui.a -o executable
如果gui.a使用了hw.a中定义的某些内容,链接将会失败。这是因为在处理hw.a时,连接器不知道稍后需要这个定义,并且没有在生成的可执行文件中包含它。手动修改链接器行不太实际,因此一个解决方案是使用--start-group--end-group,使连接器再次遍历库,直到不再找到未定义的符号为止。
g++ main.o -Wl,--start-group hw.a gui.a -Wl,--end-group -o executable

然而GNU ld手册说:

使用这个选项会有显著的性能代价。只有在两个或多个存档之间存在不可避免的循环引用时,才最好使用它。

所以我认为将所有的.a文件放在一起,并用带有索引的一个.a文件来指示需要连接的文件顺序可能更好(-s选项的GNU ar)。然后将这个.a文件提供给g++

但我想知道这样做是比使用组命令更快还是更慢。 还有这种方法是否有任何问题? 我也想知道是否有更好的解决这些相互依赖问题的方法?


编辑:我编写了一个程序,可以获取.a文件列表并生成合并的.a文件。适用于GNU通用ar格式。像这样打包LLVM的所有静态库:

$ ./arcat -o combined.a ~/usr/llvm/lib/libLLVM*.a

我将速度与手动解压所有.a文件,然后使用ar将它们放入新的.a文件并重新计算索引进行了比较。使用我的arcat工具,我得到了大约500毫秒的一致运行时间。而手动方法的时间变化很大,需要大约2秒钟。所以我认为这样做是值得的。

代码在这里。我已将其放入公共领域 :)

2个回答

3
您可以使用 lordertsort 工具确定顺序,例如:
libs='/usr/lib/libncurses.a /usr/lib/libedit.a'
libs_ordered=$(lorder $libs | tsort)

结果是/usr/lib/libedit.a /usr/lib/libncurses.a,因为libedit依赖于libncurses。

如果您不对每个链接命令运行 lordertsort,那么这只有在使用 --start-group 以上才有益。此外,它不允许像--start-group一样有相互/循环依赖性。


1
在提问之前,我试图找到Linux中的lorder,但是我找不到它。我在互联网上只找到了几个man页面,上面写着lorder已经被弃用,而ar替代了它。我找不到应该给ar什么命令才能实现与lorder相同的功能。 - Johannes Schaub - litb
Debian和Ubuntu包含在bsdmainutils软件包中。 - jilles

2

有没有第三种选择,只需一开始就构建一个单一的库? 我曾经遇到过类似的问题,最终决定采用第三种选择。

根据我的经验,将.a文件合并比使用group命令要慢。 您可以从存档中提取所有文件,然后从较小的文件创建新的.a文件

但是,您必须小心处理两个文件恰好包含相同定义的情况(您可以使用nm显式检查每个库中包含哪些定义)


2
+1 在两个库之间存在相互依赖的情况下,制作两个库是毫无意义的。 - R.. GitHub STOP HELPING ICE
3
即使libB依赖于libA,只要libA可以独立于libB使用,它们可以成为单独的库,但如果两者之间存在相互依赖关系,则将它们分开纯粹是一种麻烦事。 - R.. GitHub STOP HELPING ICE
@Johannes 要实现你想要的功能,你需要提取所有的 o 文件(使用 ar x 命令),然后再重新组合它们(使用另一个 ar 命令)。但是你应该确保不会意外地重复任何条目(例如,如果你有一个内联函数,并且编译器决定不为多个源文件内联它,那么你将在多个 .o 文件中拥有该内联函数的定义)。 - Foo Bah
1
@FooBah 我的 arcat 似乎正常工作了。你不需要解决任何问题,只需在索引中说明符号所在的文档成员即可。 - Johannes Schaub - litb
相互依赖并不一定意味着库文件总是可以合并。我遇到的情况中可能是唯一的一个用例是,如果这些库将提供非唯一标识符以创建链接时的项目配置(一种基于模块的行为,其中功能DoFoo()可能是通过链接libFooBar.a或libFooBaz.a来提供的)。 - Gerasimos R
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接