鏈結 裝載與共享庫

2021-09-10 01:24:03 字數 2574 閱讀 2851

由下圖編譯的過程可以看出,源程式經過預處理,編譯,彙編等步驟形成可重定位目標檔案,再由多個可重定位目標檔案鏈結成可執行目標檔案。

每個步驟做的事情:

(1)預處理(預編譯),處理源**中以「#」開頭的預處理指令,如#include,#define。具體工作如下:

(2)編譯過程是將預處理完成的文字進行詞法分析,語法分析,語義分析,優化後生成相應的匯程式設計序。

(3)彙編器將彙編**轉換成二進位制機器指令,彙編語句與機器指令一一對應。

(4)鏈結是將各種**和資料片段收集並組合成乙個單一可執行目標檔案的過程,可執行檔案可被載入到記憶體中直接執行。鏈結可以執行於編譯時(源**被翻譯成機器碼)、載入時(程式被載入器載入到記憶體並執行)、執行時(由應用程式來執行)

鏈結的好處在於①模組化(可構建共享庫)②效率高,時間上可分開編譯(只需要重新編譯修改過的檔案即可),空間上可執行檔案執行時記憶體只需包含所呼叫函式所在目標檔案的**(是以目標檔案為基本單位)。

現代的鏈結器一般分成兩步,第一步空間和位址分配,分配虛擬位址空間,掃瞄所有的可重定位目標檔案,獲取各個段的長度、屬性、位置,並將符號表中所有符號定義和符號引用收集起來統一放到全域性符號表中。linux下,elf可執行檔案預設從位址0x8048000開始分配,確定好每個符號的位址,後面的步驟就容易了。

第二步符號解析和重定位,具體如下,

確定符號引用關係

合併相關.o檔案

確定每個符號的位址

在指令中填入新位址

同時也可以分成兩個步驟,符號解析(步驟1,即把每個符號引用和乙個符號定義關聯起來)和重定位(步驟2\3\4)。

符號解析:聯結器解析符號引用的方法就是將每個引用與他輸入的可重定位目標檔案的符號表中的符號定義關聯起來。每個可重定位目標模組m都有乙個符號表,其包含了m定義和引用的符號資訊,符號表中有三種不同的符號:

由模組m定義並能被其他模組引用的全域性符號(非靜態函式和非靜態全域性變數)

由其他模組定義並被模組m引用的全域性符號,也成為外部符號(其他模組中定義的非靜態函式和非靜態全域性變數)

只能被模組m定義和引用的區域性符號(靜態函式和靜態變數)

注:函式內部區域性變數不在符號表中,在函式棧幀中

對於多重定義的全域性符號,在編譯時編譯器會向彙編器輸出每個全域性符號,分為強符號(函式和已初始化的全域性變數)和弱符號(未初始化的全域性變數),彙編器則會把這個資訊放在可重定位檔案的符號表中,鏈結器根據以下規則處理多重定義的符號名:

不允許有多個同名的強符號

如果有乙個強符號和多個弱符號同名,那麼選擇強符號

如果由多個弱符號同名,那麼從這些弱符號中任意選擇乙個。

重定位:合併輸入模組並為每個符號分配執行時位址。有兩步驟組成:①重定位節和符號定義,將所有相同型別的節合併為同一型別的新的聚合節②重定位節中的符號引用,鏈結器修改**節和資料節中對每個符號的引用,使其指向正確的執行位址(依賴於重定位條目)

圖中是乙個elf可重定位目標檔案格式。

首先,為什麼要把elf中指令與資料分開放?原因是

(1)許可權問題,資料大部分是可讀可寫的,**指令只是可讀的

(2)區域性性問題,指令段與**段的分離,有利於提高程式的區域性性,現代cpu的快取大部分都分為指令快取和資料快取

(3)當系統執行多個該程式副本時,記憶體中只需儲存乙份該程式的**段,可通過記憶體對映實現,而資料段是每個程序私有且不同的

接下來對一些elf檔案中的關鍵節(段)做具體分析。

elf檔案頭描述了整個elf檔案的基本屬性,主要包含系統字大小,位元組順序,elf頭大小、目標檔案型別(如可重定位、可執行或共享的)、機器型別、節頭部表的檔案偏移、以及節頭部表中條目的大小和數量。

節頭部表描述不同節的位置和大小。比如每個段的段名,段長度,在檔案中的偏移,讀寫許可權以及段的其他屬性等

段頭部表(程式頭部表)是將連續的檔案節對映到執行時記憶體段,即節與段的對映幷包含每個段的資訊。可執行目標檔案的elf頭和可重定位檔案的elf頭類似,還包括程式的入口點(即當程式執行時候執行的第一條指令的位址),其他.text節等已重定位到他們最終執行時記憶體位址。

所有的相關目標模組(.o檔案)打包成乙個單獨的檔案即為靜態庫(.a檔案)。靜態庫的缺點:①靜態庫函式被包含在每個程序的**段,造成主存的浪費②靜態庫函式被合併在可執行目標中,磁碟中空間浪費③更新困難

所有引用共享庫的可執行目標檔案共享這個.so檔案中的**和資料,而不是像靜態庫中被複製到引用他們的可執行檔案中,除此之外,在記憶體中乙個共享庫的.text節的乙個副本可以被不同的正在執行的程序共享。

linux > gcc -shared -fpic -o lib.so add1.c add2.c

linux > gcc -o a.out main.c ./lib.so

中引用add1.c或者add2.c

鏈結裝載庫

一般應用程式記憶體空間有如下區域 棧棧儲存了乙個函式呼叫所需要的維護資訊,常被稱為堆疊幀 stack frame 或活動記錄 activate record 一般包含以下幾方面 堆堆分配演算法 段錯誤 segment fault 或 非法操作,該記憶體位址不能 read write 典型的非法指標解...

靜態庫與共享庫

編寫使用靜態庫 靜態庫 共享庫 是包含了目標檔案的檔案,這些目標檔案被稱為模板或成員,是可以重用的預編譯 它們以特殊的格式和乙個表或者對映儲存在一起,這個表或者對映將符號名和儲存該符號的成員名字鏈結起來。對映加速了編譯和鏈結過程,靜態庫一般以副檔名.a 代表存檔檔案,archive 命名。為了使用庫...

靜態庫與共享庫

1.靜態庫 所有編譯器都提供一種機制,將所有相關的目標檔案模組兒打包成乙個單獨的檔案,被稱為靜態庫。它可以用作來聯結器的輸入,當聯結器構造乙個輸入的可執行檔案時,它只拷貝靜態庫裡面被應用程式引用的模組兒。在unix 系統中,靜態庫以存檔 一組連線起來的可重定位目標檔案的集合,有乙個頭部來描述成員目標...