天天看點

ELF檔案系列第三篇ELF檔案靜态結構中的節

節概述

在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 本節包含程式指令代碼。

以點号”.”為字首的節名字是為系統保留的。應用程式也可以構造自己的段,但最好不要取與上述系統已定義的節相同的名字,也不要取以點号開頭的名字,以避免潛在的沖突。注意,目标檔案中節的名字并不具有唯一性,可以存在多個相同名字的節。

繼續閱讀