目标文件里面到底有什么(2)?
生活随笔
收集整理的這篇文章主要介紹了
目标文件里面到底有什么(2)?
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
真正了不起的程序員對(duì)自己的程序的每一個(gè)字節(jié)都了如指掌!
前面對(duì)于目標(biāo)文件只是做了概念上的闡述,如果不徹底深入目標(biāo)文件的具體細(xì)節(jié),這樣的分析就是泛泛而談沒(méi)有意義,也沒(méi)有深入的理解。就象知道TCP/IP協(xié)議是基于包的結(jié)構(gòu),但是從來(lái)沒(méi)有看到過(guò)包的結(jié)構(gòu)是什么樣的(說(shuō)得好像就是我),包的頭部有哪些內(nèi)容?目標(biāo)地址和源地址是怎樣存放的?如果不能細(xì)致且深入的去了解,就會(huì)步入一個(gè)誤區(qū):很多問(wèn)題其實(shí)在表面上看似很簡(jiǎn)單,實(shí)際深入內(nèi)部會(huì)發(fā)現(xiàn)很多鮮為人知的秘密,或者發(fā)現(xiàn)以前自己認(rèn)為理所當(dāng)然的東西居然是錯(cuò)誤的!!!我需要做的,就是改變這個(gè)現(xiàn)狀!
1.挖掘SimpleSection.o中的信息
<span style="font-size:18px;">/**SimpleSection.c**linux:* gcc -c SimpleSection.c**Windows:* cl SimpleSection.c /c /Za*/ int printf(const char* format, ...); int global_init_var = 84; int global_uninit_var;void func1(int i) {printf("%d\n",i); } int main(void) {static int static_var = 85;static int static_var2;int a = 1;int b;func1(static_var + static_var2 + a + b);return a; }</span> 利用GCC對(duì)源文件進(jìn)行編譯可以得到目標(biāo)文件: <span style="font-size:18px;">$ gcc -c SimpleSection.c</span>查看目標(biāo)文件中的內(nèi)容如下: 從上面的結(jié)果中,我們能夠看到:SimpleSection.o中的段的數(shù)量比我們想象中的要多,除了最基本的代碼段、數(shù)據(jù)段和BSS段以外,還有3個(gè)段分別是只讀數(shù)據(jù)段(.rodata)、注釋信息段(.comment)、堆棧提示段(.note.GNU-stack)。先不考慮這三個(gè)額外的段。想了解一下關(guān)于段的幾個(gè)重要的屬性,首先最容易理解的就是段的長(zhǎng)度(Size)和段所在的位置(File Offset),每個(gè)段中的第二行 “CONTENTS”/"ALLOC"等表示的就是段的各種屬性。“CONTENTS”表示該段在文件中是存在的。我們能夠發(fā)現(xiàn)在BSS段中是沒(méi)有“CONTENTS”的,這表示它實(shí)際上在EFL文件中根本不存在內(nèi)容。所以,在EFL文件中實(shí)際存在的也就是“.text”、".data"、“.comment”、“.rodata”這4個(gè)段了。對(duì)于本項(xiàng)目,他們?cè)贓LF中的結(jié)構(gòu)如下圖所示:代碼段: 將所有段的內(nèi)容以十六進(jìn)制的形式打印,并對(duì)所有包含指令的段進(jìn)行反匯編,得到以下結(jié)果:
“Contents of Section ?.text”就是.text的數(shù)據(jù)以十六進(jìn)制方式打印出來(lái)的內(nèi)容,總共0x5b字節(jié),跟前面我們了解到的".text"段中的長(zhǎng)度相一致。最左面一列指的是偏移量,中間四列指的是十六進(jìn)制內(nèi)容,最后一列是該段的ASCII碼形式。 對(duì)照下面反匯編的結(jié)果,我們可以很明顯的看到,.text段里所包含的正是SimpleSection.c里面兩個(gè)函數(shù)func1()和main()函數(shù)的指令。 數(shù)據(jù)段與只讀數(shù)據(jù)段: .data段保存的是那些已經(jīng)初始化了的全局靜態(tài)變量或局部靜態(tài)變量。前面的SimpleSection.c代碼中一共有兩個(gè)這樣變量,分別是global_init_var和static_var。這兩個(gè)變量每一個(gè)都是4個(gè)字節(jié),總共八個(gè)字節(jié),所以“.data”這個(gè)段的大小為8個(gè)字節(jié)。 “.rodata”中只存放只讀數(shù)據(jù)。本例中存放的就是字符串常量,剛好是4個(gè)字節(jié)。單獨(dú)設(shè)立“.rodata”段有很多好處,不管是在語(yǔ)義上支持了C++的const關(guān)鍵字,而且操作系統(tǒng)自加載時(shí)可以將“.rodata”段的屬性映射為只讀,這樣對(duì)于這個(gè)段的任何修改操作都會(huì)作為非法操作處理,大大保證了程序的安全性。 BSS段: .bss段存放的是未初始化的全局變量和局部靜態(tài)變量。如示例代碼中,global_uninit_var和static_var就是被存放在.bss段,其實(shí)更準(zhǔn)確的說(shuō)法是,.bss段為他們預(yù)留了空間。 其他段: 除了.data、.text、.bss這3個(gè)最常用的段之外,ELF文件也有可能包含其他的段,用來(lái)保存與程序相關(guān)的其他信息。具體如下表所示:
這些段的名字都是由"."作為前綴,表示這些表的名字是系統(tǒng)保留的,應(yīng)用程序也可以使用一些非系統(tǒng)保留的名字作為段名。如我們可以在ELF文件結(jié)構(gòu)中插入一個(gè)“music”段,里面存放一首MP3,當(dāng)ELF文件結(jié)構(gòu)運(yùn)行起來(lái)以后可以讀取這個(gè)段并播放該首歌。但是,應(yīng)用程序自定義的段名不能使用"."作為前綴,否則容易與系統(tǒng)保留段名沖突。
2.ELF文件結(jié)構(gòu)描述
其實(shí),通過(guò)上面的SimpleSection.o實(shí)例,我們已經(jīng)基本了解到了ELF文件的輪廓,下面我們可以正式地看一下ELF的文件總體結(jié)構(gòu)。如下圖所示:
ELF目標(biāo)文件格式的最前部是ELF文件頭(File Header),它包含了描述整個(gè)文件的基本屬性,比如ELF文件版本、目標(biāo)機(jī)器型號(hào)、程序入口地址等。緊接著是ELF文件各個(gè)段。其中,ELF文件中與段有關(guān)的重要結(jié)構(gòu)就是段表(Section Header Table),該表描述了ELF文件中語(yǔ)段有關(guān)的所有段的信息,比如每個(gè)段的段名、段的長(zhǎng)度、在文件中的偏移、讀寫(xiě)權(quán)限以及其他屬性。本處,僅想詳細(xì)討論一下符號(hào)表的作用,以為他和程序的鏈接有著很大的關(guān)聯(lián)。 連接的接口——符號(hào):
鏈接過(guò)程的本質(zhì)就是要把多個(gè)不同的目標(biāo)文件之間相互關(guān)聯(lián)到一起。為了使不同目標(biāo)文件之間能夠在相互粘合,這些目標(biāo)文件之間必須有固定的規(guī)則才可以。在鏈接中,目標(biāo)文件之間相互拼接實(shí)際上是目標(biāo)文件地址間的相互引用,即對(duì)函數(shù)以及變量的地址進(jìn)行引用。比如目標(biāo)文件B要用到目標(biāo)文件A中的函數(shù)“foo”,那么我們就成目標(biāo)文件A定義了函數(shù)“foo”,稱目標(biāo)文件B引用了目標(biāo)文件A中的函數(shù)“foo”。在鏈接中,我們將函數(shù)以及變量都通稱為符號(hào),函數(shù)名或者變量名就是符號(hào)名。 我們可以將符號(hào)看作是鏈接中的黏合劑,整個(gè)連接過(guò)程正是基于符號(hào)才能正常地完成。鏈接過(guò)程中很重要的一環(huán)就是符號(hào)的管理,每一個(gè)目標(biāo)文件都會(huì)有一個(gè)相應(yīng)的符號(hào)表,這個(gè)表里面記載著目標(biāo)文件中所有要用的到的符號(hào),每一個(gè)定義的符號(hào)有一個(gè)對(duì)應(yīng)的值,叫做符號(hào)值。對(duì)于變量以及函數(shù)而言,符號(hào)值就是他們的地址。除了函數(shù)和變量之外,也會(huì)存在其他幾種不常用的符號(hào),如下面所示:
3.調(diào)試信息
目標(biāo)文件里面還有可能包含調(diào)試信息。幾乎所有現(xiàn)代的編譯器都支持源代碼級(jí)的調(diào)試(比如我們可以在函數(shù)里面設(shè)置斷點(diǎn)、可以監(jiān)視變量變化、可以單步執(zhí)行等),前提是編譯器必須提前將源代碼與目標(biāo)代碼之間的關(guān)系(比如目標(biāo)代碼中的地址對(duì)應(yīng)源代碼中哪一行?函數(shù)和變量的類型?結(jié)構(gòu)體的定義?字符串保存到那個(gè)文件里面?)。設(shè)置有些高級(jí)的編譯器或調(diào)試器支持查看STL容器中的相關(guān)內(nèi)容。總結(jié)
以上是生活随笔為你收集整理的目标文件里面到底有什么(2)?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 『转』数据库的委托之类型分类处理
- 下一篇: 秋赛牧飞鸽传书