2015-12-08 638 views
2

我的可执行文件与许多静态库(通常在Linux上的50到100个压缩文件)之间链接。偶尔在这些档案中存在依赖性循环。这些库出现在链接命令行上的顺序非常重要,请参阅here。尝试手动订购这些库至少是非常耗时的,特别是当存在循环时。gcc ld:确定静态库的链接顺序的方法

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

+0

我明白了。通常,人们会想自动解决它们,而不是为自己找到正确的顺序。我以为你问过你想避免手动修改订单。请问为什么你想知道订单?是否因为你担心 - 开始组 - 将会变慢? –

+0

@ l3x,它与性能有关。预定义顺序允许链接器只扫描一次库。当您尝试将70个库链接到可执行文件时,这可能会产生很大的不同。 – ThomasMcLeod

+1

如果ttere是依赖性循环,则没有正确的顺序。你将不得不隔离在它们之间具有这些循环的库,并且仅使用围绕这些库的开始/结束组,或者将这些库和“tsort”组合起来。 – EJP

回答

4

你想要一个拓扑排序

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


简短的回答:使用--start-groupliblist--end-group并用它做。

的几个原因:

的LD组是智能。它不只是在文件上循环。它通过组进行初始传递,但记住符号。因此,在随后的传递中,它使用缓存的符号表信息,因此速度非常快。

对于复杂的相互作用,可能能够破除一切周期有toposort的,所以你还是需要即使liblist已经地形排序的组。

我们说了多少时间?而且,你觉得多少时间会被节省?你将如何衡量事情以证明你确实需要这些。


去为黄金

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


如何排序拓扑库列表

如果我们这样做info coreutils,该tsort的部分将给出如何toposort符号表的例子。

但是,在我们能够做到这一点之前,我们需要得到符号。对于.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's中],它们引用由其中另一个.o定义的符号。因此,我们只记录一次符号,如果我们看到非U型,我们“促销”它(例如,如果我们看到U foo并且后来看到T foo我们将foo的类型更改为T [同样适用于D和B] 。

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

请注意,这有点浪费,因为我们可能会输出许多文件相关性行,因为上面将为每个符号生成一行。所以,我们应该跟踪行输出,并且只输出唯一文件对的依赖行。此外,请注意,它可能有foo barbar foo。也就是说,实际上,一个周期。虽然我们只是想要foo bar和/或bar foo的一个副本,但他们应该而不是排除彼此。

好了,现在上面的输出喂tsort,它会给我们拓扑排序版本liblist我们要的。

至于应该是显而易见的,脚本解析可能需要一些时间,所以tsort的输出应该在一个文件中缓存,并重建在makefile,基于liblist


的依赖列表

将一些.a文件,以.o文件

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

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

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

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

即使正常的构建makefiles无法更改,“最终”顶级可能需要.a,或者将其提取到.o's中,或者使用带有-r的ld力加载选项来获取“大“.o

+0

回到当天,我们使用了'ar tvf libXXX.a \'nm -no * .o | tsort \'',但我忘记了细节,已经有25年或更长时间了。 – EJP

+0

@EJP是的。 25年是关于我有多少内存来疏通算法:-)。叹。我记得几年前,当我第一次发现ld组时,我是“哇,没有更多的拓扑排序!”并更改了我的所有构建脚本以使用它们。在这里看来,“什么是旧的又是新的......” –

+0

真的很棒的答案。一个问题是,nm产生的名称是否应该被去除。我认为让他们受伤会提供更精确的匹配,但对于人类来说难以阅读,这是正确的吗? – ThomasMcLeod