今天的內容「讀取 ELF section」是這系列 ELF 文章的重點戲。因為我們終於進入 ELF 的核心議題「節區的觀念」了。在 loader v0.5 以前的範例,都是屬於靜態的討論(linking view);在 loader v0.6 開始的討論中,我們將會開始提到動態的執行行為(execution view)。
如何讀取 ELF Section
我們分 2 個步驟來讀取 ELF 的 section 資訊:
1. 如圖一,一開始先讀取 section header table 的資訊。section header table 是所有 section 的紀錄表格,所有的 section 都要透過 section header table 才能得知其在檔案中的偏移位置(offset),如此一來才能讀取 section 的內容。
2. 如圖二,接著再根據 section header table 讀取檔案裡的每一個 section。
section header table 裡的 section 個數(section entries)紀錄於 ELF header 裡的 e_shnum 欄位,每個 section entry 的長度則是紀錄在 ELF header 的 e_shentsize 欄位,單位是bytes。
圖一:Section Header Table
ELF header 裡的 e_shoff 欄位,是 section header table 開始的檔案偏移位置 (file offset)。因此,我們只要由 e_shoff 偏移值開始讀取 e_shnum 個單位,每個單位為 e_shentsize(bytes),即可將整個 section header table 讀取出來。
圖二:Section Entries
SysV ABI 定義 section entry 的資料結構如下:
typedef struct { Elf32_Word sh_name; Elf32_Word sh_type; Elf32_Word sh_flags; Elf32_Addr sh_addr; Elf32_Off sh_offset; Elf32_Word sh_size; Elf32_Word sh_link; Elf32_Word sh_info; Elf32_Word sh_addralign; Elf32_Word sh_entsize; } Elf32_Shdr;
Section header table 是 Elf32_Shdr data type 的陣列,其元素個數為 e_shnum,我們可以透過索引Elf32_Shdr 陣列來讀取所有的 section,如圖二所示。
Section header(Elf32_Shdr)的欄位用途說明如下表。
Field | Description |
sh_name | section的名稱,需由string table查表取得。 |
sh_type | section的類型。 |
sh_flags | section的屬性。 |
sh_addr | section在記憶體裡的起始位址,但並非所有的section都會被載入至記憶體。 |
sh_offset | section在objct file裡的開始偏移值(offset),程式必須根據此欄位來讀取section的本文。 |
sh_size | section的長度(bytes)。 |
sh_link | 可用來存放section header table的index link。 |
sh_info | 可用來存放section的額外資訊。 |
sh_addralign | 紀錄section的address alignment,例如有些section的 alignment為DWORD(dobuleword)。 |
sh_entsize | 有些section的內容為entry長度固定的table,例如symbol table。此欄用來紀錄entry的長度,單位是bytes。 |
範例程式:loader v0.3
接著說明 loader v0.2 需要改寫的功能。
在主程式新增一個 parse_sections() 函數來讀取 ELF object file 的 section header table:
parse_sections(&f_header, fd);
parse_sections() 負責做2件工作:
1. 讀取 Section Header Table
2. 找出 Section Name String Table
同時,在主程式也要加入以下 2 個變數:
Elf32_Shdr header[40];
Elf32_Shdr *strtab; /* point to string table */
header[] 用來存放由 section header table 所讀取出來的所有 section entry,其型別為Elf32_Shdr;strtab 指標用來指向 section name string table,其型別為 Elf32_Shdr。Section name string table 是一個特殊的 section,下一個範例再來處理這個 section。
將檔案讀寫指標移到 e_shoff 的地方,準備開始讀取 section header table:
lseek(fd, hdr->e_shoff, SEEK_SET);
然後再利用最簡單的方式,一次一個將所有的section entry讀取出來,並且判斷該section entry是否為string table:
for (i = 0; i < hdr->e_shnum; i++) { read(fd, &header[i], sizeof(Elf32_Shdr)); /* find out string table ! */ if (header[i].sh_type == SHT_STRTAB) strtab = &header[i]; }
最後,section 用途是根據他的類型來區分的,這個部份留待下次再做說明。