1,
編譯器編譯源**生成的檔案叫做目標檔案。
從結構上說,是編譯後的可執行檔案,只不過還沒有經過鏈結
3.1目標檔案的格式
1,可執行檔案的格式:windows下的pe 和linux下的elf
2,從廣義上說,目標檔案與可執行檔案的格式幾乎是一樣的,所以廣義上可以將目標檔案與可執行檔案看成是一種型別的檔案。
3,可執行檔案,動態鏈結庫,靜態鏈結庫都按照可執行檔案格式儲存(windows下是pe-coff格式,linux下是elf格式)。
4,linux下命令:$: file *** 顯示出對應檔案的型別
目標檔案是什麼樣的
1,目標檔案包含的內容:編譯後的機器指令**、資料,還有鏈結時要的一些資訊(比如符號表、除錯資訊、字串等)。
1, 一般目標檔案把上述資訊按不同的屬性,以節(section)的形式儲存。有時也叫段(segment)
2, **段常見的名字有「.code」 「.text」,編譯後的機器指令就放在**段,
資料段:一般名字都是「.data」,已初始化全域性變數和區域性靜態變數資料放這
3,elf檔案的開頭是乙個」檔案頭」,它描述了整個檔案的檔案屬性(是否可讀可寫可執行,是靜態鏈結還是動態鏈結及入口位址)、目標硬體、目標作業系統等資訊。
4, 檔案頭還有乙個段表(section table)。描述檔案各個段的陣列(各個段在檔案中的偏移和屬性)。
5, 關於bss段:未初始化的全域性變數和靜態區域性變數一般放在乙個」.bss」段的地方。
它只是為未初始化的全域性變數和靜態區域性變數預留位置而已,並沒有內容,在檔案中不佔空間。
6, 總體來說:程式源**被編譯後主要分成兩種段:程式指令(**段),程式資料(資料段,bss段)。
3.3挖掘******section.o
1,objdump 檢視各種目標檔案的結構和內容,objdump –h main.o -h 表示把elf檔案的各個段的基本資訊打出來
readelf 專門針對elf檔案格式的解析器
size 可以用來檢視elf檔案的**段、資料段和bss段的長度size main.o
3.3.1**段
1,objdump 的引數–s 將所有的段的內容以十六進製制的方式列印出來
-d 將所有包含指令的段反彙編比如objdump –s –d main.o
-h 把關鍵的段顯示了出來,忽略了輔助性段
3.3.2資料段和唯讀資料段
1,「.data」 :儲存的是已初始化全域性變數和區域性靜態變數
2,,」rodata」:存放的是唯讀資料,一般是程式中的唯讀變數(如用const修飾的)和字串常量。
3.3.3 bss段
1,存放的是未初始化的全域性變數和靜態區域性變數。不佔磁碟空間
3.3.4 其他段
還可以自定義段gcc提供的擴充套件機制
3.4 elf檔案結構描述
3.4.1 檔案頭
1,檢視readelf –h main.o
2,elf檔案中定義了:elf魔數、檔案機器位元組長度、資料儲存方式、版本、執行平台、abi版本、
elf重定位型別、硬體平台、硬體平台版本、入口位址、程式頭入口和長度、段表的位置和長度及段的數量等。
3,elf魔數:檔案頭最開始的4個位元組是所有elf都必須相同的標識碼。又稱為魔數。
魔數用來確認檔案型別,作業系統在載入可執行檔案的時候會確認魔數是否正確,若不正確則拒絕載入。
4,elf檔案型別:分為可重定位、可執行、共享目標檔案、核心轉儲檔案
3.4.2 段表(除了檔案頭以外最重要的結構)
1,描述了elf各個段的資訊(比如每個段的段名、段的長度、在檔案中的偏移,讀寫許可權及其他屬性)。
2,編譯器、鏈結器和裝載器都是靠段表來定位和訪問各個段的屬性的。
3,readelf –s main.o 顯示真正的段表結構
4,上述結果是乙個以「elf32_shdr」 結構體為元素的陣列。
3.4.3 重定位表
1,乙個叫」rel.text」的段,型別是」sht_rel」,就是重定位表
2,鏈結器在處理目標檔案時,需要對目標檔案中的某些部位進行重定位(即**段和資料段中那些對絕對位址的引用的位置)。
3,這些重定位資訊都記錄在elf檔案的重定位表裡。對於每個需要重定位的**段或資料段,都會有乙個相應的重定位表。
3.4.3 字串表
1,把elf檔案中用到的字串(段名、變數名等)集中起來存放到乙個表中。然後使用字串在表中的偏移來引用字串。這個表就是字串表。
2,一般字串表在elf檔案中也以段的形式儲存。常見的段名有」.strtab」,」.shstrtab」
3,」.strtab」:字串表:儲存普通的字串
4,」.shstrtab」:段表字串表:儲存段表中用到的字串,最常見的就是段名。
3.5鏈結的介面——符號
1,鏈結過程的本質就是要把多個不同的目標檔案之間相互粘滯到一起。
2,為了使不同目標檔案之間能夠相互粘合,這寫目標檔案必須要有固定的規則才行。
4,在鏈結中,我們將函式和變數統稱為符號(symbol),函式名或變數名就是符號名(symbol name)。
5,每乙個目標檔案都會有乙個相應的符號表(symbol table),記錄著目標檔案中所用到的所有的符號。
6,每個定義的符號有乙個對應的值叫做符號值(symbol value)。對於變數或函式來說,符號值就是它們的位址。
8, 符號的分類:(1)和(2)比較需要關注
(1) 定義在本目標檔案的全域性符號,可被其他目標檔案引用。
(2) 在本目標檔案中引用的全域性符號,在別的目標檔案中定義。一般叫做外部符號(external symbol)。
(3) 段名,這種符號往往由編譯器產生,它的值就是該段的起始位址。
(4) 區域性符號,這類符號只在編譯單元內部可見,
(5) 行號資訊,即目標檔案指令與源**中**行的對應關係,是可選的。
3.5.1 elf符號表結構
1,elf檔案中的符號表往往是檔案中的乙個段,y一般叫做「.symtab」。是乙個elf32_sym的陣列,陣列中每個元素對應乙個符號。
2,結構體定義如下
3.5.2 特殊符號
1,ld鏈結器產生可執行檔案時,會給我們定義很多符號(沒有在自己的程式中定義),但是可以直接宣告並且引用它,我們稱之為特殊符號。
3.5.3 符號修飾與函式簽名
1,c++增加了命名空間(namespace)的方法來解決多模組之間的符號衝突問題。
2,c++符號修飾
(1) 函式簽名:包含了乙個函式的資訊(函式名、引數型別、所在的類和命名空間和其他資訊)。函式簽名用於識別不同的函式。
3.5.4 extern」c」
1,c++為了與c相容,在符號的管理上,c++有乙個用來宣告或定義乙個c的符號extern」c」的關鍵字用法。
2,可以讓c++的名稱修飾機制不作用。
3.5.5 弱符號與強符號
1,多個目標檔案含有相同名字的全域性符號的定義,在鏈結時將會出現符號重複定義的錯誤。
2,編譯器預設函式和初始化了的全域性變數為強符號,未初始化的全域性變數為弱符號。
3,強弱符號都是針對定義來說的,不是針對符號的引用。假如extern int ext; ext不是強符號也不是弱符號,因為它是乙個外部變數的引用。
4,鏈結器按下面規則處理與選擇被多次定義的全域性符號。
(1) 不允許強符號被多次定義,否則報錯。
(2) 若乙個符號在某個目標檔案中是強符號,在其他檔案中都是弱符號,那麼選擇強符號。
(3) 若乙個符號早所有的目標檔案中都是若符號,那麼選擇其中占用空間最大的那乙個。
5,弱引用與強引用:
弱引用:假如鏈結時沒找到該符號的定義,鏈結器不會報錯,預設其為0或是乙個特殊值。
6,gcc中可以通過「__attribute__((weakref))」這個擴充套件關鍵字來宣告乙個外部函式為弱引用p93
用法:__attribute__((weakref)) void foo(); 這樣就說明foo為弱符號。
7,弱符號和弱引用對庫來說非常有用,比如庫中定義的弱符號可以被使用者定義的強符號所覆蓋。
8,補充:linux中支援多執行緒時需要在編譯時加上–lpthread 選項,比如gcc test.c -lpthread
3.6除錯資訊
1,在gcc編譯時加上–g 引數就會在產生的目標檔案裡面加上除錯資訊。目標檔案會多些debug段。
2,假設有個目標檔案test,linux下可以用」strip」 命令來去除除錯資訊,用法:$ strip test
3.7本章小結
參考《程式設計師的自我修養》
elf檔案分析 程式設計師的自我修養第三章學習
今天學習程式設計師的自我修養第三章學習對其中的一些內容提取。elf檔案 linux 作業系統二進位制檔案統一管理的格式檔案,對其分析對於理解編譯和鏈結 整合大型系統,修復 bug很有幫助。源程式都會通過編譯 鏈結成可執行檔案,可執行檔案的elf 檔案格式是固定,下面看如下圖 這個就是c語言 與 el...
程式設計師的自我修養第三章筆記(目標檔案有什麼)
目標檔案主要分為檔案頭 段 資料段 bss 段等等。objdump 工具可以分析目標檔案,包括 d 反彙編 s 顯示為16進製制的形式等等。一般情況下 會被放到 text 段,而全域性變數和靜態變數會被放到 data 和 bss 段。readelf 可以檢視 elf 檔案的檔案頭。elf檔案的結構可...
程式設計師的自我修養筆記三
目標檔案裡有什麼 3.1目標檔案的格式 windows中動態鏈結庫.dll linux中是.so windows中靜態鏈結庫.lib linux中是.a 可執行檔案在windows下按照pe coff格式儲存,linux下按照elf格式儲存 elf檔案分類 3.2目標檔案是什麼樣的 目標檔案中不僅有...