《深刻理解計算機系統》第七章學習筆記

第七章  連接數據結構

姓名:王瑋怡  學號:20135116函數

1、關於連接工具

一、含義spa

  連接是將各類代碼和數據部分收集起來並組合成爲一個單一文件的過程,這個文件可被加載(或被拷貝)到存儲器並執行。連接是由連接器程序自動執行的。操作系統

二、執行時間命令行

  • 編譯時
  • 加載時
  • 運行時

 

2、編譯器驅動程序翻譯

  驅動編譯器:表明用戶在須要時調用語言預處理器、編譯器、彙編器和連接器。debug

 

3、靜態連接3d

一、靜態連接器指針

  Unix的靜態連接器(static linker)ld,以一組可重位目標文件和命令行參數做爲輸入,生成一個徹底連接的能夠加載和運行的可執行目標文件做爲輸出。輸入的可重定位目標文件由各類不一樣的代碼和數據節(section)組成。指令在一個節中,初始化的全局變量在另外一個節中,而未初始化的變量又在另一個節中。

二、連接器的兩個任務

  • 符號解析:將每一個符號引用恰好與一個符號定義聯繫起來
  • 重定位:連接器將每個符號定義與一個存儲器位置鏈接起來,而後修改這些符號引用,使得他們指向這個存儲器位置,從而重定位

 

4、目標文件

目標文件的三種形式

  • 可重定位目標文件(編譯器和彙編器可生成)
  • 可執行目標文件(連接器可生成)
  • 共享目標文件(編譯器和彙編器可生成)

 

5、可重定位目標文件

 

  • .text:已編譯程序的機器代碼。
  • .rodata:只讀數據,好比printf語句中的格式串和開關語句的跳轉表。
  • .data:已初始化的全局C變量。
  • .bss:未初始化的全局C變量。在目標文件中這個節不佔據實際的空間,它僅僅是一個佔位符。
  • .symtab:一個符號表,它存放在程序中定義和引用的函數和全局變量的信息。
  • .rel.text:一個.text節中位置的列表,當連接器把這個目標文件和其餘文件結合時,須要修改這些位置。
  • .rel.data:被模塊引用或定義的任何全局變量的重定位信息。
  • .debug:一個調試符號表,其條目是程序中定義的局部變量和類型定義,程序中定義和引用的全局變量,以及原始的C源文件。只有以-g選項調用編譯驅動程序時纔會獲得這張表。
  • .line:原始C源程序中的行號和.text節中機器指令之間的映射。
  • .strtab:一個字符串表,其內容包括:.symtab和.debug節中的符號表,以及節頭部中的節名字。字符串表就是以null結尾的字符串序列。

 

6、符號和符號表

一、連接器的上下文中,有三種不一樣的符號:

  • 由m定義並能被其餘模塊引用的全局符號。全局連接器符號對應於非靜態的C函數以及被定義爲不帶C static屬性的全局變量。
  • 由其餘模塊定義並被模塊m引用的全局符號。這些符號稱爲外部符號(external),對應於定義在其餘模塊中的C函數和變量。
  • 只被模塊m定義和引用的本地符號。有的本地連接器符號對應於帶static屬性的C函數和全局變量。

二、符號表

 

 

7、符號解析

一、連接器如何解析多重定義的全局符號

(1)強符號:函數和已初始化的全局變量

(2)弱符號:未初始化的全局變量

(3)處理規則:

  • 不容許有多個強符號。
  • 若是有一個強符號和多個弱符號,那麼選擇強符號。
  • 若是有多個弱符號,那麼從這些弱符號中任意選擇一個。

二、與靜態庫連接

  全部的編譯系統都提供一種機制,將全部相關的目標模塊打包成爲一個單獨的文件,稱爲靜態庫,能夠用作連接器的輸入。其中,在Linux下是存檔文件,Windows下是lib。

  在符號解析的階段,連接器從左到右按照它們在編譯器驅動程序命令行上出現的相同順序來掃描可重定位目標文件和存檔文件。(驅動程序自動將命令行中全部的.c文件翻譯成.o文件),在此次掃描中,連接器維持一個可重定位目標文件的集合E(這個集合中的文件會被合併起來造成可執行文件),一個未解析的符號(即引用了可是還沒有定義的符號)集合U,以及一個在前面輸入文件中已定義的符號集D,初始時,E、U和D都是空的。

 

7、重定位

一、重定位的兩個步驟

(1)重定位節和符號定義

  • 連接器將全部相同類型的節合併爲同一類型的新的聚合節,將運行時存儲器地址賦給新的聚合節,賦給輸入模塊定義的每一個節,以及賦給輸入模塊定義的每一個符號。
  • 完成這一步後,程序中的每一個指令和全局變量都有惟一的運行時存儲器地址了。

 

