《程序員的自我修養》筆記--目標文件詳解

從源程序到目標文件的生成過程

 

最簡單的編譯命令是gcc helloworld.c,它包含了以下幾個步驟:

預處理、編譯、彙編、鏈接,下面分別簡介。


預處理:處理#define宏定義、#if #ifdef等條件編譯指令、#include預編譯指令,刪除註釋,添加行號和文件名標識,保留所有的#pargma編譯器指令,經過預編譯後的文件爲.i文件。預編譯命令爲:gcc -E hello.c -o hello.i或者cpp hello.c > hello.i


編譯:把預處理完得文件進行一系列的詞法分析、語法分析、語意分析及優化後產生的彙編代碼文件。編譯命令爲gcc -S hello.i -o hello.s。現在版本的gcc把預編譯和編譯兩個步驟合併成一個步驟,使用ccl程序來完成,命令爲ccl hello.c。

也可以使用gcc -S hello.c -o hello.s直接從.c文件生成.s彙編文件。


彙編:將彙編代碼轉變成機器可以執行的指令,每一條彙編語句幾乎都對應一條機器指令。命令爲:as hello.s -o hello.o或者gcc -c hello.s -o hello.o或者我們最熟悉的gcc -c hello.c -o hello.o


鏈接:當我們的程序模塊調用a另一個模塊中b的函數(foo())或變量時,在編譯的階段編譯器並不知道函數foo的地址,所以暫時把調用foo的指令的目標地址擱置,等待最後鏈接的時候由連接器去將這些指令的目標地址修正。把目標文件和庫一起鏈接成可執行文件。最常見的庫時運行時庫。


 

目標文件中的格式

 

目標文件就是源代碼編譯後但未進行鏈接的那些中間文件,它和可執行文件的內容和結構其實很相似,所以一般和可執行文件採用同一種格式存儲,那就是ELF格式。與ELF格式相對應的是Windows平臺下的PE格式,它們都是COFF格式的變種。不光是可執行文件和目標文件按照ELF格式存儲,動態鏈接庫(.so)和靜態鏈接庫(.a)都按照ELF格式存儲。


ELF格式的文件可分爲以下4類:

1 可重定位文件(Relocatable File):這類文件包含了代碼和數據,可以被用來鏈接成可執行文件或共享目標文件,靜態鏈接庫也可以歸爲這一類,代表是Linux的.o文件


2 可執行文件(Executable File):這類文件包含了可以直接執行的程序,它的代表就是ELF可執行文件,它們一般都沒有擴展名,如/bin/bash文件


3 共享目標文件(Shared Object File):這種文件包含了代碼和數據,可以在以下兩種情況下使用。一種是鏈接器可以使用這種文件跟其他的可重定位文件和共享目標文件鏈接,產生新的目標文件。第二種是動態鏈接器可以將這幾個共享目標文件與可執行文件結合,作爲進程影響的一部分來運行。


4 核心轉儲文件(Core Dump File):當進程意外終止時,系統可以將該進程的地址空間的內容以終止時的一些其他信息轉儲帶核心轉儲文件。

上面幾種文件在file命令下會顯示出相應的類型。

 

 

目標文件裏有什麼:

 

 

ELF文件最重要的結構是ELF文件頭(ELF Header):ELF文件頭裏定義了ELF魔數、文件機器字節長度、數據存儲方式、版本、運行平臺、ABI版本、ELF重定位類型、硬件平臺、硬件平臺版本、入口地址、程序頭入口和長度、段表的位置和長度及段得數量等。


三個最重要的段:代碼段(.text)、數據段(.data)和只讀數據段(.rodata)、BSS段(.bss)

可以用objdump -s -x -d hello.o來分析各個段得內容,-d表示反彙編,-s表示把各段內容用16進制打印,-x表示詳細數據。


 顧名思義,.text段主要存放可執行的代碼數據;.data段保存的是已經初始化了的全局變量和靜態變量;.rodata段保存只讀數據,一般是程序裏的只讀變量(如const修飾的變量)和字符串常量;bss保存未初始化的全局變量和靜態變量。


除此之外,還有一些常見的段,如

.comment段存放編譯器版本信息,比如字符串“GCC:(GNU)4.2.0”

.dubug段存放調試信息

.dynamic存放動態鏈接信息

.hash段存放符號哈希表

.line段存放調試時的行號表

.note段存放額外的編譯器信息

.strtab段存放字符串表,用於存儲ELF文件中用到的各種字符串,比如符號的名字

.shstrtab段存放段表中用到的字符串,最常見的就是段名

.symtab段是符號表,通過符號表就能知道這個符號在哪個段,以及在這個段的具體位置,還有這個符號在字符串表中的位置

Section Table(段表)也是其中一個段,它保存了各個段的信息,如段名、段的長度、在文件中的偏移、讀寫權限及段的其他屬性

.rel.text段是一個重定位表,正如前面所說的,鏈接器在處理目標文件時,需要對目標文件的某些部位進行重定位,即代碼段和數據段中那些對絕對地址的引用的位置,這些重定位的信息都記錄在ELF文件的重定位表裏


通過ELF文件頭的信息可以找到段表的位置,從而找到各個段得位置和信息。


可以用readelf -S hello.o命令詳細查看ELF文件中各段的信息。

 

經過了編譯和彙編,便由源文件生成了目標文件,接下來的就是如果由多個目標文件連接成可執行文件的問題了。

 

 

 

 

 

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章