第3章 目標文件里有什么

.obj是目標文件,所以可以知道目標文件是指編譯后生成的文件,目標文件幾乎和可執行文件相同只是稍微有點不同而已。其不同之處在于有些符號和地址沒有被調整。

3.1目標文件的格式

正是因為目標文件與可執行文件幾乎相同,所以它們的存儲格式是一樣的,可以把它們近似看成同一種文件。

Linux下的動態鏈接庫格式為.so,Windows和Linux下的靜態鏈接庫格式分別為.lib和.a。

靜態鏈接庫是一個文件,該文件包含了很多目標文件,它是一個整體。

Linux下的可執行文件是按照ELF格式存儲的,ELF標準包含4種文件,請看P81。我所熟悉的Windows下的DLL就屬于共享目標文件。

3.2目標文件是什么樣的

目標文件一般包含了哪些內容?編譯后的機器指令代碼、數據、連接所需的信息、符號表、調試信息、字符串等。

目標文件把信息按照屬性的不同分段存儲。寫到這里我感覺這書上說的與老師課上講的程序在內存中的分段方法有些相似。在目標文件中,編譯后的機器指令代碼放在代碼段(Code

Section)中,段名一般為.code和.text。全局變量和靜態變量放在數據段(Data

Section)中,段名一般為.data。

BSS段(Block Started By Symbol)用來存儲未初始化的靜態變量和全局變量。話雖如此bss中并沒有這些變量的內容,它只是為這些變量按照所占空間大小預留空間而已。由于這些變量默認就是0,所以壓根沒必要再為它們分配一個數據0,也沒有必要讓它們待在data段中。因此bss的作用是為這些變量預留空間。

另外目標代碼還有一個文件頭用來保存該目標文件的信息,它里面還有一個段表。

源代碼被編譯以后生成兩種段數據段和指令段,.code.text屬于指令段.data.bss屬于數據段。

這樣分主要有3點好處:

1、防止程序被有意無意篡改。這是因為指令段只讀,數據段可讀寫。

2、提高了緩存命中率。

3、節省內存空間。因為指令段可被多個副本共享,但是副本可以擁有自己的數據段。

3.3挖掘SimpleSection.o

原來目標文件中的段還有只讀數據段(.rodata)、注釋信息段(.comment)、堆棧提示段(.note.GNU-stack)。

從書中所給的例子來看一個ELF文件只有4個段是由內容的,即.data、.text、.rodata、.comment。

從圖3-3可以看出在內存中,從低地址到高地址是按照ELF

header、text、data、rodata、comment、other

data的順序存放的。

3.3.3

BSS段

由本小節可知,全局變量可能因為語言和編譯器的不同不一定存放在bss段,但是靜態變量一定存放在bss段。

雖說bss存放的是未初始化的靜態和全局變量,但是有些變量如果被初始化為0,它也會被放在bss中,這是編譯器的優化,有時候這種優化會帶來麻煩。

3.3.4其他段

表3-2列出了其他段及意義。

此外,這個段還可以自定義。

3.4

ELF文件結構描述

圖3-4展示了ELF的層次結構。

最重要的兩個部分就是ELF文件頭和段表。ELF文件頭描述整個文件的基本屬性,段表描述各段的信息。

3.4.1文件頭

清單3-2清楚地描述了ELF文件頭的信息,P95黑體部分列舉了ELF文件頭包含的信息。

ELF文件兼容各平臺,它的文件結構和相關參數定義在”/usr/include/elf.h”里,它有32位和64位兩種。

表3-3展示了elf.h的自定義變量體系。

表3-4展示了ELF文件頭結構成員含義。

ELF魔數:ELF文件頭的第一個字段是Magic,包含16bytes,對應于Elf32_Ehdr中的e_ident成員。Magic用來表示平臺的各種屬性。

1~4個字節是所有ELF文件都相同的標識碼,分別對應del、E、L、F,這四個字節就是ELF魔數。操作系統通過確認魔術是否正確以決定是否加載可執行文件。

第5個字節用來表示ELF文件是32位的還是64位的。

第6個字節用來表示ELF字節序。

第7個字節用來表示ELF文件版本號。

后面的9個字節用來預留,有些平臺可能用來作為擴展標志。

Elf32_Ehdr中的e_type成員表示ELF文件類型,ELF總共有三種文件類型如表3-5所示。操作系統是通過判斷文件類型而不是擴展名來確定ELF文件類型的。

Elf32_Ehdr中的e_machine成員表示ELF文件的平臺屬性。雖然ELF遵循統一標準但不代表同一ELF文件可以在不同平臺上使用。

3.4.2段表

它用來表示各個段的信息,ELF文件中的段是由段表決定的。

一個ELF文件不僅僅包含像data、text、bss這樣的段,還包括其他的輔助性段。

段表是一個Elf32_Shdr類型的結構體數組,元素的個數代表段的個數,每個元素對應一個段。這個Elf32_Shdr被稱為段描述符。

表3-7描述了Elf32_Shdr中各字段的意義。

段的名稱對于編譯和鏈接有意義,對操作系統無意義。決定段的類型的是段的類型字段,并不是段的后綴名和名稱。

