鏈接裝載與庫《程序員的自我修養》 之 全局符號衝突問題
1. 由強符號和弱符號引起的全局符號衝突
強符號:編譯器默認函數和初始化了的全局變量
弱符號:未初始化的全局變量爲弱符號(也可以通過gcc的__attribute__((weak))指定)
衝突的例子:如果我們在目標文件A和目標文件B都定義了一個全局整形變量g_variable,並將它們都初始化,這樣在鏈接時鏈接器就會報multiple definition的錯誤,這種錯誤就是因爲強符號不能被多次定義。
但如果我們在A中定義了一個double類型的g_variable,B文件中定義了一個int類型的g_variable,它們都沒有被初始化過,則鏈接器會選擇double類型8字節爲g_variable分配空間。
原因:由於鏈接器需要支持弱符號機制(對於程序庫來說這種模式便於用戶進行擴展和自定義),並且鏈接器並不支持鏈接時的符號類型,所以允許多個不同類型弱符號存在。
如果想關掉弱符號可以使用gcc的“-fno-common”選項
被多次定義的全局符號,鏈接器會按照以下規則進行處理:
1. 不允許強符號被多次定義,否則,鏈接器報符號重複定義錯誤。
2. 如果一個符號在某個目標文件中是強符號,在其他文件中都是弱符號,那麼選擇強符號
3. 如果一個符號在所有目標文件中都是弱符號,那麼選擇佔用空間最大的那個,見上例。
2. 共享對象中的符號衝突
當不同共享對象中存在同名符號時,會存在全局符號介入(Global Symbol Interpose)的現象:一個共享對象裏面的全局符號被另一個共享對象的同名全局符號覆蓋。
關於這個問題,linux下動態鏈接器的處理策略是:當一個符號需要被加入到全局符號表時,如果相同的符號名已經存在,則後加入的符號被忽略。也就是說,如果不同的共享對象存在同名符號時,最後剩下誰是依賴於動態鏈接器的加載順序的(往往按照廣度優先)
例如:a.so中存在函數foo,b.so中也存在函數foo,如果一個程序既依賴於a.so也依賴於b.so,並且a.so先被動態鏈接器加載,則最終程序中調用的foo程序就是a.so中的。