1. 檔案格式:
現在pc平台流行的可執行檔案格式,主要是windows下的pe(portable executable)和linux的elf(executable linkable format),它們都是coff(common file format)格式的變種。
目標檔案就是源**編譯後但未進行鏈結的那些中間檔案(windows的.obj和linux的.o),它跟可執行檔案的內容與結構很相似,所以一般跟可執行檔案格式採用一種格式儲存。
動態鏈結庫(windows的.dll和linux的.so)及靜態鏈結庫(windows的.lib和linux的.a)檔案都按照可執行檔案格式儲存。
2. 檔案內容:
目標檔案中的內容包含編譯後的機器指令**,資料。還包括了鏈結時所需要的一些資訊,比如符號表,除錯資訊,字串等。
目標檔案將這些資訊按不同的屬性,以「節(section)」的形式儲存,有時候也叫做「段(segment)」,它們表示乙個一定長度的區域。
(1)**段(.text)
程式源**編譯後的機器指令
(2)資料段(.data)
已初始化了的全域性變數和區域性靜態變數資料
(3).bss段(.bss)
未初始化的全域性變數和區域性靜態變數
注:
未初始化的全域性變數和區域性靜態變數預設值都為0,本來也可以被放在.data段,但是在目標檔案中為它們分配空間是沒有必要的。因此,統一放到.bss段,.bss段大小為0,不出現在目標檔案中。而變數名以及變數的大小,與其他變數一樣,放在了符號表(.symtab段)中。
(4)其他段
除了.text,.data,.bss這3個最常用的段之外,目標檔案也有可能包含其他的段,用來儲存與程式相關的其他資訊。
3. 詳細分析:
(1)用gcc編譯******scriont.c得到了目標檔案******section.o
$ gcc -c ******section.c
(2)使用binutils的工具objdump來檢視目標檔案內部結構,-h顯示各個段的基本資訊
其中,size表示段的長度,file off(file offset)表示段所在的位置,
每個段第二行表示段的屬性,例如,contents表示該段在檔案中存在。
根據偏移位址我們就可以畫出檔案結構了。
我們看到,.bss段和.note.gnu-stack段在檔案中都不存在。
(3)檢視**段
$ objdump -s -d ******section.o
其中,
-s用於將所有段的內容以16進製制的方式列印出來,
-d用於將所有包含指令的段反彙編。
段資料:
最左邊一列是偏移量,中間4列是16進製制內容,最右邊一列是.text段的ascii碼形式。
反彙編結果:
對照反彙編結果,可以看到.text段裡包含的正是******section.c裡兩個函式func1()和main()的機器指令。
(4)檢視資料段
我們看到54000000(0x00000054)正好是全域性變數global_init_var的值84,
55000000(0x00000055)正好是區域性靜態變數static_var的值85。
注:
.rodata段存放的是唯讀資料,一般是程式裡面的唯讀變數和字串常量。
(5).bss段
雖然在段的基本資訊裡,.bss段的大小為4位元組,但是.bss段的資料為空,因此不占用目標檔案的空間。
.bss段基本資訊:
注:
程式中,未初始化的全域性變數global_uninit_var和區域性靜態變數static_var2共佔8位元組空間,可是.bss段的大小只有4位元組大小。原因是,有些編譯器會將全域性未初始化的變數放到.bss段,有些則不然,只是預留乙個未定義的全域性變數符號。因此,這裡的4位元組,指的是static_var2。
4. 符號表
鏈結的本質,就是把不同的多個目標檔案粘合到一起。
每個函式或變數都必須有自己獨特的名字,才能避免在鏈結過程中產生混亂。
在鏈結中,函式和符號統稱為符號(symbol),函式名或變數名稱為符號名(symbol name)。
每個目標檔案都有乙個相應的符號表(symbol table),記錄了目標檔案中用到的所有符號。每個符號都有乙個對應的值,叫做符號值(symbol value),符號值可以是符號所對應的資料在段中的偏移量,也可以是該符號的對齊屬性。
符號表儲存在.symtab段中,資訊如下:
第1列num表示符號表陣列的下標,第2列value就是符號值,第3列size為符號大小,第4列第5列分別為符號型別和繫結資訊,第6列暫時忽略它,第7列ndx表示該符號所屬的段,最後一列,符號名稱。
我們看到func和main的ndx為1,表示**段,因為.text在段表中的下標為1。
其中,段表資訊如下:
printf這個符號,ndx為und(shn_undef),沒有在******section.c中定義,是被引用的。
global_init_var是已初始化的全域性變數,ndx是3,定義在.data段,偏移量為0
static_var.1553是已初始化的區域性靜態變數,ndx是3,定義在.data段,偏移量為4
global_uninit_var是未初始化的全域性變數,ndx是com(shn_common)本身並沒有在.bss段中。
static_var2.1534是未初始化的區域性靜態變數,ndx是4,定義在.bss段,偏移量為0
對於型別為stt_section的符號,名稱並沒有顯示,它們是ndx所示段的段名
******section.c這個符號表示編譯單元的源檔名。
linux 如何執行乙個可執行檔案
本文只為整理思路,供自己日後參考。現在就從從乙個執行檔案a.out的執行開始,自上而下地分析linux是如何執行乙個執行檔案的。1 首先,需要了解一下a.out這個目標檔案。a.out在linux下是elf executable linkable format 檔案格式,該目標檔案由乙個檔案頭 段 ...
linux 執行可執行檔案
1 首先,需要了解一下a.out這個目標檔案。a.out在linux下是elf executable linkable format 檔案格式,該目標檔案由乙個檔案頭 段 資料段 已初始化 從定位資訊區 符號表及符號名字字串構成,如下左圖所示,經過鏈結後生成執行檔案如下右圖所示,需要說明的是1 bs...
linux編譯和執行乙個可執行檔案初學篇
在大公司上班,系統中的模組都有明確的分工,一直以來也只是熟悉自己負責的那一點點東西,感覺就是井底之蛙。所以希望自己能夠靜下心來,學一點,多學一點。本篇作為乙個學習的起點,先學習如何編譯和執行乙個可執行檔案。測試 如下 include int main int argc,char argv int i...