深入理解計算及系統 Chapter7 學習筆記
Chapter7 連結
1.為了構造可執行檔案,連結器必須完成兩個主要任務:
(1)符號解析:目的是將每個符號引用正好和一個符號定義關聯起來
(2)重定位:編譯期和彙編器生成從地址0開始的程式碼和資料節,通過把每個符號定義與一個記憶體位置關聯起來,修改所有對這些符號的引用,使得它們指向這個記憶體位置
2.目標檔案(*.o):一個以檔案形式存放在磁碟中的位元組序列,是按照特定的目標檔案格式來組織的,Linux下為ELF,格式如下圖所示:
3.ELF可重定位目標檔案
如圖所示,夾在ELF頭和節頭部表之間的都是節(section),主要有:
(1).text:已編譯程式的機器程式碼(就是經過預處理->編譯->彙編生成的程式碼的機器碼)
(2).data:已初始化的全域性和靜態變數
(3).bss:未初始化的全域性和靜態變數。區分已初始化和未初始化主要是為了節省空間
(4).symtab:符號表,存放在程式中定義和引用的函式以及全域性變數的資訊
(5).rel.text/.rel.data:一個.text/.data中位置的列表,包含重定位資訊(因為生成的目標檔案是從地址0開始的,需要在執行時根據此列表修改為真實的地址)
3.符號表的型別
(1)全域性符號:由模組m定義並被模組m引用的符號
(2)外部符號:由其他模組定義並被模組m引用的符號
(3)區域性符號:只被模組m定義和引用的區域性符號(c中為static變數和函式,要與區域性變數
注意,.symtab中不包含對應於本地非靜態程式變數的任何符號(區域性變數的符號),這些符號在執行時在棧中被管理。有個例外,就是本地靜態變數不在棧中管理,而是在.data或.bss為每個定義分配空間,並在符號表中建立一個有唯一名字的本地連結器符號
4.符號表的條目(entry)
即.symtab節在目標檔案中是如何被組織的,下面用程式碼來說明其格式:
typedef struct { int name; /*位元組偏移,指向符號的字串名字*/ char type:4; /*型別(資料或函式)*/ charbinding:4; /*本地或者全域性*/ char reserved; short secotion; /*被分配到目標檔案的哪個節中*/ long value; /*距定義目標的節的起始位置的偏移*/ long size; }
用GNU READELF程式讀取某個目標檔案,結果如下:
Num: Value Size Type Bind Vis Ndx Name
8: 0000000000000000 24 FUNC GLOBAL DEFAULT 1 main
9: 0000000000000000 8 OBJECT GLOBAL DEFAULT 3 array
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND sum
5.符號解析
連結器解析符號引用的方法是將對每個引用與它輸入的可重定位目標檔案的符號表中的一個確定的符號定義關聯起來:
(1)區域性符號:對於和引用定義在相同模組中的區域性符號的引用,符號解析簡單明瞭,編譯器只允許每個模組中每個區域性符號有一個定義(如main、array)
(2)全域性符號:當編譯器遇到一個不是在當前模組中定義的符號(變數或函式名)時,會假設該符號在其他模組中定義,生成一個連結器符號表條目,並把它交給連結器處理(如sum)