連接器

連接是一個頗有意思的話題,動態連接,靜態連接以及在各類變成語言中的應用。想一想都是一個頗有意思的。

ELF 文件格式

在計算機系統中,要想執行一個文件,那麼這個文件就須要有特定的符合當前系統的執行文件格式。在windows 系統上的文件格式標準是PE(ortable Executable),而Unix和類Unix系統上所使用的是ELF(Executable and Linkable Format )格式。固然仍是有其餘文件格式的。在linux系統中,ELF文件格式是標準。
每一個ELF文件是由一個ELF 頭, 節頭部表,以及夾在二者之間的節組成的。linux

ELF文件的格式

目標文件參與了程序連接(用來構建一個程序)和程序的執行(運行一個程序),從便利和效率上看,文件格式爲文件的內容提供了不一樣視角。下圖展現了一個目標文件的組織結構。
windows

目標文件

  • 可重定位的文件:包含代碼和數據,用於同其餘文件連接起來的造成可執行的文件或者可共享的文件。
  • 可執行文件:包含代碼和數據,能夠直接用於加載器加載到存儲器中並執行。
  • 可共享文件:包含代碼和數據,可在兩個上下文域中進行連接。首先,連接編輯器能夠用它與可重定位文件或可共享文件去建立另一個目標文件;其次,動態連接器將它與可執行文件和其餘共享文件去建立一個進程鏡像。

ELF 文件頭中的信息

目標文件的節

目標文件定義的節,以下圖所示:
編輯器

節的內容

一下三個是這篇博文中要說的:函數

  • .data : 已經初始化的全局C變量,局部C變量運行時保存在棧。
  • .bss : 未初始化的全局C變量,僅做佔位符之用。
  • .symtab : 存放在程序中定義和引用函數和全局變量,和編譯器的符號表不一樣,.symtab不包含局部變量的條目。

符號和符號表

每一個可重定位的目標文件 obj 都有一個符號表,符號表中的符號分爲三種類型:spa

  1. 由目標文件 obj 定義的而且能被其餘目標文件所引用的全局符號,全局連接器符號對應於非靜態的C函數以及被定義爲不帶 C static 屬性的全局變量。
  2. 由其餘目標文件將定義的被目標文件 obj 引用的,稱爲外部符號,對應於定義在其餘模塊的C函數和變量。
  3. 只被目標文件 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進程

相關文章
相關標籤/搜索