節概述
在ELF檔案中可以包含很多“節”(section),所有這些“節”都記錄在一張稱為“節頭表”(section header table)的數組裡。節頭表的每一個表項是一個 Elf32_Shdr 結構,通過每一個表項可以定位到對應的節。
在 ELF 檔案頭中, e_shoff 成員給出節頭表在 ELF 檔案中相對于檔案開始處的偏移量;e_shnum 成員指明節頭表中包含多少個表項;e_shentsize 成員指明了每一個表項的大小。
節頭表數組中,某些表項的索引值被保留,有特殊的含義。ELF 檔案的節頭表中不會出現索引值為以下各值的表項:
名字 | SHN_UNDEF | SHN_LORESERVE | SHN_LOPROC | SHN_HIPROC | SHN_ABS | SHN_COMMON | SHN_HIRESERVE |
---|---|---|---|---|---|---|---|
值 | 0xff00 | 0xff00 | 0xff1f | 0xfff1 | 0xfff2 | 0xffff |
SHN_UNDEF
該值被定義為 0,它表示一個未定義的、不存在的節的索引。盡管索引值 0 是一個未定義的保留值,但在節頭表中的索引還是會從 0 開始, 0 号表項的意義将在後面說明。
SHN_LORESERVE:被保留索引号區間的下限。
SHN_LOPROC:為特定處理器定制節所保留的索引号區間的下限。
SHN_HIPROC:為特定處理器定制節所保留的索引号區間的上限。
SHN_ABS:此節中所定義的符号有絕對的值,這個值不會因重定位而改變。
SHN_COMMON:此節中所定義的符号是公共的,比如 FORTRAN COMMON 符号或者未配置設定的 C 外部變量。
SHN_HIRESERVE:被保留索引号區間的上限。
除 0 以外,節頭表中所有保留的索引值都位于 SHN_LORESERVE ~SHN_HIRESERVE 範圍之間。與索引值”0”不同,這些大數值的索引并不出現在節頭表中。
通常,目标檔案中含有衆多的“節”,“節”區是檔案中最大的部分,它們需要滿足下列這些條件:
- ELf檔案中的每一個節一定對應有一個節頭,節頭中有對節的描述資訊;但有的節頭可以沒有對應的節,而隻是一個空的頭。
- 每一個節所占用的空間是連續的。
- 各個節之間不能互相重疊。
- 在ELF檔案中,節與節之間可能會存在一些多餘的位元組,這些位元組不屬于任何節。
節頭的結構
可以用以下這個資料結構體來描述節頭。
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;
各個成員的含義如下:
h_name:本節的名字。名字并不存儲在這裡,它僅是一個索引号,指向“字元串表”節中的某個位置,那裡存儲了一個節名字元串。
sh_type: 本節的類型。下表給出了所有的節類型。
名字 | 值 | 解釋說明 |
SHT_NULL | 此值表明本節頭是個無效的節頭,它也沒有對應的節。 | |
SHT_PROGBITS | 1 | 本節所含有的資訊的格式和含義都由程式來決定。 |
SHT_SYMTAB /SHT_DYNSYM | 2/11 | 這兩類節都含有符号表,隻能各包含一個這兩種節。SHT_SYMTAB 提供的符号用于在建立ELF檔案的時候編輯連接配接,在運作期間也有可能會用于動态連接配接 |
SHT_STRTAB | 3 | 本節是字元串表,ELF檔案中可包含多個字元串表節 |
SHT_RELA | 4 | 重定位節,一個ELF檔案可能含有多個重定位節 |
SHT_HASH | 5 | 本節包含一張哈希表,所有參與動态連接配接的ELF檔案都必須要包含一個符号哈希表。目前,一個ELF檔案中最多隻能有一個哈希表 |
SHT_DYNAMIC | 6 | 動态連接配接資訊。一個ELF檔案中最多隻能有一個 。 |
SHT_NOTE | 7 | 本節包含的資訊用于以某種方式來标記本檔案 |
SHT_NOBITS | 8 | 這一節的内容是空的,節并不占用實際的空間。 |
SHT_REL | 9 | 本節是一個重定位節 |
SHT_SHLIB | 10 | 此值是一個保留值,暫未指定語義。 |
SHT_LOPROC~SHT_HIPROC | 0x70000000~0x7fffffff | SHT_LOPROC~SHT_HIPROC區間是為特殊處理器節類型的保留值。 |
SHT_LOUSER~SHT_HIUSER | 0x80000000~0xffffffff | SHT_LOUSER~SHT_HIUSER,由應用程式自定義區間類型,是一段保留值。 |
sh_flags :本節的一些屬性,由一系列标志比特位組成,以下是這些标志位的清單及含義。
名字 | 值 | |
SHF_WRITE | 0x1 | 如果此标志被設定,表示本節所包含的内容在程序運作過程中是可寫的。 |
SHF_ALLOC | 0x2 | 如果此标志被設定,表示本節内容在程序運作過程中要占用記憶體單元。 |
SHF_EXECINSTR | 0x4 | 如果此标志被設定,表示此節内容是指令代碼。 |
SHF_MASHPROC | 0xf0000000 | 所有被此值所覆寫的位都是保留做特殊處理器擴充用的。 |
sh_addr:如果本節的内容需要映射到程序空間中去,此成員指定映射的起始位址;如果不需要映射,此值為 0。
sh_offset:指明了本節第一個位元組相對于檔案開頭的偏移量。機關是位元組。如果該節的類型為 SHT_NOBITS 的話,表明這一節的内容是空的,節并不占用實際的空間,這時 sh_offset 隻代表一個邏輯上的位置概念,并不代表實際的内容。
sh_size:指明節的大小,機關是位元組。如果該節的類型為 SHT_NOBITS,此值仍然可能為非零,但沒有實際的意義。
sh_link: 此成員是一個索引值,指向節頭表中本節所對應的位置。根據節的類型不同,本成員的意義也有所不同,具體見下表。
sh_info: 此成員含有此節的附加資訊,根據節的類型不同,本成員的意義也有所不同。
sh_type | sh_link | sh_info |
SHT_DYNAMIC | 本節中項目的字元串表在節頭表中相應的索引值 | |
SHT_HASH | 本節中哈希表的符号表在節頭表中相應的索引值 | |
SHT_REL /SHT_RELA | 相應符号表在節頭表中的索引值 | 本重定位節所應用到目标節在節頭表中的索引值 |
SHT_SYMTAB/SHT_DYNSYM | 相關字元串表的節頭索引 | 符号表中最後一個本地符号的索引值加 1 |
其它 | SHN_UNDEF |
sh_addralign
此成員指明本節内容如何對齊位元組,即該節的位址應該向多少個位元組對齊。也就是說,本節内容在程序空間中的映射位址 sh_addr 必須是一個向sh_addralign 對齊,即能被 sh_addralign 整除的值。目前,sh_addralign 隻能取 0、1或者 2 的正整數倍。如果該值為 0 或 1,表明本節沒有位元組對齊限制。
sh_entsize
有一些節的内容是一張表,其中每一個表項的大小是固定的,比如符号表。對于這種表來說,本成員指定其每一個表項的大小。如果此值為 0 則表明本節内容不是這種表格。
索引值0節頭項内容
下面說一下索引值為 0 的節頭表項,它并不表達實際的内容,隻是一個空的表項,内容見下表。
名字 | 值 | 意義 | 名字 | 值 | 意義 |
sh_name | 無名字 | sh_size | 無大小 | ||
sh_type | SHT_NULL | 非活動類型 | sh_link | SHN_UNDEF | 無節頭表對應項 |
sh_flags | 無标志 | sh_info | 無附加資訊 | ||
sh_addr | 無位址 | sh_addralign | 無對齊格式 | ||
sh_offset | 無檔案内偏移量 | sh_entsize | 無表項大小 |
特殊節
在 ELF 檔案中有一些特定的節是預定義好的,其内容是指令代碼或者控制資訊,這些節專門為作業系統使用。在建構可執行程式時,連接配接器(linker)可能需要把一些獨立的目标檔案和庫檔案連接配接在一起,在這個過程中,連接配接器要解析各個檔案中的互相引用,調整某些目标檔案中的絕對引用,并重定位指令碼。每種作業系統都有自己的一套連接配接模型,但總的來說,不外乎靜态和動态兩類:
靜态連接配接 :所有的目标檔案和動态連接配接庫被靜态地綁定在一起,所有的符号都被解析出來。所建立的目标檔案是完整的,運作時不依賴于任何外部的庫。
動态連接配接 :所有的目标檔案、系統共享資源以及共享庫以動态的形式連接配接在一起,外部庫的内容沒有完整地拷貝進來。如果建立的是可執行檔案的話,程式在運作的時候,在建構時所依賴的那些庫必須在系統中能找到,把它們一并裝載之後,程式才能運作起來。運作期間如何解析那些動态連接配接進來的符号引用,不同的系統有各自不同的方式。
動态連接配接過程所需要的資訊由.dynsym、.dynstr、.interp、.hash、.dynamic、.rel、.rela、.got、.plt 等節提供。.init 和.fini 節用于程序的初始化和終止過程。
名字 | 類型 | 屬性 | |
.bss | SHT_NOBITS | SHF_ALLOC+SHF_WRITE | 包含未初始化的全局變量。可執行程式在開始運作的時候,系統會把這一段内容清零。但是,在運作期間的 bss 段是由系統初始化而成的,在ELF檔案中.bss 節并不包含任何内容,其長度為 0,是以它的節類型為 SHT_NOBITS。 |
.comment | SHT_PROGBITS | 無 | 本節包含版本控制資訊。 |
.data .data1 | SHT_PROGBITS | SHF_ALLOC+SHF_WRITE | |
這兩個節用于存放程式中被初始化過的全局變量。在ELF檔案中,它們是占用實際的存儲空間的,與.bss 節不同。 | |||
.debug | SHT_PROGBITS | 無 | 調試資訊,内容格式沒有統一規定。 |
.dynamic | SHT_DYNAMIC | SHF_ALLOC+[SHF_WRITE] | 本節包含動态連接配接資訊 |
.dynstr | SHT_STRTAB | SHF_ALLOC | 含有用于動态連接配接的字元串,一般是那些與符号表相關的名字 |
.dynsym | SHT_DYNSYM | SHF_ALLOC | 此節含有動态連接配接符号表。 |
.fini | SHT_PROGBITS | SHF_ALLOC+SHF_EXECINSTR | 此節包含程序終止時要執行的程式指令。當程式正常退出時,系統會執行這一節中的代碼。 |
.got | SHT_PROGBITS | SHF_ALLOC+SHF_WRITE | 此節包含全局偏移量表。 |
.hash | SHT_HASH | SHF_ALLOC | 本節包含一張符号哈希表。 |
.init | SHT_PROGBITS | SHF_ALLOC+SHF_EXECINSTR | 此節包含程序初始化時要執行的程式指令。當程式開始運作時,系統會在進入主函數之前執行這一節中的代碼。 |
.interp | SHT_PROGBITS | [SHF_ALLOC] | 此節含有 ELF 程式解析器的路徑名。 |
.line | SHT_PROGBITS | 無 | 本節也是一個用于調試的節,它包含那些調試符号的行号,為程式指令碼與源檔案的行号建立起聯系。其内容格式沒有統一規定。 |
.note | SHT_NOTE | 無 | 注釋節較長的描述 |
.plt | SHT_PROGBITS | SHF_ALLOC+SHF_EXECINSTR | 此節包含函數連接配接表。 |
.relname .relaname | SHT_REL /SHT_RELA | [SHF_ALLOC] | 重定位資訊。注意,這兩個節的名字中”name”是可替換的部分,執照慣例,對哪一節做重定位就把”name”換成哪一節的名字。比如,.text 節的重定位節的名字将是.rel.text 或.rela.text。 |
.rodata .rodata1 | SHT_PROGBITS | SHF_ALLOC | |
本節包含程式中的隻讀資料,在程式裝載時,它們一般會被裝入程序空間中那些隻讀的段中去。 | |||
.shstrtab | SHT_STRTAB | 無 | 本節是“節名字表”,含有所有其它節的名字。 |
.strtab | SHT_STRTAB | [SHF_ALLOC] | 本節用于存放字元串,主要是那些符号表項的名字。 |
.symtab | SHT_SYMTAB | [SHF_ALLOC] | 本節用于存放符号表。 |
.text | SHT_PROGBITS | SHF_ALLOC+SHF_EXECINSTR | 本節包含程式指令代碼。 |
以點号”.”為字首的節名字是為系統保留的。應用程式也可以構造自己的段,但最好不要取與上述系統已定義的節相同的名字,也不要取以點号開頭的名字,以避免潛在的沖突。注意,目标檔案中節的名字并不具有唯一性,可以存在多個相同名字的節。