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