(2)重定義節中的符號引用

  • 連接器修改代碼節和數據節中對每一個符號的引用,使得它們指向正確的運行時地址。
  • 連接器依賴於稱爲重定位條目的可重定位目標模塊中的數據結構。

二、重定位條目

(1)不管什麼時候彙編器遇到對最終位置位置的目標引用,它就會生成一個重定位條目,告訴連接器在將目標文件合併成可執行文件時如何修改這個引用。

(2)代碼的重定位條目放在.rel.text中。

(3)已初始化的數據的重定位條目放在.rel.data中。

(4)ELF定義了11種不一樣的重定位類型。兩種最基本的重定位類型:

  • *R_386_PC32 重定位一個使用32位PC相對地址的引用。
  • *R_386_32 重定位一個使用32位絕對地址的引用。

  • offset:須要被修改的引用的節偏移
  • symbol:標識被修改的引用應該指向的符號
  • type:告知連接器如何修改新的引用

三、重定位符號引用

(1)相對引用

(2)絕對引用

 

8、可執行目標文件

 

 

9、加載可執行目標文件

  加載器將可執行目標文件中的執行代碼和數據從磁盤拷貝到存儲器中,而後經過跳轉到程序的第一條指令或入口點來運行該程序。這個將程序拷貝到存儲器並運行的過程叫作加載。

  • 在32位Linux系統中,代碼段老是從地址0x08048000處開始。
  • 數據段是在接下來的下一個4KB對齊的地址處。
  • 運行時堆在讀/寫段以後接下來的第一個4KB對齊的地址處,並經過調用malloc庫往上增加。
  • 有一個段是爲共享庫保留的。
  • 用戶棧老是最大的合法用戶地址開始,向下增加的(向低存儲器地址方向增加)。從棧的上部開始的段是爲操做系統駐留存儲器的部分(也就是內核)的代碼和數據保留的。

 

10、動態連接共享庫

一、靜態庫的缺點:

  • 靜態庫在更新時,使用該庫的程序須要與更新的庫進行從新連接。
  • 因爲使用靜態庫的程序在連接時都會拷貝靜態庫裏被應用程序引用的目標模塊,像printf和scanf這樣的函數的代碼在運行時都會被複制到每一個運行進程的文本段中,這形成了冗餘,浪費了稀缺的存儲器資源。

二、共享庫

  • 共享庫是一個目標模塊,在運行時,能夠加載到任意的存儲器地址,並和一個在存儲器中的程序連接起來。這個過程稱爲動態連接,是由一個叫作動態連接器的程序來執行的。
  • 共享庫也稱爲共享目標,在Unix系統中一般用.so後綴來表示。微軟的操做系統大量地利用了共享庫,它們稱爲DLL(動態連接庫)。
  • 共享庫是以兩種不一樣的方式來「共享」的(在Windows中分別稱爲「隱式連接」和「顯示連接」)。
  •       首先,在任何給定的文件系統中,對於一個庫只有一個.so文件。全部引用該庫的可執行目標文件共享這個.so文件中的代碼和數據,而不是像靜態庫的內容那樣被拷貝和嵌入引用它們的可執行的文件中。其次,在存儲器中,一個共享庫的.text節 一個副本能夠被不一樣的正在運行的進程共享。

 

11、從應用程序中加載和連接共享庫

#include<dlfcn.h>

void *dlopen(const char *filename,int flag); //返回:若成功則爲指向句柄的指針,若出錯則爲NULL

void *dlsym(void *handle,char *symbol); //返回:若成功則爲指向符號的指針,若出錯則爲NULL

int dlclose(void *handle); //返回:若成功則爲0,若出錯則爲-1

const char *dlerror(void);

 

12、與位置無關的代碼(PIC)

  • 編譯庫代碼,使得不須要連接器修改庫代碼就能夠在任何地址加載和執行這些代碼。
  • 用戶對GCC使用-fPIC選項指示GNU生成PIC代碼

 

十3、處理目標文件的工具

 

 

十4、總結

  連接能夠在編譯時由靜態編譯器來完成,也能夠在加載時和運行時由動態連接器來完成。連接器處理成爲目標文件的二進制文件,它有三種不一樣的形式:可重定位的、可執行的和共享的。可重定位的目標文件由靜態連接器合併成爲一個可執行的目標文件,它能夠加載到存儲器中並執行。共享目標文件(共享庫)是在運行時由動態連接器連接和加載的,或者隱含地在調用程序被加載和開始執行時,或者根據須要在程序調用dlopen庫的函數時。

相關文章
相關標籤/搜索