gcc ld:确定静态库链接顺序的方法

时间:2015-12-08 19:41:11

标签: c++ gcc ld static-linking

我的可执行文件与许多静态库链接,通常在Linux上有50到100个存档。偶尔在这些档案中存在依赖循环。这些库在链接命令行上显示的顺序非常重要,请参阅here。尝试手动订购这么多库是非常耗时的,特别是当存在循环时。

问题:是否有一种实用程序或技术可以分析代码库并生成正确的链接命令行排序?

2 个答案:

答案 0 :(得分:6)

  

您需要拓扑排序

tsort程序会这样做,但你需要做更多工作才能使用它[准备写一个perl / python脚本]。此外,还有另一种方式。并且,我到达下面的“howto”,因为我之前做过这样的事情。

  

简答:使用--start-group liblist --end-group并完成它。

有几个原因:

ld组是智能。它不只是循环文件。它首先通过组,但记住符号。因此,在后续传递中,它使用缓存的符号表信息,因此它非常快。

对于复杂的互动,你可能能够摆脱所有周期与一个转发点,所以你仍然需要即使 liblist 已被拓扑排序,也是一个群组。

我们在谈论多长时间?而且,你认为会节省多少时间?你将如何衡量事情以证明你真的需要这个。

  

寻找黄金

请考虑使用ld,而不是ld.gold。它已从头开始重写为 not 使用libbfd [这很慢]并直接在ELF文件上运行。创建它的主要动机是简单性和速度

  

如何对库列表进行拓扑排序

如果我们info coreutils,则tsort部分将举例说明如何设置符号表。

但是,在我们开始之前,我们需要获得符号。对于.a文件,nm可以提供列表:nm -go <liblist>

输出如下:

libbfd.a:
libbfd.a:archive.o:0000000000000790 T _bfd_add_bfd_to_archive_cache
libbfd.a:archive.o:                 U bfd_alloc
libbfd.a:archive.o:0000000000000c20 T _bfd_append_relative_path
libbfd.a:archive.o:                 U bfd_assert
libbfd.a:archive.o:                 U bfd_bread
libbfd.a:archive.o:00000000000021b0 T _bfd_bsd44_write_ar_hdr
libbfd.a:archive.o:                 U strcpy
libbfd.a:archive.o:                 U strlen
libbfd.a:archive.o:                 U strncmp
libbfd.a:archive.o:                 U strncpy
libbfd.a:archive.o:                 U strtol
libbfd.a:archive.o:                 U xstrdup
libbfd.a:bfd.o:                 U __asprintf_chk
libbfd.a:bfd.o:00000000000002b0 T _bfd_abort
libbfd.a:bfd.o:0000000000000e40 T bfd_alt_mach_code
libbfd.a:bfd.o:                 U bfd_arch_bits_per_address
libbfd.a:bfd.o:0000000000000260 T bfd_assert
libbfd.a:bfd.o:0000000000000000 D _bfd_assert_handler
libbfd.a:bfd.o:0000000000000450 T bfd_canonicalize_reloc
libbfd.a:bfd.o:                 U bfd_coff_get_comdat_section
libbfd.a:bfd.o:0000000000000510 T _bfd_default_error_handler
libbfd.a:bfd.o:0000000000000fd0 T bfd_demangle
libbfd.a:bfd.o:                 U memcpy
libbfd.a:bfd.o:                 U strchr
libbfd.a:bfd.o:                 U strlen
libbfd.a:opncls.o:0000000000000a50 T bfd_openr
libbfd.a:opncls.o:0000000000001100 T bfd_openr_iovec
libbfd.a:opncls.o:0000000000000b10 T bfd_openstreamr
libbfd.a:opncls.o:0000000000000bb0 T bfd_openw
libbfd.a:opncls.o:0000000000001240 T bfd_release
libbfd.a:opncls.o:                 U bfd_set_section_contents
libbfd.a:opncls.o:                 U bfd_set_section_size
libbfd.a:opncls.o:0000000000000000 B bfd_use_reserved_id
libbfd.a:opncls.o:00000000000010d0 T bfd_zalloc
libbfd.a:opncls.o:00000000000011d0 T bfd_zalloc2

