《深入理解計算機系統》筆記---(1)鏈接

        鏈接器在軟件開發中扮演一個重要的角色,因爲它使得分離編譯成爲可能。我們不用將一個大型的應用程序組織成爲一個巨大的源文件,而是可以把他分解爲更小的,更好管理的模塊,可以獨立的修改和編譯這些模塊。當我們改變這些模塊的一個時,只需簡單的重新編譯它,並重新鏈接應用,而不必重新編譯其他文件。    


要想了解鏈接的機制,需要知道一個程序從編輯完代碼到運行的過程(C語言程序)。

以下方的c程序爲例。經過4個階段生成可執行的二進制文件。

//hello.c
    #include<stdio.h>
    int main(){
         printf("%s","hello.word");
         return 0;
    }

       

  • 預處理階段(cpp) 

             預處理器(cpp)根據以字符#開頭的命令,修改原始的C程序。比如hello.c中的 #include<stdio.h> 命令告訴預處理器讀取系統頭文件stdio.h的內容(只有stdio.h頭文件裏的內容,不包括stdio.c中的定義部分),並把它直接插入到程序文本中。結果就得到了另一個C程序,通常是以.i作爲文件擴展名。

  • 編譯階段(ccl)

             編譯器(ccl) 將文本文件hello.i翻譯成由彙編語言組成的文本文件hello.s.

  • 彙編階段(as)

             彙編器(as)將hello.s翻譯成機器語言指令,將這些指令打包成一中叫做可重定位目標程序文件的格式,並將結果保存到目標文件hello.o中。

  • 鏈接階段(ld)

            鏈接hello.c中調用的函數,比如例子中的printf函數。 printf函數存在一個名爲printf.o的單獨的預編譯好了的目標文件中,而這個文件必須以某種方式合併到我們的heelo.o程序中。鏈接器就是負責處理這種合併。 結果得到可執行目標文件hello,它可以被加載到內存中,由系統執行。


     以上就是背景知識了。下面介紹鏈接器是如何工作的。O(∩_∩)O


 一.靜態鏈接

       靜態鏈接器的功能就是以一組可重定位目標文件爲輸入 生成一個可以加載的可執行目標文件

       目標文件定義:

                   1.可重定位目標文件---包含二進制代碼和數據,可以在編譯時與其他可重定位目標文件合併起來。

                   2.共享目標文件---一種特殊類型的1。

                   3.可執行目標文件---可直接執行的。

                   由1+【2】--》3;

爲了瞭解鏈接器是如何把一組可重定位目標文件鏈接在一起,我們先了解下它的格式。

   可重定位目標文件格式:


ELF頭 
.text已編譯程序的機器代碼
.rodata    只讀數據,比如printf語句中的格式串和開關語句的跳轉表
.data已初始化的全局和靜態C變量
.bss未初始化的全局和靜態C變量
.symtab一個符號表,存放程序中定義和引用的函數和全局變量的信息。
.rel.text一個.text節中位置的列表,鏈接時就是修改這個位置
.rel.data需要重定位的全局變量信息
.debug調試符號表
.line原始C程序中行號和.text節中機器指令之間的映射。
.startab一個字符串表
節頭部表 


       爲了構造可執行文件,鏈接器必須完成兩個主要任務:

                  1.符號解析---將符號引用和符號定義聯繫起來。

                  2.重定位---合併各節,然後修改符號引用指向的地址。



         符號解析

              符號-----每個符號對應於一個函數,一個全局變量或是一個靜態變量。

             在建立符號聯繫時,鏈接器要確保它們擁有唯一的名字,對於局部變量很簡答。但是對於全局變量就有點棘手。爲此linux鏈接器使用下面的規則:

                (強符號:已定義的全局變量,弱符號:只聲明未定義的全局變量。)

                     規則1:不允許有多個同名強符號。

                     規則2:一強多弱 選強

                     規則3:多弱,任意選

             ----與靜態庫鏈接

               

          重定位 

               將符號解析完,接下來就是重定位了

                    步驟1.將所有相同類型的節合併爲同一類型的新的聚合節。

                   步驟2.修改代碼節和數據節中對每個符號的引用,使得它們指向正確的運行時地址。----》這一步依賴於重定位條目

                  重定位條目:彙編時生成的描述如何修改引用的文件,代碼重定位條目放在.rel.text中,數據重定位條目放在.rel.data中。

                   重定位包括 相對引用,和絕對引用。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章