段的類型和段的標志位字段決定了段的屬性。表3-8展示了段的各種類型。

段的標志位表示該段在進程虛擬地址空間中的屬性,如是否可讀。表3-9列出了段的各種屬性。

表3-10列出了系統保留段的各種屬性。

段的連接信息包括sh_link和sh_info,它們與鏈接相關,如表3-11所示。

3.4.3重定位表

目標文件中有一個SHT_REL的.rel.text字段,它是重定位表。重定位發生在連接的過程中,這個在前面已經講過,重定位表記錄了重定位相關信息。

3.4.4字符串表

顧名思義,就是用來表示各種名稱的字符串的表。它是一個裝有各種字符串的表格,每個字符在表中都有一個固定的位置。

這種表在ELF文件中保存為2種形式——.strtab和.shstrtab,它們分別是字符串表和段字符串表,它們在ELF文件中都以獨立的段而存在。為了輕松地找到這個段,在ELF文件頭中包含了這兩個段的下標,名為e_shstrndx。

3.5鏈接的接口——符號

鏈接是組合目標文件的過程,目標文件是根據彼此之間的地址相互引用,從而組合成可執行文件的。而,這個地址可以簡單地理解為目標文件中的函數和變量。在這里,函數和變量統稱為符號,函數名和變量名統稱為符號名。

鏈接器的著眼點主要在定義在本目標文件和定義在其他目標文件的全局性符號,因為只有這些涉及到目標文件之間的組合。

3.5.1

ELF符號表結構

ELF文件的符號表是一個段,段名為“.symtab”,它是一個Elf32_sym類型的數組,每個數組元素代表一個符號。

在Elf32_sym結構體中有一個32bit成員叫st_info,低4bit表示符號的類型,高28bit符號的綁定信息。綁定信息具體可見表3-15,符號類型可參見表3-16。

Elf32_sym.st_shndx:如果符號定義在本目標文件中,它表示該符號所在的段在段表中的下標,否則它具有其他意義。st_shndx具體信息可見表3-17。

Elf32_sym.st_value:每個符號都有一個對應值,它一般為變量和函數的地址。st_value的意義有如下幾種:

1、如果符號定義在目標文件中,并且它不是COMMON類型,則st_value代表符號在段中的偏移。

2、如果符號定義在目標文件中并且是COMMON類型,則st_value表示符號的對齊屬性。

3、在可執行文件中st_value表示符號的虛擬地址。

3.5.2特殊符號

鏈接器本身自帶的,不是你定義的,定義在鏈接腳本中的,但是你可以用的,這樣的符號是特殊符號。它們存在的時機是鏈接器鏈接生成可執行文件時,此時鏈接器會將它們解析成正確的值,

書中P110舉了幾個具有代表性的特殊符號。

3.5.3符號修飾與函數簽名

本小節明確了函數簽名的概念。

函數簽名:主要是指函數名和參數類型,其次是所在類和命名空間等。它用于區分不同函數。

編譯器和連接器會使用名稱修飾的辦法加工函數簽名使之成為修飾后名稱,在C++中為符號名。

不同的編譯器對函數簽名的修飾方法不同,這導致不同種類的目標文件無法互連。

原來C++編譯器已經默認定義了宏__cplusplus來兼容C語言和C++。

3.5.5弱符號和強符號

在不同目標文件中含有相同全局性符號定義,這種情況被稱為強符號,它會引起符號重定義。

C/C++編譯器認為未初始化的全局變量是弱符號。

這個強弱符號是可以被定義的,所以強弱之別是根據定義來劃分的,并不針對符號的引用,P117代碼說明了這一點。

鏈接器根據符號的強弱來處理和選擇定義的全局變量:

1、不允許多次定義強符號,否則報錯。

2、同一個符號在各目標文件中出現了多次,但只有一個是強符號,那么編譯器選擇強符號的那個。

3、如果一個符號在所有目標文件中都是弱符號,那么編譯器選擇占用空間最大的一個。由此可見編譯器對于弱符號的選擇并不明顯,所以由弱符號造成的錯誤也相對難以發現。

強引用:目標文件對于非本目標文件的符號引用,在鏈接成可執行文件的過程中,如果找不到該符號的定義,就報未定義錯誤。

弱引用:與強引用差不多,只不過在找不到符號時不報錯。

強弱引用主要用于庫的鏈接。對于未定義的弱引用,編譯器為便于識別把它看作是某一值,一般為0。

弱符號與COMMON塊聯系較密切。

弱引用是可以手動聲明的,如P118第一段代碼所示。

弱符號的作用在于提供一個默認的庫符號,但是當用戶想要自定義該符號的時候,該自定義符號就獲得了更高的優先級。而弱引用的作用在于增強了程序的可擴展性,因為有了弱引用程序功能更強,沒有弱引用程序也能正常運行。

3.6調試信息

目標文件和可執行文件中都可能保存調試信息,ELF文件采用DWARF格式保存調試信息。

由于調試信息與可執行文件最終結果無關,而且占用大量空間,所以在發布軟件時應該去掉這些調試信息。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容