ELF相比Hex、Bin文件格式有哪些與眾不同?
素材來源:綜合CSDN
編輯整理:strongerHuang
關(guān)于計(jì)算機(jī)的文件有很多種,今天分享一種用于二進(jìn)制文件、可執(zhí)行文件、目標(biāo)代碼、共享庫(kù)和核心轉(zhuǎn)儲(chǔ)格式文件。
一、ELF文件簡(jiǎn)介
ELF:Executable and Linkable Format,可執(zhí)行與可鏈接格式。
首先,你需要知道的是所謂對(duì)象文件(Object files)有三個(gè)種類:
1)可重定向文件:文件保存著代碼和適當(dāng)?shù)臄?shù)據(jù),用來和其他的目標(biāo)文件一起來創(chuàng)建一個(gè)可執(zhí)行文件或者是一個(gè)共享目標(biāo)文件。(目標(biāo)文件或者靜態(tài)庫(kù)文件,即通常后綴為.a和.o的文件)
2)可執(zhí)行文件:文件保存著一個(gè)用來執(zhí)行的程序。(例如bash,gcc等)
3)共享目標(biāo)文件:共享庫(kù)。文件保存著代碼和合適的數(shù)據(jù),用來被下連接編輯器和動(dòng)態(tài)鏈接器鏈接。
二、ELF文件格式
首先,ELF文件格式提供了兩種視圖,分別是鏈接視圖和執(zhí)行視圖。
鏈接視圖是以節(jié)(section)為單位,執(zhí)行視圖是以段(segment)為單位。鏈接視圖就是在鏈接時(shí)用到的視圖,而執(zhí)行視圖則是在執(zhí)行時(shí)用到的視圖。上圖左側(cè)的視角是從鏈接來看的,右側(cè)的視角是執(zhí)行來看的。總個(gè)文件可以分為四個(gè)部分:
ELF header:描述整個(gè)文件的組織。
Program Header Table: 描述文件中的各種segments,用來告訴系統(tǒng)如何創(chuàng)建進(jìn)程映像的。
sections 或者 segments:segments是從運(yùn)行的角度來描述elf文件,sections是從鏈接的角度來描述elf文件,也就是說,在鏈接階段,我們可以忽略program header table來處理此文件,在運(yùn)行階段可以忽略section header table來處理此程序(所以很多加固手段刪除了section header table)。從圖中我們也可以看出,segments與sections是包含的關(guān)系,一個(gè)segment包含若干個(gè)section。
Section Header Table: 包含了文件各個(gè)segction的屬性信息,我們都將結(jié)合例子來解釋。
三、ELF Header
名稱 | 大小 | 對(duì)齊 | 目的 |
---|---|---|---|
Elf32_Addr | 4 | 4 | 無符號(hào)程序地址 |
Elf32_Half | 2 | 2 | 無符號(hào)中等整數(shù) |
Elf32_Off | 4 | 4 | 無符號(hào)文件偏移 |
Elf32_SWord | 4 | 4 | 有符號(hào)大整數(shù) |
Elf32_Word | 4 | 4 | 無符號(hào)大整數(shù) |
unsigned char | 1 | 1 | 無符號(hào)小整數(shù) |
#define EI_NIDENT 16
typedef struct {
unsigned char e_ident[EI_NIDENT];
ELF32_Half e_type;
ELF32_Half e_machine;
ELF32_Word e_version;
ELF32__Addr e_entry;
ELF32_Off e_phoff;
ELF32_Off e_shoff;
ELF32_Word e_flags;
ELF32_Half e_ehsize;
ELF32_Half e_phentsize;
ELF32_Half e_phnum;
ELF32_Half e_shentsize;
ELF32_Half e_shnum;
ELF32_Half e_shstrndx;
}Elf32_Ehdr;
e_ident :ELF的一些標(biāo)識(shí)信息,前四位為.ELF,其他的信息比如大小端等
e_machine :文件的目標(biāo)體系架構(gòu),比如ARM
e_version : 0為非法版本,1為當(dāng)前版本
e_entry :程序入口的虛擬地址
e_phoff :程序頭部表偏移地址
e_shoff :節(jié)區(qū)頭部表偏移地址
e_flags :保存與文件相關(guān)的,特定于處理器的標(biāo)志
e_ehsize :ELF頭的大小
e_phentsize :每個(gè)程序頭部表的大小
e_phnum :程序頭部表的數(shù)量
e_shentsize:每個(gè)節(jié)區(qū)頭部表的大小
e_shnum :節(jié)區(qū)頭部表的數(shù)量
e_shstrndx:節(jié)區(qū)字符串表位置
接著運(yùn)行readelf -h android_server命令,可以看到ELF Header結(jié)構(gòu)的內(nèi)容。
或者使用010Editor的ELF模板也可以看到ELF Header結(jié)構(gòu)。對(duì)比以下三類ELF文件,我們得到了以下結(jié)論:
1、e_type標(biāo)識(shí)了文件類型
2、Relocatable File(.o文件)不需要執(zhí)行,因此e_entry字段為0,且沒有Program Header Table等執(zhí)行視圖
3、不同類型的ELF文件的Section也有較大區(qū)別,比如只有Relocatable File有.strtab節(jié)。
Shared Object File(.so文件)
Executable File(可執(zhí)行文件android_server)
Relocatable File(.o文件)
四、Section Header Table
一個(gè)ELF文件中到底有哪些具體的 sections,由包含在這個(gè)ELF文件中的 section head table(SHT)決定。在SHT中,針對(duì)每一個(gè)section,都設(shè)置有一個(gè)條目(entry),用來描述對(duì)應(yīng)的這個(gè)section,其內(nèi)容主要包括該 section 的名稱、類型、大小以及在整個(gè)ELF文件中的字節(jié)偏移位置等等。我們也可以在TISCv1.2規(guī)范中找到SHT表中條目的C結(jié)構(gòu)定義:
typedef struct{
Elf32_Word sh_name; //節(jié)區(qū)名,是節(jié)區(qū)頭部字符串表節(jié)區(qū)(Section Header String Table Section)的索引。名字是一個(gè) NULL 結(jié)尾的字符串。
Elf32_Word sh_type; //為節(jié)區(qū)類型
Elf32_Word sh_flags; //節(jié)區(qū)標(biāo)志
Elf32_Addr sh_addr; //如果節(jié)區(qū)將出現(xiàn)在進(jìn)程的內(nèi)存映像中,此成員給出節(jié)區(qū)的第一個(gè)字節(jié)應(yīng)處的位置。否則,此字段為 0。
Elf32_Off sh_offset; //此成員的取值給出節(jié)區(qū)的第一個(gè)字節(jié)與文件頭之間的偏移。
Elf32_Word sh_size; //此 成 員 給 出 節(jié) 區(qū) 的 長(zhǎng) 度 ( 字 節(jié) 數(shù) )。
Elf32_Word sh_link; //此成員給出節(jié)區(qū)頭部表索引鏈接。其具體的解釋依賴于節(jié)區(qū)類型。
Elf32_Word sh_info; //此成員給出附加信息,其解釋依賴于節(jié)區(qū)類型。
Elf32_Word sh_addralign; //某些節(jié)區(qū)帶有地址對(duì)齊約束.
Elf32_Word sh_entsize; //某些節(jié)區(qū)中包含固定大小的項(xiàng)目,如符號(hào)表。對(duì)于這類節(jié)區(qū),此成員給出每個(gè)表項(xiàng)的長(zhǎng)度字節(jié)數(shù)。
}Elf32_Shdr;
sh_type的取值如下:
名稱 | 取值 | 說明 |
---|---|---|
SHT_NULL | 0 | 此值標(biāo)志節(jié)區(qū)頭部是非活動(dòng)的,沒有對(duì)應(yīng)的節(jié)區(qū)。此節(jié)區(qū)頭部中的其他成員取值無意義。 |
SHT_PROGBITS | 1 | 此節(jié)區(qū)包含程序定義的信息,其格式和含義都由程序來解釋。 |
SHT_SYMTAB | 2 | 此節(jié)區(qū)包含一個(gè)符號(hào)表。目前目標(biāo)文件對(duì)每種類型的節(jié)區(qū)都只能包含一個(gè),不過這個(gè)限制將來可能發(fā)生變化。一般,SHT_SYMTAB 節(jié)區(qū)提供用于鏈接編輯(指 ld 而言)的符號(hào),盡管也可用來實(shí)現(xiàn)動(dòng)態(tài)鏈接。 |
SHT_STRTAB | 3 | 此節(jié)區(qū)包含字符串表。目標(biāo)文件可能包含多個(gè)字符串表節(jié)區(qū)。 |
SHT_RELA | 4 | 此節(jié)區(qū)包含重定位表項(xiàng),其中可能會(huì)有補(bǔ)齊內(nèi)容(addend),例如 32 位目標(biāo)文件中的 Elf32_Rela 類型。目標(biāo)文件可能擁有多個(gè)重定位節(jié)區(qū)。 |
SHT_HASH | 5 | 此節(jié)區(qū)包含符號(hào)哈希表。所有參與動(dòng)態(tài)鏈接的目標(biāo)都必須包含一個(gè)符號(hào)哈希表。目前,一個(gè)目標(biāo)文件只能包含一個(gè)哈希表,不過此限制將來可能會(huì)解除。 |
SHT_DYNAMIC | 6 | 此節(jié)區(qū)包含動(dòng)態(tài)鏈接的信息。目前一個(gè)目標(biāo)文件中只能包含一個(gè)動(dòng)態(tài)節(jié)區(qū),將來可能會(huì)取消這一限制。 |
SHT_NOTE | 7 | 此節(jié)區(qū)包含以某種方式來標(biāo)記文件的信息。 |
SHT_NOBITS | 8 | 這 種 類 型 的 節(jié) 區(qū) 不 占 用 文 件 中 的 空 間 , 其 他 方 面 和SHT_PROGBITS 相似。盡管此節(jié)區(qū)不包含任何字節(jié),成員sh_offset 中還是會(huì)包含概念性的文件偏移 |
SHT_REL | 9 | 此節(jié)區(qū)包含重定位表項(xiàng),其中沒有補(bǔ)齊(addends),例如 32 位目標(biāo)文件中的 Elf32_rel 類型。目標(biāo)文件中可以擁有多個(gè)重定位節(jié)區(qū)。 |
SHT_SHLIB | 10 | 此節(jié)區(qū)被保留,不過其語(yǔ)義是未規(guī)定的。包含此類型節(jié)區(qū)的程序與 ABI 不兼容。 |
SHT_DYNSYM | 11 | 作為一個(gè)完整的符號(hào)表,它可能包含很多對(duì)動(dòng)態(tài)鏈接而言不必要的符號(hào)。因此,目標(biāo)文件也可以包含一個(gè) SHT_DYNSYM 節(jié)區(qū),其中保存動(dòng)態(tài)鏈接符號(hào)的一個(gè)最小集合,以節(jié)省空間。 |
SHT_LOPROC | 0X70000000 | 這一段(包括兩個(gè)邊界),是保留給處理器專用語(yǔ)義的。 |
SHT_HIPROC | OX7FFFFFFF | 這一段(包括兩個(gè)邊界),是保留給處理器專用語(yǔ)義的。 |
SHT_LOUSER | 0X80000000 | 此值給出保留給應(yīng)用程序的索引下界。 |
SHT_HIUSER | 0X8FFFFFFF | 此值給出保留給應(yīng)用程序的索引上界。 |
五、Section
名稱 | 類型 | 屬性 | 含義 |
.bss |
SHT_NOBITS |
SHF_ALLOC + |
包含將出現(xiàn)在程序的內(nèi)存映像中的為初始化數(shù)據(jù)。根據(jù)定義,當(dāng)程序開始執(zhí)行,系統(tǒng)將把這些數(shù)據(jù)初始化為 0。此節(jié)區(qū)不占用文件空間。 |
.comment |
SHT_PROGBITS |
(無) |
包含版本控制信息。 |
.data |
SHT_PROGBITS |
SHF_ALLOC + |
這些節(jié)區(qū)包含初始化了的數(shù)據(jù),將出現(xiàn)在程序的內(nèi)存映像中。 |
.data1 |
SHT_PROGBITS |
SHF_ALLOC + |
這些節(jié)區(qū)包含初始化了的數(shù)據(jù),將出現(xiàn)在程序的內(nèi)存映像中。 |
.debug |
SHT_PROGBITS |
(無) |
此節(jié)區(qū)包含用于符號(hào)調(diào)試的信息。 |
.dynamic |
SHT_DYNAMIC |
此節(jié)區(qū)包含動(dòng)態(tài)鏈接信息。節(jié)區(qū)的屬性將包含 SHF_ALLOC 位。是否 SHF_WRITE 位被設(shè)置取決于處理器。 |
|
.dynstr |
SHT_STRTAB |
SHF_ALLOC |
此節(jié)區(qū)包含用于動(dòng)態(tài)鏈接的字符串,大多數(shù)情況下這些字符串代表了與符號(hào)表項(xiàng)相關(guān)的名稱。 |
.dynsym |
SHT_DYNSYM |
SHF_ALLOC |
此節(jié)區(qū)包含了動(dòng)態(tài)鏈接符號(hào)表。 |
.fini |
SHT_PROGBITS |
SHF_ALLOC + |
此節(jié)區(qū)包含了可執(zhí)行的指令,是進(jìn)程終止代碼的一部分。程序正常退出時(shí),系統(tǒng)將安排執(zhí)行這里的代碼。 |
.got |
SHT_PROGBITS |
此節(jié)區(qū)包含全局偏移表。 |
|
.hash |
SHT_HASH |
SHF_ALLOC |
此節(jié)區(qū)包含了一個(gè)符號(hào)哈希表。 |
.init |
SHT_PROGBITS |
SHF_ALLOC + |
此節(jié)區(qū)包含了可執(zhí)行指令,是進(jìn)程初始化代碼的一部分。當(dāng)程序開始執(zhí)行時(shí),系統(tǒng)要在開始調(diào)用主程序入口之前(通常指 C 語(yǔ)言的 main 函數(shù))執(zhí)行這些代碼。 |
.interp |
SHT_PROGBITS |
此節(jié)區(qū)包含程序解釋器的路徑名。如果程序包含一個(gè)可加載的段,段中包含此節(jié)區(qū),那么節(jié)區(qū)的屬性將包含 SHF_ALLOC 位,否則該位為 0。 |
|
.line |
SHT_PROGBITS |
(無) |
此節(jié)區(qū)包含符號(hào)調(diào)試的行號(hào)信息,其中描述了源程序與機(jī)器指令之間的對(duì)應(yīng)關(guān)系。其內(nèi)容是未定義的。 |
.note |
SHT_NOTE |
(無) |
此節(jié)區(qū)中包含注釋信息,有獨(dú)立的格式。 |
.plt |
SHT_PROGBITS |
此節(jié)區(qū)包含過程鏈接表(procedure linkage table)。 |
|
.relname |
SHT_REL |
這些節(jié)區(qū)中包含了重定位信息。如果文件中包含可加載的段,段中有重定位內(nèi)容,節(jié)區(qū)的屬性將包含 SHF_ALLOC 位,否則該位置 0。傳統(tǒng)上 name 根據(jù)重定位所適用的節(jié)區(qū)給定。例如 .text 節(jié)區(qū)的重定位節(jié)區(qū)名字將是:.rel.text 或者 .rela.text。 |
|
.rodata |
SHT_PROGBITS |
SHF_ALLOC |
這些節(jié)區(qū)包含只讀數(shù)據(jù),這些數(shù)據(jù)通常參與進(jìn)程映像的不可寫段。 |
.shstrtab |
SHT_STRTAB |
此節(jié)區(qū)包含節(jié)區(qū)名稱。 |
|
.strtab |
SHT_STRTAB |
此節(jié)區(qū)包含字符串,通常是代表與符號(hào)表項(xiàng)相關(guān)的名稱。如果文件擁有一個(gè)可加載的段,段中包含符號(hào)串表,節(jié)區(qū)的屬性將包含SHF_ALLOC 位,否則該位為 0。 |
|
.symtab |
SHT_SYMTAB |
此節(jié)區(qū)包含一個(gè)符號(hào)表。如果文件中包含一個(gè)可加載的段,并且該段中包含符號(hào)表,那么節(jié)區(qū)的屬性中包含SHF_ALLOC 位,否則該位置為 0。 |
|
.text |
SHT_PROGBITS |
SHF_ALLOC + |
此節(jié)區(qū)包含程序的可執(zhí)行指令。 |
六、Program Header Table
程序頭部(Program Header)描述與程序執(zhí)行直接相關(guān)的目標(biāo)文件結(jié)構(gòu)信息。用來在文件中定位各個(gè)段的映像。同時(shí)包含其他一些用來為程序創(chuàng)建映像所必須的信息。
可執(zhí)行文件或者共享目標(biāo)文件的程序頭部是一個(gè)結(jié)構(gòu)數(shù)組,每個(gè)結(jié)構(gòu)描述了一個(gè)段或者系統(tǒng)準(zhǔn)備程序執(zhí)行所必須的其他信息。目標(biāo)文件的“段”包含一個(gè)或者多個(gè)“節(jié)區(qū)”,也就是“段內(nèi)容(Segment Contents)”。程序頭部?jī)H對(duì)可執(zhí)行文件和共享目標(biāo)文件有意義。
程序頭部的數(shù)據(jù)結(jié)構(gòu)如下:
typedef struct {
Elf32_Word p_type; //此數(shù)組元素描述的段的類型,或者如何解釋此數(shù)組元素的信息。
Elf32_Off p_offset; //此成員給出從文件頭到該段第一個(gè)字節(jié)的偏移
Elf32_Addr p_vaddr; //此成員給出段的第一個(gè)字節(jié)將被放到內(nèi)存中的虛擬地址
Elf32_Addr p_paddr; //此成員僅用于與物理地址相關(guān)的系統(tǒng)中。System V忽略所有應(yīng)用程序的物理地址信息。
Elf32_Word p_filesz; //此成員給出段在文件映像中所占的字節(jié)數(shù)??梢詾?。
Elf32_Word p_memsz; //此成員給出段在內(nèi)存映像中占用的字節(jié)數(shù)。可以為0。
Elf32_Word p_flags; //此成員給出與段相關(guān)的標(biāo)志。
Elf32_Word p_align; //此成員給出段在文件中和內(nèi)存中如何對(duì)齊。
} Elf32_phdr;
p_type:
名稱 | 取值 | 說明 |
---|---|---|
PT_NULL | 0 | 此數(shù)組元素未用。結(jié)構(gòu)中其他成員都是未定義的。 |
PT_LOAD | 1 | 此數(shù)組元素給出一個(gè)可加載的段,段的大小由 p_filesz 和 p_memsz描述。文件中的字節(jié)被映射到內(nèi)存段開始處。如果 p_memsz 大于p_filesz,“剩余”的字節(jié)要清零。p_filesz 不能大于 p_memsz??杉虞d的段在程序頭部表格中根據(jù) p_vaddr 成員按升序排列。 |
PT_DYNAMIC | 2 | 數(shù)組元素給出動(dòng)態(tài)鏈接信息。 |
PT_INTERP | 3 | 數(shù)組元素給出一個(gè) NULL 結(jié)尾的字符串的位置和長(zhǎng)度,該字符串將被當(dāng)作解釋器調(diào)用。這種段類型僅對(duì)與可執(zhí)行文件有意義(盡管也可能在共享目標(biāo)文件上發(fā)生)。在一個(gè)文件中不能出現(xiàn)一次以上。如果存在這種類型的段,它必須在所有可加載段項(xiàng)目的前面。 |
PT_NOTE | 4 | 此數(shù)組元素給出附加信息的位置和大小。 |
PT_SHLIB | 5 | 此段類型被保留,不過語(yǔ)義未指定。包含這種類型的段的程序與 ABI不符。 |
PT_PHDR | 6 | 此類型的數(shù)組元素如果存在,則給出了程序頭部表自身的大小和位置,既包括在文件中也包括在內(nèi)存中的信息。此類型的段在文件中不能出現(xiàn)一次以上。并且只有程序頭部表是程序的內(nèi)存映像的一部分時(shí)才起作用。如果存在此類型段,則必須在所有可加載段項(xiàng)目的前面。 |
PT_LOPROC~ PT_HIPROC |
0x70000000~ 0x7fffffff |
此范圍的類型保留給處理器專用語(yǔ)義。 |
好了,本文主要內(nèi)容就分享到這里,具體可以參看ELF文件詳細(xì)描述。
C++中字符編碼的轉(zhuǎn)換
手把手教你用STM32Trust生成加密固件
FreeRTOS如何減少RAM占用空間并加快執(zhí)行速度
長(zhǎng)按前往圖中包含的公眾號(hào)關(guān)注
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!