連接是一個頗有意思的話題,動態連接,靜態連接以及在各類變成語言中的應用。想一想都是一個頗有意思的。
在計算機系統中,要想執行一個文件,那麼這個文件就須要有特定的符合當前系統的執行文件格式。在windows
系統上的文件格式標準是PE
(ortable Executable),而Unix
和類Unix
系統上所使用的是ELF
(Executable and Linkable Format )格式。固然仍是有其餘文件格式的。在linux系統中,ELF
文件格式是標準。
每一個ELF
文件是由一個ELF
頭, 節頭部表,以及夾在二者之間的節組成的。linux
目標文件參與了程序連接(用來構建一個程序)和程序的執行(運行一個程序),從便利和效率上看,文件格式爲文件的內容提供了不一樣視角。下圖展現了一個目標文件的組織結構。
windows
目標文件定義的節,以下圖所示:
編輯器
一下三個是這篇博文中要說的:函數
每一個可重定位的目標文件 obj
都有一個符號表,符號表中的符號分爲三種類型:spa
obj
定義的而且能被其餘目標文件所引用的全局符號,全局連接器符號對應於非靜態的C函數以及被定義爲不帶 C static
屬性的全局變量。obj
引用的,稱爲外部符號,對應於定義在其餘模塊的C函數和變量。obj
定義和引用的本地符號。對應於帶 static
屬性的 C函數和全局變量。帶 static
的本地變量不在棧中管理,而是在 .data
和 .bss
爲其分配空間。在編譯的過程當中,編譯器想彙編器輸出的每一個全局符號,彙編器將它們隱含地包含在可重定位目標文件的符號表裏。已初始化的全局變量爲強符號,未初始化的全局變量爲弱符號。Unix用如下規則來處理多重定義的符號。命令行
所謂的靜態庫,就是包裝了不少函數的一個集合,這個集合中包含着關於相關庫的路徑,而後在編譯程序的時候,連接器僅會將靜態庫中被引用的目標模塊拷貝。靜態庫會以歸檔的特殊文件格式存儲。
在符號解析的階段,連接器從左到右來掃描可重定位目標文件和存檔文件,而且維持一個可重定位目標文件集合E(固然了它們是用來造成可執行目標文件的),未解析的符號集合U,已定義的符號集合。code
- 對於命令行上輸入的每一個文件f,若是是一個目標文件,那麼就將它放入E中,修改U和D來反應f中的符號定義和引用,並繼續下一個文件。
- 若是輸入的這個文件是歸檔文件,那麼連接器就嘗試匹配U中未解析的符號和由這個歸檔文件的成員所定義的符號,若是該成員定義的符號與U中的符號相匹配,那麼就將改爲員添加到E中,並對其餘成員進行類似的操做,知道成員結束。而後將未包含在E中的模塊丟棄掉。
- 當過程之後,若是U是非空的,那麼此時將會報錯。
在命令行中輸入的庫若是有相互依賴的關係,那麼仍是必需要分清楚前後關係的。好比說:orm
gcc foo.c lib1.a lib2.a
在運行的時候,若是lib2.a
依賴lib1.a
,那麼在運行的時候,解析到lib2.a
的時候,由於在解析lib1.a
的時候未經引用的符號被簡單的丟棄了,因此在解析lib2.a
完了之後,U 的集合並無爲空,因此會報錯。
ELF-WIKI進程