1. 文件格式:
現(xiàn)在PC平臺流行的可執(zhí)行文件格式托嚣,主要是Windows下的PE(Portable Executable)和Linux的ELF(Executable Linkable Format),它們都是COFF(Common file format)格式的變種发皿。
目標文件就是源代碼編譯后但未進行鏈接的那些中間文件(Windows的.obj和Linux的.o),它跟可執(zhí)行文件的內(nèi)容與結構很相似绰垂,所以一般跟可執(zhí)行文件格式采用一種格式存儲键菱。
動態(tài)鏈接庫(Windows的.dll和Linux的.so)及靜態(tài)鏈接庫(Windows的.lib和Linux的.a)文件都按照可執(zhí)行文件格式存儲。
2. 文件內(nèi)容:
目標文件中的內(nèi)容包含編譯后的機器指令代碼努溃,數(shù)據(jù)硫嘶。還包括了鏈接時所需要的一些信息,比如符號表梧税,調(diào)試信息沦疾,字符串等。
目標文件將這些信息按不同的屬性第队,以“節(jié)(Section)”的形式存儲哮塞,有時候也叫做“段(Segment)”,它們表示一個一定長度的區(qū)域斥铺。
(1)代碼段(.text)
程序源代碼編譯后的機器指令
(2)數(shù)據(jù)段(.data)
已初始化了的全局變量和局部靜態(tài)變量數(shù)據(jù)
(3).bss段(.bss)
未初始化的全局變量和局部靜態(tài)變量
注:
未初始化的全局變量和局部靜態(tài)變量默認值都為0彻桃,本來也可以被放在.data段,但是在目標文件中為它們分配空間是沒有必要的晾蜘。因此邻眷,統(tǒng)一放到.bss段,.bss段大小為0剔交,不出現(xiàn)在目標文件中肆饶。而變量名以及變量的大小,與其他變量一樣岖常,放在了符號表(.symtab段)中驯镊。
(4)其他段
除了.text,.data,.bss這3個最常用的段之外板惑,目標文件也有可能包含其他的段橄镜,用來保存與程序相關的其他信息。
例如:.symtab符號表冯乘,.debug調(diào)試信息洽胶,.dynamic動態(tài)鏈接信息,等等裆馒。
3. 詳細分析:
(1)用GCC編譯SimpleScriont.c得到了目標文件SimpleSection.o
$ gcc -c SimpleSection.c
(2)使用binutils的工具objdump來查看目標文件內(nèi)部結構姊氓,-h顯示各個段的基本信息
$ objdump -h SimpleSection.o
其中,Size表示段的長度喷好,F(xiàn)ile off(File Offset)表示段所在的位置翔横,
每個段第二行表示段的屬性,例如梗搅,CONTENTS表示該段在文件中存在禾唁。
根據(jù)偏移地址我們就可以畫出文件結構了。
我們看到些膨,.bss段和.note.GNU-stack段在文件中都不存在蟀俊。
(3)查看代碼段
$ objdump -s -d SimpleSection.o
其中,
-s用于將所有段的內(nèi)容以16進制的方式打印出來订雾,
-d用于將所有包含指令的段反匯編肢预。
段數(shù)據(jù):
最左邊一列是偏移量,中間4列是16進制內(nèi)容洼哎,最右邊一列是.text段的ASCII碼形式烫映。
反匯編結果:
對照反匯編結果,可以看到.text段里包含的正是SimpleSection.c里兩個函數(shù)func1()和main()的機器指令噩峦。
(4)查看數(shù)據(jù)段
我們看到54000000(0x00000054)正好是全局變量global_init_var的值84锭沟,
55000000(0x00000055)正好是局部靜態(tài)變量static_var的值85。
注:
.rodata段存放的是只讀數(shù)據(jù)识补,一般是程序里面的只讀變量和字符串常量族淮。
(5).bss段
雖然在段的基本信息里,.bss段的大小為4字節(jié)凭涂,但是.bss段的數(shù)據(jù)為空祝辣,因此不占用目標文件的空間。
.bss段基本信息:
注:
程序中切油,未初始化的全局變量global_uninit_var和局部靜態(tài)變量static_var2共占8字節(jié)空間蝙斜,可是.bss段的大小只有4字節(jié)大小。原因是澎胡,有些編譯器會將全局未初始化的變量放到.bss段孕荠,有些則不然娩鹉,只是預留一個未定義的全局變量符號。因此稚伍,這里的4字節(jié)弯予,指的是static_var2。
4. 符號表
鏈接的本質(zhì)槐瑞,就是把不同的多個目標文件粘合到一起熙涤。
每個函數(shù)或變量都必須有自己獨特的名字阁苞,才能避免在鏈接過程中產(chǎn)生混亂困檩。
在鏈接中,函數(shù)和符號統(tǒng)稱為符號(Symbol)那槽,函數(shù)名或變量名稱為符號名(Symbol Name)悼沿。
每個目標文件都有一個相應的符號表(Symbol Table),記錄了目標文件中用到的所有符號骚灸。每個符號都有一個對應的值糟趾,叫做符號值(Symbol Value),符號值可以是符號所對應的數(shù)據(jù)在段中的偏移量甚牲,也可以是該符號的對齊屬性义郑。
符號表保存在.symtab段中,信息如下:
第1列Num表示符號表數(shù)組的下標丈钙,第2列Value就是符號值非驮,第3列Size為符號大小,第4列第5列分別為符號類型和綁定信息雏赦,第6列暫時忽略它,第7列Ndx表示該符號所屬的段星岗,最后一列,符號名稱俏橘。
我們看到func和main的Ndx為1允华,表示代碼段寥掐,因為.text在段表中的下標為1。
其中曹仗,段表信息如下:
printf這個符號,Ndx為UND(SHN_UNDEF)怎茫,沒有在SimpleSection.c中定義妓灌,是被引用的。
global_init_var是已初始化的全局變量蜜宪,Ndx是3,定義在.data段圃验,偏移量為0
static_var.1553是已初始化的局部靜態(tài)變量掉伏,Ndx是3,定義在.data段澳窑,偏移量為4
global_uninit_var是未初始化的全局變量斧散,Ndx是COM(SHN_COMMON)本身并沒有在.bss段中。
static_var2.1534是未初始化的局部靜態(tài)變量摊聋,Ndx是4鸡捐,定義在.bss段,偏移量為0
對于類型為STT_SECTION的符號麻裁,名稱并沒有顯示箍镜,它們是Ndx所示段的段名
SimpleSection.c這個符號表示編譯單元的源文件名。