ELF、編譯、鏈接、裝載和庫

ELF文件分類

1、可重定位文件(Relocatable File)

與其他目標文件鏈接來創建可執行文件或者共享目標文件的代碼和數據。

2、可執行文件(Executable File

此文件規定了exec()如何創建一個程序的進程映像。

3、共享目標文件(SharedObject File

首先鏈接編輯器可以將它和其它可重定位文件和共享目標文件一起處理,生成另一個共享目標文件。其次,動態鏈接器可能將它與某個可執行文件以及其他共享目標一起組合,創建進程映像。

4、核心轉儲文件(CoreDump File

包含進程意外終止時的地址空間內容及其他信息。


ELF文件內容






符號表:

記錄目標文件中所有符號和地址。符號代表函數和變量,鏈接過程基於符號進行

 

符號的地址(nm)

可執行文件中,符號的地址對應程序運行時變量或函數的虛擬地址。

共享目標文件中,符號的地址對應程序運行時變量或函數的虛擬地址和裝載地址的偏移

可重定位目標文件中,符號地址是不確定的

 

符號的類別

強符號:函數和初始化了的全局變量

弱符號:未初始化的全局變量爲弱符號。

 

符號的引用

強引用:符號在鏈接成可執行文件時,必須被正確決議。

弱引用:鏈接時可有可無的符號;有則引用,沒有也不報錯。

 

重定位表:

記錄用於修改相應段的內容的信息。


重定位

普通重定位:鏈接時候重定位。

動態重定位:加載、運行時重定位,一般有程序啓動時動態加載符號來重定位,和函數符號被調用時候動態加載。

  

爲什麼需要重定位

使用外部變量和函數地址不確定,需要在鏈接時確定。

動態庫需要生成地址無關代碼,需要進行指令修正

 


程序編譯


預編譯:處理宏定義、條件編譯、頭文件包含等

編譯:把預處理完的文件進行一系列的詞法分析、語法分析、語義分析及優化後生產相應的彙編代碼文件。

彙編:將彙編代碼轉變成機器可以執行的指令。

鏈接:將程序鏈接成可執行文件或動態庫。


MakeFile編譯相關參數

-g選項新添加的是調試信息),被相關調試工具(如gdb)使用,可以被strip掉。

-rdynamic選項新添加的是動態連接符號信息,用於動態連接功能,比如dlopen()系列函數、backtrace()系列函數使用,不能被strip掉,即強制strip將導致程序無法執行。-rdynamic選項不產生任何調試信息,因此在一般情況下,新增的附加信息比-g選項要少得多。

-C指定編譯目錄

.PHONY:



鏈接器的工作

空間與地址分配:

掃描輸入的目標文件,搜索其中的符號定義和符號引用。計算出輸出文件中每個段合併後的長度和位置,並建立映射關係。

 

符號解析與重定向:

A.符號解析,找出外部符號在哪定義。如果外部符號在一個靜態庫中定義,則直接將對應的定義代碼複製到最終生成的目標文件中

B.符號重定位。編譯器在生成目標文件時,通常使用從零開始的相對地址,而在鏈接過程中,鏈接器從一個指定的地址開始,根據輸入目標文件的順序,以段(segment)爲單位將他們拼接起來。其中每個段可以包括很多哥節(section)。除了目標文件的拼裝, 重定位過程中還完成了下面兩個任務:一是生成最終的符號表,二是對代碼段(.text)中的某些位置進行修改,要修改的位置由編譯器生成的重定位表指出。

指定輸出文件各個段虛擬地址、段的名字、段存放順序等:

指定程序入口、動態鏈接器名字、是否靜態鏈接程序等選項:

 

 


靜態連接、動態連接

靜態鏈接:

由鏈接器在鏈接時將庫的內容加入到可執行程序中。發生在程序編譯期間

優點:可執行文件能不依賴其他庫運行。

缺點:生成的可執行文件太大,需要更多的系統資源,在裝入內存時也會消耗更多的時間。

動態鏈接:

可執行文件裝載時或運行時,由操作系統的裝載程序加載庫。發生在程序裝載期間

優點:無需重新編譯可更新程序,節省內存和磁盤空間。

缺點:可執行程序依賴分別存儲的庫文件才能正確執行。

 


 

程序的裝載

Linux內核裝載ELF過程簡介

1、bash調用fork創建進程,新的進程調用execve系統調用執行指定的ELF文件。

2、檢測ELF可執行文件的有效性。

3、設置動態鏈接器路徑。

4、根據ELF可執行程序頭表進行映射。

5、初始化ELF進程環境

6、將系統調用的返回地址修改成ELF可執行文件的入口點。對於靜態鏈接的ELF可執行文件,就是ELF文件頭中的ENTRY所指地址;對於動態鏈接的ELF可執行文件,程序入口點是動態鏈接器。

 


動態庫的查找過程

1、可執行文件中的.dynamic指定的路徑。

2、ld.so.conf文件中指定的目錄。

3、環境變量LD_LIBRARY_PATH指定的目錄。




動態加載庫

運行時加載,讓程序自己在運行時控制加載指定的模塊,並且可在不需要該模塊的時候卸載

相關API

  • dlopen打開動態庫,將其加載到進程的地址空間
  • dlsym查找動態庫中指定的符號
  • dlclose將加載的動態庫卸載
  • dlerror判斷對動態庫的其他API操作是否成功。

 

 
輔助工具

  • ar:創建靜態庫。插入、刪除、列出和提取成員。
  • strings:列出一個目標文件中所有可打印的字符串。
  • nm:列出一個目標文件中的符號表中定義的符號(有可能被strip掉)。
  • size: 列出目標文件中節的名字和大小。
  • readelf:顯示一個目標文件的完整結構。
  • objdump:顯示一個目標文件中所有的信息,反編譯.text節。
  • ldd:列出一個可執行文件在運行時所需的共享庫。
  • xxd:以十六進制打印文件的所有內容。
  • Ldd (ldconfig -p |grep mysql)