程式設計師的自我修養之ELF檔案格式

語言: CN / TW / HK

持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第5天,點選檢視活動詳情

這一系列是《程式設計師的自我修養》的閱讀筆記:

程式設計師的自我修養之程式的編譯和連結

程式設計師的自我修養之目標檔案裡有什麼

程式設計師的自我修養之ELF檔案格式

前言

我們已經大致瞭解了ELF檔案的輪廓,接著就來看看ELF檔案的結構格式。

image.png

ELF目標檔案格式的最前部是ELF檔案頭(ELF Header),緊接著是ELF檔案各個段。其中ELF檔案中與段有關的重要結構就是段表(Section Header Table),另外還有一些ELF中輔助的結構,比如字串表、符號表等。

檔案頭

我們可以用readelf命令來詳細檢視ELF檔案,通過-h指令來檢視ELF檔案頭。

$readelf -h hello.o

image.png

從上面輸出的結果可以看到,ELF的檔案頭中定義了ELF魔數、檔案機器位元組長度、資料儲存方式、版本、執行平臺、ABI版本、ELF重定位型別、硬體平臺、硬體平臺版本、入口地址、程式頭入口和長度、段表的位置和長度及段的數量等。

ELF檔案頭結構及相關常數被定義在“/usr/include/elf.h”裡,因為ELF檔案在各種平臺下都通用,ELF檔案有32位版本和64位版本。它的檔案頭結構也有這兩種版本,分別叫做“Elf32_Ehdr”和“Elf64_Ehdr”。

“elf.h”使用typedef定義了一套自己的變數體系,如下表所示。

image.png

64位版本的elf檔案頭結構定義如下:

image.png

可以看到readelf輸出的ELF檔案頭資訊和ELF檔案頭中的結構很多都一一對應。有點例外的是“Elf64_Ehdr”中的e_ident這個成員對應了readelf輸出結果中的“Magic”、“Class”、“Data”、“Version”、“OS/ABI”和“ABI Version”這6個引數,其他都一一對應。

| 成員 | readelf輸出結果與含義 | | --- | --- | | e_ident | Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0 | | e_type | Type: REL (Relocatable file)
ELF檔案型別|
| e_machine | Machine: Advanced Micro Devices X86-64
ELF檔案的CPU平臺屬性| | e_version | Version: 0x1
ELF版本號| | e_entry | Entry point address: 0x0
入口地址。作業系統在載入完程式後從這個地址開始載入程式的指令。可重定位檔案一般沒有入口地址,則這個值為0。| | e_shoff | Start of section headers: 760 (bytes into file)
段表在檔案中的偏移。值是760,表示段表是從761個位元組開始。|

段表

ELF檔案中有很多各種各樣的段,段表(SectionHeader Table)就是儲存這些段的基本屬性的結構。段表是ELF檔案中除了檔案頭以外最重要的結構,它描述了ELF的各個段的資訊,比如每個段的段名、段的長度、在檔案中的偏移、讀寫許可權及段的其他屬性。

使用readelf工具來檢視ELF檔案的段,除了之前我們瞭解到的 .text,.data,.bss段,它還有很多其他的輔助段。

$readelf -S hello.o

image.png

重定位表

連結器在處理目標檔案時,須要對目標檔案中某些部位進行重定位,即程式碼段和資料段中那些對絕對地址的引用的位置。這些重定位的資訊都記錄在ELF檔案的重定位表裡面。

hello.o段表中有一個.rela.text段,它的型別為 RELA,也就是說它是一個重定位表(Relocation Table)。

字串表

ELF檔案中用到了很多字串,比如段名、變數名等。因為字串的長度往往是不定的,所以用固定的結構來表示它比較困難。一種很常見的做法是把字串集中起來存放到一個表,然後使用字串在表中的偏移來引用字串。

一般字串表在ELF檔案中也以段的形式儲存,常見的段名為“.strtab”或“.shstrtab”。這兩個字串表分別為字串表(StringTable)和段表字符串表(Section Header String Table)。顧名思義,字串表用來儲存普通的字串,比如符號的名字;段表字符串表用來儲存段表中用到的字串,最常見的就是段名。