libglib-2.0.a:
libglib-2.0.a:libglib_2_0_la-gallocator.o:0000000000000100 T g_allocator_free
libglib-2.0.a:libglib_2_0_la-gallocator.o:00000000000000f0 T g_allocator_new
libglib-2.0.a:libglib_2_0_la-gallocator.o:0000000000000150 T g_blow_chunks
libglib-2.0.a:libglib_2_0_la-gallocator.o:0000000000000160 T g_list_push_allocator
libglib-2.0.a:libglib_2_0_la-gallocator.o:0000000000000060 T g_mem_chunk_alloc
libglib-2.0.a:libglib_2_0_la-gallocator.o:0000000000000090 T g_mem_chunk_alloc0
libglib-2.0.a:libglib_2_0_la-gallocator.o:0000000000000110 T g_mem_chunk_clean
libglib-2.0.a:libglib_2_0_la-gallocator.o:0000000000000120 T g_mem_chunk_reset
libglib-2.0.a:libglib_2_0_la-gallocator.o:00000000000001b0 T g_node_pop_allocator
libglib-2.0.a:libglib_2_0_la-gallocator.o:00000000000001a0 T g_node_push_allocator
libglib-2.0.a:libglib_2_0_la-gallocator.o:                 U g_return_if_fail_warning
libglib-2.0.a:libglib_2_0_la-gallocator.o:                 U g_slice_alloc
libglib-2.0.a:libglib_2_0_la-gallocator.o:                 U g_slice_alloc0
libglib-2.0.a:libglib_2_0_la-gallocator.o:                 U g_slice_free1
libglib-2.0.a:libglib_2_0_la-gallocator.o:0000000000000190 T g_slist_pop_allocator
libglib-2.0.a:libglib_2_0_la-gslice.o:                 U g_private_get
libglib-2.0.a:libglib_2_0_la-gslice.o:                 U g_private_set
libglib-2.0.a:libglib_2_0_la-gslice.o:                 U g_return_if_fail_warning
libglib-2.0.a:libglib_2_0_la-gslice.o:00000000000010d0 T g_slice_alloc
libglib-2.0.a:libglib_2_0_la-gslice.o:0000000000001770 T g_slice_alloc0
libglib-2.0.a:libglib_2_0_la-gslice.o:00000000000017a0 T g_slice_copy
libglib-2.0.a:libglib_2_0_la-gslice.o:00000000000017e0 T g_slice_free1
libglib-2.0.a:libglib_2_0_la-gslice.o:0000000000001ae0 T g_slice_free_chain_with_offset

所以,语法将是:

<libname.a>:<objname.o>:<address> [TDB] <symbol>
<libname.a>:<objname.o>:          U     <symbol>

我们需要提取 libname.a ,符号类型(例如T,D,B,U)和符号

我们创建一个文件列表。在每个文件结构中,我们记住所有符号及其类型。任何 U [未定义的符号]的类型都将定义符号。

注意,当我们构建符号表时,库可能有多个U [在各种.o中],它们引用由另一个.o定义的符号。所以,我们只记录一次符号,如果我们看到一个非U类型,我们就“推广”它(例如,如果我们看到U foo,然后看到T foo,我们就会改变 foo的类型T [同样适用于D和B]。

现在我们遍历文件列表(例如curfile)。对于文件符号表中的每个符号,如果类型为U [未定义],我们扫描所有文件,查找非U符号定义。如果我们找到一个(在symfile(例如)中),我们可以输出tsort的依赖关系:<curfile> <symfile>。我们对所有文件和符号重复此操作。

请注意,这有点浪费,因为我们可以输出许多相同的文件依赖关系行,因为上面会为每个符号生成一行。因此,我们应该跟踪行输出,并仅输出唯一文件对的依赖行。另请注意, 可能同时包含foo bar bar foo。也就是说,实际上是一个循环。虽然我们只需要foo bar和/或bar foo的一个副本,但它们会相互排斥。

好的,现在将上面的输出提供给tsort,它将为我们提供我们想要的拓扑排序版本的 liblist

显而易见,脚本解析可能需要一些时间,因此tsort输出应该缓存在一个文件中,并根据 liblist

  

将一些.a文件转换为.o文件

如果给定的库使用其所有[或大部分] .o文件,而不是ar rv libname.a ...,请考虑执行ld -r libname.o ...

这与创建共享库.so文件的方法类似,但“大”.o仍然可以静态链接。

现在,您有一个.o,它将比.a更快地链接,因为库内链接已经解决。此外,它将有助于依赖循环。

对topo脚本的轻微扩展可以告诉你哪些库适合这个。

即使正常的构建makefile无法更改,“最终”顶级可能需要.a,要么将其提取到.o中,要么使用带有-r的ld force load选项来获取“大”的.o

答案 1 :(得分:1)

BSD世界在nm周围有一个包装器,以完成生成lordertsort适当输入的工作。它实际上只是一个shell脚本,您可以看到source code in FreeBSD。它完全按照现有答案说的去做,但是以两个文件的形式提供,以供参考和参考。 (同样,在输入文件名中对空格的处理也很差,但我离题了。)

lorder很古老。自从AT&T Unix 7版(1979年)以来就一直存在。


关于建议使用更现代的ld.gold来提高速度, 也考虑使用LLVM的ld.lld。 LLD易于使用,因为它完全不需要良好的链接顺序。是faster still