博客專欄

        EEPW首頁 > 博客 > 靜態鏈接全過程

        靜態鏈接全過程

        發布人:電子禪石 時間:2021-04-13 來源:工程師 發布文章
        寫在前面

        本文大部分內容翻譯自Library order in static linking

        靜態鏈接順序很重要

        在gcc編譯時,靜態鏈接庫鏈接順序會影響鏈接結果和行為。
        舉例如下:

        $ cat simplefunc.c
        int func(int i) {
            return i + 21;
        }
        
        $ cat simplemain.c
        int func(int);
        
        int main(int argc, const char* argv[]) {
            return func(argc);
        }
        
        $ gcc -c simplefunc.c
        $ gcc -c simplemain.c
        $ gcc simplefunc.o simplemain.o
        $ ./a.out ; echo $?
        22
        
        $ gcc simplemain.o simplefunc.o
        $ ./a.out ; echo $?
        22123456789101112131415161718192021

        對于object文件,鏈接順序是沒有影響的,gcc會將所有的目標文件加入到鏈接過程中。

        $ ar r libsimplefunc.a simplefunc.o
        $ ranlib libsimplefunc.a
        $ gcc  simplemain.o -L. -lsimplefunc
        $ ./a.out ; echo $?
        22
        
        $ gcc  -L. -lsimplefunc  simplemain.o
        simplemain.o: In function 'main':
        simplemain.c:(.text+0x15): undefined reference to 'func'
        collect2: ld returned 1 exit status12345678910

        當鏈接器遇到libsimplefunc.a時,它仍然沒有看到simplemain.o,這意味著func尚未出現在未定義的列表中。當鏈接器查看庫時,它會看到導出func的simplefunc.o。但由于它不需要func,因此該目標文件不包含在鏈接中。當鏈接器確實到達simplemain.o并且看到func確實是必需的時,它被添加到未定義的列表中(因為它不在導出的列表中)。鏈接器然后到達鏈接的末尾并且func仍未定義。

        請注意在先前的鏈接順序中不會發生這種情況 - 因為simplemain.o首先出現,func在鏈接器看到庫之前位于未定義的列表中,因此導出它的目標文件確實包含在內。

        從上面的例子中可以得出:靜態鏈接時,鏈接庫順序會影響鏈接結果。如果對象或庫AA需要來自庫BB的符號,則AA應該在鏈接器的命令行調用中位于庫BB 之前。
        下面一節,我們將從原理上來了解鏈接庫的順序是如何影響鏈接結果的!

        靜態鏈接過程

        程序靜態鏈接的過程如下,舉例說明(翻譯Library order in static linking的The linking process一節)

        $ gcc main.o -L/some/lib/dir -lfoo -lbar -lbaz1

        注:庫指library,對象指object,此處要注意區分。
        1 當鏈接時,鏈接器維護兩個列表:
        1.1 到目前為止遇到的所有目標文件(object)和庫(library)導出的符號列表,記做exports(導出的符號)
        1.2 遇到的目標文件(object)和庫(library)請求導入但尚未找到的未定義符號列表,即undefined reference,記做imports(待導入的符號)

        2 當鏈接器遇到新的目標文件(object)時,
        2.1 它導出的符號會被添加到上面提到的導出符號列表中。 如果任何符號位于未定義列表中,則會從那里刪除它,因為現在已找到它。 如果導出列表中已有任何符號,則會出現“多重定義”錯誤:兩個不同的對象導出相同的符號,鏈接器會報錯
        2.2 它導入的符號被添加到未定義符號列表中,除非它們可以在導出符號列表exports中找到。

        3 當鏈接器遇到新時,事情會更有趣。 鏈接器遍歷庫中的所有目標文件(object),庫是object打包成的一個整體。 對于每一個,它首先查看它導出的符號
        3.1 如果它導出的任何符號位于未定義列表中,則該對象將添加到鏈接中,并執行下一步。 否則,將跳過下一步。
        3.2 如果目標文件(object)已添加到鏈接中,則按上述方式對其進行處理 - 未定義和導出的符號將添加到符號表中。
        3.3 如果庫中的任何目標文件(object)已包含在鏈接中,則會再次重新掃描整個庫,因為這個目標文件可能依賴庫中的其他目標文件。

        鏈接器完成后,它會查看符號表。 如果任何符號保留在未定義的列表中,鏈接器將拋出“未定義的引用”錯誤。
        在鏈接器查看庫之后,它將不會再次查看它,即鏈接器只查看庫一次。 即使它導出一些后來的庫可能需要的符號。 鏈接器返回重新掃描對象的唯一時間它已經在單個庫中發生 - 如上所述,一旦某個庫中的對象被帶入鏈接,同一庫中的所有其他對象將被重新掃描。 傳遞給鏈接器的標志可以調整這個過程 - 再次,我們稍后會看到一些例子。
        另外,檢查庫時,如果不提供符號表所需的符號,則可以將其中的目標文件排除在鏈接之外。 這是靜態鏈接的一個非常重要的特性。 我之前提到過的C庫大量使用了這個功能,主要是將自身分解為每個函數的一個對象。 因此,例如,如果您的代碼使用的唯一C標準庫函數是strlen,則只有strlen.o將從libc.a進入鏈接 - 并且您的可執行文件將非常小。

        建議
        1. 在項目開發過層中盡量讓lib是垂直關系,避免循環依賴,越是底層的庫,越是往后面寫

        2. 若存在循環依賴的情況,可用以下方法解決

          Xlinker "-("-la -lb  -lc"-)" OR--start-group -la -lb lc -Wl,--end-group
          • 1

          • 2

          • 3

        參考文檔

        https://blog.csdn.net/caikunbob/article/details/85550000

        *博客內容為網友個人發布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。



        關鍵詞:

        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 浦江县| 丰原市| 包头市| 汝南县| 乌拉特前旗| 乌苏市| 栾川县| 邵东县| 错那县| 罗平县| 弥勒县| 类乌齐县| 宝应县| 濉溪县| 瑞安市| 花莲市| 浪卡子县| 陕西省| 东宁县| 甘德县| 福清市| 平安县| 红河县| 襄垣县| 惠东县| 白银市| 德保县| 策勒县| 仙居县| 武宣县| 神木县| 司法| 余江县| 桂平市| 女性| 延吉市| 得荣县| 西吉县| 霍城县| 宝鸡市| 贵港市|