1. 概述
目標(biāo)文件是指源代碼經(jīng)過編譯后沒有被鏈接的那些中間文件(Linux下的.o)。因為目標(biāo)文件的內(nèi)容和結(jié)構(gòu)與可執(zhí)行文件很像立宜,所以目標(biāo)文件按照可執(zhí)行的文件的格式存儲。此外犬第,動態(tài)鏈接庫和靜態(tài)鏈接庫也是按照可執(zhí)行文件的格式存儲的儡炼。
2.初步了解目標(biāo)文件
下圖顯示了將源代碼編譯為目標(biāo)文件后,目標(biāo)文件的結(jié)構(gòu)和內(nèi)容玷禽。源代碼與目標(biāo)文件.png
如上圖所示赫段,目標(biāo)文件大致可以分為File Header,.text, .data, .bss四部分矢赁。
- File Header:文件頭糯笙。文件頭存儲了整個文件的屬性信息,包括文件是否可執(zhí)行撩银,是靜態(tài)鏈接還是動態(tài)鏈接以及入口地址等信息给涕。
- .text :代碼段。代碼段存儲了源代碼經(jīng)過編譯后的機器指令。
- .data:數(shù)據(jù)段够庙。數(shù)據(jù)段主要存儲了已經(jīng)初始化的全局變量或靜態(tài)變量恭应。
- .bss:未初始化的全局變量和靜態(tài)變量存儲在bss段。因為未初始化的全局變量和靜態(tài)變量默認(rèn)是0耘眨,如果放在data段就要為其分配存儲空間昼榛,沒有必要,因此放在了bss段剔难。bss段在編譯后實際大小為0胆屿,不占存儲空間。但是程序運行時钥飞,bss段是要占內(nèi)存空間的莺掠,因此可執(zhí)行文件必須要記錄所有未初始化的全局變量和靜態(tài)變量的大小的總和,作為bss段的大小读宙。所以,bss段只是為未初始化的全局變量和靜態(tài)變量預(yù)留位置楔绞,并沒有實際的內(nèi)容结闸。
1. 為什么要將程序的指令和數(shù)據(jù)進行分段呢?原因有如下三點:
- 當(dāng)程序被裝載后酒朵,數(shù)據(jù)和指令被映射到兩個不同的虛存區(qū)域桦锄。數(shù)據(jù)區(qū)域?qū)M程是可寫可讀的,而指令區(qū)只是可讀的蔫耽,這樣就避免了進程修改指令帶來的問題结耀。
- 當(dāng)系統(tǒng)中運行著多個該程序的副本時,它們的指令是相同的匙铡,數(shù)據(jù)可能不同图甜。因此在內(nèi)存中只需保存一份該程序的指令,這樣就節(jié)省了大量的存儲空間鳖眼。
- 為了提高緩存的命中率黑毅。將數(shù)據(jù)和指令分離有助于提高程序的局部性。
3. 目標(biāo)文件詳細結(jié)構(gòu)
目標(biāo)文件詳細內(nèi)容與結(jié)構(gòu).jpg
上圖為第二節(jié)中代碼編譯后的目標(biāo)文件中所有段的信息钦讳。下面矿瘦,本文將分析各個段的存儲中內(nèi)容。
- 首先愿卒,ELF Header缚去,文件頭,第二節(jié)中已經(jīng)說明琼开。主要存儲了該目標(biāo)文件屬性信息易结,包括文件是否可執(zhí)行,是動態(tài)鏈接還是靜態(tài)鏈接以及可執(zhí)行文件入口等信息。
- text:代碼段衬衬。
- data:數(shù)據(jù)段买猖。
- .rodata:只讀數(shù)據(jù)段。.rodata段存儲的是程序里的只讀變量(const修飾的變量)和字符串常量滋尉。在操作系統(tǒng)加載的可執(zhí)行文件的時候玉控,rodata會映射成只讀,這樣對這個段的任何修改都會被視為非法操作狮惜,保證程序安全性高诺。
- .comment段。注釋段碾篡。
-
.shstrtab: 字符串表虱而。在ELF文件中用到了很多字符串,比如段名开泽,變量名等牡拇。當(dāng) ELF 文件的其它部分需要引用字符串時,只需提供該字符串在字符串表中的位置索引即可穆律。如下圖所示:
image.png - symtab:ELF符號表惠呼,是一個ELF32_Sym結(jié)構(gòu)的數(shù)組。
typedef struct {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;
Elf32_Sym結(jié)構(gòu)中最主要的是以下三個成員:
1.st_name: 符號名峦耘。這個成員包含了該符號名在字符串表中的下標(biāo)符號名剔蹋,也即該符號名在符號串表中的下標(biāo)。
2.st_value:符號相對應(yīng)的值辅髓。這個值跟符號有關(guān)泣崩,可能是一個絕對值,也可能是一個地址等洛口,不同的符號矫付,它所對應(yīng)的值含義不同符號值。如果這個符號是一個函數(shù)或變量的定義绍弟,那么這個值就是函數(shù)或者是變量的地址(.data段)技即。
3.st_shndx:該符號所在的段。
- Section Table:段表樟遣。描述ELF文件各個段的信息而叼,你如每個段的段名、段長度豹悬、在文件中的偏移葵陵、讀寫權(quán)限以及段的其他屬性。
- .rel.text:當(dāng)鏈接噐把這個目標(biāo)文件和其他文件結(jié)合時瞻佛,.text節(jié)中的許多位置都需要修改脱篙。一般而言娇钱,任何調(diào)用外部函數(shù)或者引用全局變量(包括本目標(biāo)文件內(nèi)的全局變量,因為在鏈接時要多個目標(biāo)文件的相同段合并绊困,這樣數(shù)據(jù)的地址就會改變文搂,所以要重定位)的指令都需要修改。另一方面調(diào)用本地函數(shù)的指令則不需要修改秤朗。