參考:《程序員的自我修養》第3章
真正了不起的程序員對自己的程序的每一個字節都瞭如指掌。
學習筆記,我就是個搬運工。
ELF:Executable Linkable Format。
PE-COFF:Portable Executable Common File Format。
查看目標文件內部結構的工具:
objdump -h file.o // 將elf文件各個段的基本信息打印出來
objdump -x file.o // 將elf文件各個段的基本信息打印出來,更詳細
objdump -s file.o // 將段的內容以16進制打印出來
objdump -d file.o // 反彙編
readelf -h file.o // 查看elf文件頭信息
readelf -S file.o // 查看elf文件段表結構
nm file.o // 查看elf文件的符號表
查看elf文件代碼段、數據段、BSS長度:
size file.o
1. 什麼是目標文件
源代碼編譯後但未進行鏈接的中間文件,Linux下的.o,Windows下的.obj。
目標文件與可執行文件採用相同的文件格式,主流是Linux爲ELF,Windows爲PE-COFF。
2. 可執行文件格式
3. ELF文件結構
ELF文件結構大致可分爲ELF文件頭、段表、字符串表、符號表等。
-
ELF頭
ELF魔數: ELF頭中的Magic,是ELF魔數,16個字節,對應class到ABI version的內容,用於標識ELF文件的平臺屬性,如ELF字長,字節序,ELF版本等。
0x7f 45 4c 46:對應的ASCII碼爲 DEL控制符,E,L,F。
文件類型:系統通過文件類型常量來判斷ELF的真正文件類型,而非後綴名。REL(1)—可重定位文件,EXEC(2)—可執行文件,DYN(3)—共享目標文件。
機器類型:表示該ELF文件的平臺屬性,即可以在什麼平臺下運行。 -
段表:描述了ELF文件各個段的信息,如段名、段的長度、在文件中的偏移、讀寫權限等。主要確定段屬性的是段的類型和段的標誌位。
段類型
段標誌位 :表示該段在進程虛擬地址空間中的屬性,可讀可寫可執行,分配空間等。 -
重定位表:鏈接器在處理目標文件時,需對目標文件中的某些部位進行重定位,即代碼段和數據段那些絕對地址的引用的位置。重定位表裏記錄着這些重定位信息。
-
字符串表:由於字符串長度不固定,所以用固定結構來表示比較困難,所以把字符串集中起來存放到一個表,用字符串在表中的偏移來引用字符串。
.strtab :字符串表,保存普通的字符串。
.shstrtab :段表字符串表,保存段表中用到的字符串,如段名。
4. 鏈接
目標文件相互拼合實質是目標文件之間對地址的引用,即對函數和變量地址的引用。
- 符號:函數和變量。
- 符號名:函數名和變量名。
- 符號表:每一個目標文件都有一個對應的符號表,記錄了目標文件中用到的所有符號。
- 符號值:對於函數和變量而言,就是它們的地址。
ELF符號表結構
- Num:表示符號表數組的下標。
- Value:符號值。
- Size:符號大小。
- Type:符號類型。NOTYPE,未知類型;OBJECT,數據對象;FUNC,函數或其它可執行代碼;SECTION,段;FILE,文件。
- Bind:符號綁定信息。LOCAL,局部符號,對目標文件外的符號不可見;GLOBAL:全局符號,外部可見;WEAK:弱引用。
- Ndx:表示該符號所屬的段。
- Name:符號名稱。
函數簽名:用於識別不同的函數,包含了函數名、參數類型、所在類、名稱空間及其它。
弱符號和強符號:弱符號和強符號是針對定義來說,而非引用。
- 編譯器默認函數和已初始化的全局變量爲強符號,未初始化的全局變量爲弱符號。
- 可用__attributte__((weak))來定義任何一個強符號爲弱符號。
- 鏈接器處理多次定義的全局符號:
❤❤ 不允許強符號被多次定義,即不同的目標文件不能有相同的強符號;如果有,就報錯。
❤❤ 如果一個符號在某個目標文件中是強符號,其它是弱符號,那麼選擇強符號。
❤❤ 如果一個符號在所有目標文件中都是弱符號,選佔用空間最大的那個。
弱引用和強引用:強引用,鏈接器發現某個符號未定義就會報未定義的錯誤。弱引用,鏈接器發現某個符號未定義不報錯,默認其爲0或者一個特定的值。
- 可用__attributte__((weakref))來申明對一個外部函數的引用爲弱引用。