考慮以下源檔案:
交流:
extern int baz();
int foo() { return 123; }
int bar() { return baz() 1; }
公元前:
extern int foo();
int main() { return foo(); }
現在,當我嘗試使用這些資源構建程式時,會發生以下情況:
$ gcc -c -o a.o a.c
$ gcc -c -o b.o b.c
$ gcc -o prog a.o b.o
/usr/bin/ld: a.o: in function `bar':
a.c:(.text 0x15): undefined reference to `baz'
collect2: error: ld returned 1 exit status
這是在 Devuan GNU/Linux Chimaera 上,帶有 GNU ld 2.35.2,GCC 10.2.1。
為什么會這樣?我的意思是,不需要任何復雜的優化就可以知道在某些時候baz()確實不需要bar()- ld 在某些時候自然會注意到這一點 - 例如,當完成它的遍歷時,bar()沒有注意到使用的位置baz()。
現在,你可以說“einpoklum,你沒有讓編譯器為你找麻煩”——我想這很公平,但即使我使用-O3這些指令,我也會得到同樣的錯誤。
注意:啟用 LTO 和優化后,我們可以繞過這個問題:
$ gcc -c -flto -O1 -o b.o b.c
$ gcc -c -flto -O1 -o a.o a.c
$ gcc -o prog -O1 -flto a.o b.o
$ /prog ; echo $?;
123
uj5u.com熱心網友回復:
在此代碼的“普通”傳統編譯中:
extern int baz();
int foo() { return 123; }
int bar() { return baz() 1; }
編譯器創建一個目標模塊,其中包含兩個例程的代碼以及符號定義foo和bar對baz. 沒有什么可以告訴聯結器屬于的代碼在哪里foo開始和結束,屬于代碼的開始和結束在哪里,bar甚至任何給定的代碼片段——或目標模塊中的任何給定位元組——只屬于foo或之一bar。如果我用匯編語言撰寫并匯編成一個物件模塊,我可以在其中包含foo跳轉到的代碼bar(僅使用由匯編程式計算的硬編碼偏移量,并且不會在聯結器可見的任何符號中顯示),反之亦然。
所以聯結器無法知道這一點foo并且bar可以分開。
后來,為編譯器創建了一個協議,以保持函式分離并在目標模塊中提供足夠的資訊,以便聯結器可以確定它們在哪里分離,并告訴聯結器可以分離函式。當啟用該選項時,聯結器可能能夠包含foo在程式中而不包含bar.
該功能還不是工具中的默認功能,這是各種構建系統和專案、慣性和當前實踐的遺留問題。
uj5u.com熱心網友回復:
如果您使用gcc和 binutilsld來構建您的程式,您需要將函式放在單獨的部分中。它由-fdata-sections& -ffunction-sections命令列選項存檔。
與資料相同。然后,如果您不希望在可執行檔案中包含死代碼,則需要使用--gc-sections ldoption 啟用它。
把這一切放在一起:
$ gcc -fdata-sections -ffunction-sections -c -o a.o a.c
$ gcc -c -o b.o b.c
$ gcc -Wl,--gc-sections -o prog a.o b.o
$ /prog ; echo $?
123
如果您想在默認情況下啟用它,GCC啟用這些選項的簡單構建。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/418550.html
標籤:
下一篇:三元條件控制
