我的可執行文件與許多靜態庫(通常在Linux上的50到100個壓縮文件)之間鏈接。偶爾在這些檔案中存在依賴性循環。這些庫出現在鏈接命令行上的順序非常重要,請參閱here。嘗試手動訂購這些庫至少是非常耗時的,特別是當存在循環時。gcc ld:確定靜態庫的鏈接順序的方法
問題:是否有一種實用工具或技術可以分析代碼庫並生成正確的鏈接命令行排序?
我的可執行文件與許多靜態庫(通常在Linux上的50到100個壓縮文件)之間鏈接。偶爾在這些檔案中存在依賴性循環。這些庫出現在鏈接命令行上的順序非常重要,請參閱here。嘗試手動訂購這些庫至少是非常耗時的,特別是當存在循環時。gcc ld:確定靜態庫的鏈接順序的方法
問題:是否有一種實用工具或技術可以分析代碼庫並生成正確的鏈接命令行排序?
你想要一個拓撲排序。
tsort
程序會做到這一點,但你需要做更多的工作來使用它[準備編寫一個perl/python腳本]。另外還有另一種方法。並且,我將轉到下面的「howto」,因爲我以前做過這種事情。
簡短的回答:使用
--start-group
liblist--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 bar
和bar 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
回到當天,我們使用了'ar tvf libXXX.a \'nm -no * .o | tsort \'',但我忘記了細節,已經有25年或更長時間了。 – EJP
@EJP是的。 25年是關於我有多少內存來疏通算法:-)。嘆。我記得幾年前,當我第一次發現ld組時,我是「哇,沒有更多的拓撲排序!」並更改了我的所有構建腳本以使用它們。在這裏看來,「什麼是舊的又是新的......」 –
真的很棒的答案。一個問題是,nm產生的名稱是否應該被去除。我認爲讓他們受傷會提供更精確的匹配,但對於人類來說難以閱讀,這是正確的嗎? – ThomasMcLeod
我明白了。通常,人們會想自動解決它們,而不是爲自己找到正確的順序。我以爲你問過你想避免手動修改訂單。請問爲什麼你想知道訂單?是否因爲你擔心 - 開始組 - 將會變慢? –
@ l3x,它與性能有關。預定義順序允許鏈接器只掃描一次庫。當您嘗試將70個庫鏈接到可執行文件時,這可能會產生很大的不同。 – ThomasMcLeod
如果ttere是依賴性循環,則沒有正確的順序。你將不得不隔離在它們之間具有這些循環的庫,並且僅使用圍繞這些庫的開始/結束組,或者將這些庫和「tsort」組合起來。 – EJP