GCC鏈接的幾個注意點


庫文件依賴順序

GCC在鏈接時對依賴庫的順序是敏感的,被依賴的庫必須放在后面,比如liba.a依賴libb.a,必須寫成liba.a libb.a,否則鏈接將出錯。在庫比較多依賴關系比較復雜或者相互依賴或者自己不清楚的情況下,可以使用下面的選項來強制GCC重復查找依賴庫:

g++ -o tt tt.o -Xlinker "-(" -lws2_32 -lclsocketd -Xlinker "-)"

強符號和弱符號

在鏈接中,如果多個目標文件中含有相同名字的全局符號的定義,鏈接器是怎么進行處理的?這里就涉及到強符號和弱符號的問題,編譯器默認函數和初始化了的全局變量為強符號,未初始化的全局變量為弱符號,強符號和弱符號都是針對定義來說的,不是針對符號的引用。鏈接器會按照如下規則處理重復定義的全局符號:

規則1:不允許強符號被多次定義,否則鏈接器報符號重定義錯誤。

規則2:如果一個符號在某個目標文件中是強符號,在其它文件中都是弱符號,那么選擇強符號。

規則3:如果弱符號在所有目標文件中都是弱符號,那么選擇占用空間最大的一個。

我們可以通過GCC的__atrribute__((weak))來定義任何一個強符號為弱符號,如:

__atrribute__((weak)) weak = 1;

鏈接時如果未找到某個符號的定義,鏈接器就會報符號未定義錯誤,這種被稱為強引用。與之對應的還有一種弱引用,如果弱引用的符號未定義,鏈接器對該引用不報錯。我們可以使用GCC中的__attribute__((weakref))這個關鍵字來聲明對一個外部函數的引用為弱引用,如:

__attribute__((weakref)) void foo();

int main()

{

    if (foo) foo();

}

這種弱符號和弱引用對庫來說十分有用,比如庫中定義的弱符號可以被用戶定位的強符號所覆蓋,從而使程序使用自定義版本的庫函數;或者程序可以對某些擴展功能模塊的引用定義為弱引用,如果我們去掉了某些功能模塊,那么程序也可以正常鏈接,只是缺少了相應的功能,這使得程序功能更容易裁剪和組合。

全局符號介入

在動態鏈接中,鏈接器按照各個模塊之間的依賴關系,對各個共享對象進行裝載並且將它們的符號並入到全局符號表時,如果兩個不同的模塊定義了同一個符號,會出現什么結果呢?這個問題涉及到共享對象全局對象介入,即一個共享對象里面的全局對象會被另一個共享對象的同名全局符號覆蓋。Linux下的動態鏈接器的處理規則是這樣的:當一個符號需要被加入全局符號表時,如果相同的符號名已經存在,則后加入的符號被忽略。

共享庫版本

Linux使用共享庫版本的方法來解決共享庫的兼容性問題,它規定共享庫的文件命名規則如下:

libname.so.x.y.z

x表示主版本號,主版本號表示庫的重大升級,不同主版本號之間是不兼容的。

y表示次版本號,次版本號表示庫的增量升級,即新增一些新的接口符號,且保持原來的符號不變。在主版本號相同的情況下,高的此版本號的庫向下兼容低的次版本號的庫。

z表示發布版本號,發布版本號表示庫的一些錯誤修正、性能改進等,並不添加任何新的接口,也不對接口進行更改。相同主版本號、次版本號的共享庫之間完全兼容。

Linux采用一種叫做SO-NAME的命令機制來記錄共享庫的依賴關系。每個共享庫都有一個對應的SO-NAME,這個SO-NAME即共享庫的文件名去掉次版本號和發布版本號,保留主版本號。比如一個共享庫叫做libfoo.so.2.6.1,那么它的SO-NAME就是libfoo.so.2。系統會為每個共享庫在它所在的目錄創建一個跟SO-NAME相同的並且指向它的軟鏈接,這個軟鏈接會指向目錄中主版本號相同、次版本號和發布版本號最新的共享庫。

在編譯輸出ELF文件時,將被依賴共享庫的SO-NAME保存到.dynamic中,這樣當動態鏈接器進行共享庫依賴文件查找時,就會根據系統中各種共享目錄中的 SO-NAME軟鏈接自動定向到所兼容的最新版本的共享庫。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM