日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

目标文件里面到底有什么(2)?

發布時間:2025/3/15 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 目标文件里面到底有什么(2)? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

真正了不起的程序員對自己的程序的每一個字節都了如指掌!

前面對于目標文件只是做了概念上的闡述,如果不徹底深入目標文件的具體細節,這樣的分析就是泛泛而談沒有意義,也沒有深入的理解。就象知道TCP/IP協議是基于包的結構,但是從來沒有看到過包的結構是什么樣的(說得好像就是我),包的頭部有哪些內容?目標地址和源地址是怎樣存放的?如果不能細致且深入的去了解,就會步入一個誤區:很多問題其實在表面上看似很簡單,實際深入內部會發現很多鮮為人知的秘密,或者發現以前自己認為理所當然的東西居然是錯誤的!!!我需要做的,就是改變這個現狀!

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>查看目標文件中的內容如下: 從上面的結果中,我們能夠看到:SimpleSection.o中的段的數量比我們想象中的要多,除了最基本的代碼段、數據段和BSS段以外,還有3個段分別是只讀數據段(.rodata)、注釋信息段(.comment)、堆棧提示段(.note.GNU-stack)。先不考慮這三個額外的段。想了解一下關于段的幾個重要的屬性,首先最容易理解的就是段的長度(Size)和段所在的位置(File Offset),每個段中的第二行 “CONTENTS”/"ALLOC"等表示的就是段的各種屬性。“CONTENTS”表示該段在文件中是存在的。我們能夠發現在BSS段中是沒有“CONTENTS”的,這表示它實際上在EFL文件中根本不存在內容。所以,在EFL文件中實際存在的也就是“.text”、".data"、“.comment”、“.rodata”這4個段了。對于本項目,他們在ELF中的結構如下圖所示:
代碼段 將所有段的內容以十六進制的形式打印,并對所有包含指令的段進行反匯編,得到以下結果:

“Contents of Section ?.text”就是.text的數據以十六進制方式打印出來的內容,總共0x5b字節,跟前面我們了解到的".text"段中的長度相一致。最左面一列指的是偏移量,中間四列指的是十六進制內容,最后一列是該段的ASCII碼形式。 對照下面反匯編的結果,我們可以很明顯的看到,.text段里所包含的正是SimpleSection.c里面兩個函數func1()和main()函數的指令。 數據段與只讀數據段 .data段保存的是那些已經初始化了的全局靜態變量或局部靜態變量。前面的SimpleSection.c代碼中一共有兩個這樣變量,分別是global_init_var和static_var。這兩個變量每一個都是4個字節,總共八個字節,所以“.data”這個段的大小為8個字節。 “.rodata”中只存放只讀數據。本例中存放的就是字符串常量,剛好是4個字節。單獨設立“.rodata”段有很多好處,不管是在語義上支持了C++的const關鍵字,而且操作系統自加載時可以將“.rodata”段的屬性映射為只讀,這樣對于這個段的任何修改操作都會作為非法操作處理,大大保證了程序的安全性。 BSS段 .bss段存放的是未初始化的全局變量和局部靜態變量。如示例代碼中,global_uninit_var和static_var就是被存放在.bss段,其實更準確的說法是,.bss段為他們預留了空間。 其他段 除了.data、.text、.bss這3個最常用的段之外,ELF文件也有可能包含其他的段,用來保存與程序相關的其他信息。具體如下表所示:
這些段的名字都是由"."作為前綴,表示這些表的名字是系統保留的,應用程序也可以使用一些非系統保留的名字作為段名。如我們可以在ELF文件結構中插入一個“music”段,里面存放一首MP3,當ELF文件結構運行起來以后可以讀取這個段并播放該首歌。但是,應用程序自定義的段名不能使用"."作為前綴,否則容易與系統保留段名沖突

2.ELF文件結構描述


其實,通過上面的SimpleSection.o實例,我們已經基本了解到了ELF文件的輪廓,下面我們可以正式地看一下ELF的文件總體結構。如下圖所示:

ELF目標文件格式的最前部是ELF文件頭(File Header),它包含了描述整個文件的基本屬性,比如ELF文件版本、目標機器型號、程序入口地址等。緊接著是ELF文件各個段。其中,ELF文件中與段有關的重要結構就是段表(Section Header Table),該表描述了ELF文件中語段有關的所有段的信息,比如每個段的段名、段的長度、在文件中的偏移、讀寫權限以及其他屬性。本處,僅想詳細討論一下符號表的作用,以為他和程序的鏈接有著很大的關聯。 連接的接口——符號
鏈接過程的本質就是要把多個不同的目標文件之間相互關聯到一起。為了使不同目標文件之間能夠在相互粘合,這些目標文件之間必須有固定的規則才可以。在鏈接中,目標文件之間相互拼接實際上是目標文件地址間的相互引用,即對函數以及變量的地址進行引用。比如目標文件B要用到目標文件A中的函數“foo”,那么我們就成目標文件A定義了函數“foo”,稱目標文件B引用了目標文件A中的函數“foo”。在鏈接中,我們將函數以及變量都通稱為符號,函數名或者變量名就是符號名 我們可以將符號看作是鏈接中的黏合劑,整個連接過程正是基于符號才能正常地完成。鏈接過程中很重要的一環就是符號的管理,每一個目標文件都會有一個相應的符號表,這個表里面記載著目標文件中所有要用的到的符號,每一個定義的符號有一個對應的值,叫做符號值。對于變量以及函數而言,符號值就是他們的地址。除了函數和變量之外,也會存在其他幾種不常用的符號,如下面所示:

3.調試信息

目標文件里面還有可能包含調試信息。幾乎所有現代的編譯器都支持源代碼級的調試(比如我們可以在函數里面設置斷點、可以監視變量變化、可以單步執行等),前提是編譯器必須提前將源代碼與目標代碼之間的關系(比如目標代碼中的地址對應源代碼中哪一行?函數和變量的類型?結構體的定義?字符串保存到那個文件里面?)。設置有些高級的編譯器或調試器支持查看STL容器中的相關內容。

總結

以上是生活随笔為你收集整理的目标文件里面到底有什么(2)?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。