連結器、載入器與目的檔格式

ELF 標頭(ELF header)

如果 ELF 檔是一棟大樓,ELF 標頭就是大門旁的樓層導覽板:在檔案最開頭的一小段固定位元組,告訴你開始導覽所需的一切。當核心被要求執行一個檔案時,它讀的第一樣東西就是它;readelf 之類的工具剖析的第一樣東西也是它。沒有它,檔案其餘部分只是一堆沒有意義的位元組。

標頭是一個固定大小的結構(在 64 位元 ELF 中是 Elf64_Ehdr 結構,64 位元組)。它以一個 16 位元組的識別陣列 e_ident 開頭,其前四個位元組是魔術數字 0x7f 'E' 'L' 'F',接著是記錄類別(32 與 64 位元)、資料編碼(小端序與大端序)與版本的欄位。e_ident 之後是命名物件型別(可重定位 .o、可執行檔或共享物件)、目標機器架構、進入點位址 e_entry(執行開始的虛擬位址)的欄位,以及關鍵的兩對欄位:e_phoff 與 e_phnum 定位並計數程式標頭表,e_shoff 與 e_shnum 定位並計數節區標頭表。所以標頭本身不含程式碼或資料——它是一張地圖,指出那兩張大表在哪裡。

它重要,是因為它是唯一可信的起點。核心檢查魔術數字與類別,以判斷它在這台機器上能不能執行這個檔案,然後透過 e_phoff 找到要載入的區段。一個常見的混淆是把 e_entry 與 main() 搞混:e_entry 通常指向 C 執行期提供的一小段啟動常式(常是 _start),它先做好設定,之後才呼叫你的 main()。

// 64 位元 ELF 標頭,關鍵欄位 unsigned char e_ident[16]; // 7f 45 4c 46 02 01 01 ... uint16_t e_type; // ET_EXEC / ET_DYN / ET_REL uint64_t e_entry; // 第一條指令的虛擬位址 uint64_t e_phoff; // 程式標頭表的檔案偏移量 uint64_t e_shoff; // 節區標頭表的檔案偏移量

標頭不放程式碼;它放偏移量(e_phoff、e_shoff),指向程式標頭表與節區標頭表。

e_entry 是進入點,不是你的 main()。在典型的 glibc 程式裡它指向 _start,由 C 執行期提供;main() 是在設定完成後從那裡被呼叫的。

又稱
ELF file headerElf64_EhdrELF 檔頭