Linux[ELF]: ELF文件結(jié)構(gòu)簡單梳理
一.編譯過程-ELF文件生成
二.ELF的文件概述
2.1 4種ELF文件類型
| ELF文件類型 | 說明 | 實(shí)例 |
|---|---|---|
| Relocatable File | 包含例代碼和數(shù)據(jù),可以被鏈接成可執(zhí)行文件或共享目標(biāo)文件 | Linux下的.o文件 |
| Executable File | 包含可以直接執(zhí)行的程序,ELF可執(zhí)行文件,一般沒有擴(kuò)展名 | /bin/bash文件 |
| Shared Object File | 包含代碼和數(shù)據(jù),和其他目標(biāo)文件鏈接成新的目標(biāo)文件,和可執(zhí)行文件鏈接作為進(jìn)程映像的一部分來允許 | Linux下的.so文件 |
| Core Dump File | 進(jìn)程意外終止時(shí)可以產(chǎn)生的文件,存儲(chǔ)著該進(jìn)程的內(nèi)存空間中的內(nèi)容等信息 | Linux下的core dump |
2.2 ELF文件簡單結(jié)構(gòu)
兩種視圖:
三.ELF重要的結(jié)構(gòu)
3.1 文件結(jié)構(gòu)詳細(xì)學(xué)習(xí):
ELF文件格式解析: https://blog.csdn.net/dddxxxx/article/details/80347610
3.2 詳細(xì)結(jié)構(gòu)整理:
3.3 注意點(diǎn):
- 除了 ELF 頭部表以外,其他節(jié)區(qū)和段都沒有規(guī)定的順序
- 目標(biāo)文件中的每個(gè)節(jié)區(qū)都有對(duì)應(yīng)的節(jié)區(qū)頭部描述它,反過來,有節(jié)區(qū)頭部不意味著有節(jié)區(qū)
- 每個(gè)節(jié)區(qū)占用文件中一個(gè)連續(xù)字節(jié)區(qū)域(這個(gè)區(qū)域可能長度為 0)。
- 文件中的節(jié)區(qū)不能重疊,不允許一個(gè)字節(jié)存在于兩個(gè)節(jié)區(qū)中的情況發(fā)生。
- 目標(biāo)文件中可能包含非活動(dòng)空間(INACTIVE SPACE)。這些區(qū)域不屬于任何頭部和節(jié)區(qū),其內(nèi)容未指定。
- 以“.”開頭的節(jié)區(qū)名稱是系統(tǒng)保留的。應(yīng)用程序可以使用沒有前綴的節(jié)區(qū)名稱,以避免與系統(tǒng)節(jié)區(qū)沖突。
- 目標(biāo)文件中也可以包含多個(gè)名字相同的節(jié)區(qū)。
- Section和Segment的區(qū)別和聯(lián)系
可執(zhí)行文件中,一個(gè)program header描述的內(nèi)容稱為一個(gè)段(segment)。Segment包含一個(gè)或者多個(gè)section - 可執(zhí)行程序中的幾個(gè)段:
| 名稱 | 內(nèi)容 |
|---|---|
| 代碼段 | 可執(zhí)行代碼、字符串常量 |
| 數(shù)據(jù)段 | 已初始化全局變量、已初始化全局靜態(tài)變量、局部靜態(tài)變量、常量數(shù)據(jù) |
| BSS段 | 未初始化全局變量,未初始化全局靜態(tài)變量 |
| 而當(dāng)程序被加載到內(nèi)存單元時(shí),則需要另外兩個(gè)域:堆域和棧域 | |
| 棧 | 局部變量、函數(shù)參數(shù) |
| 堆 | 動(dòng)態(tài)內(nèi)存分配 |
3.4 section類型:
| 名稱 | 類型 | 屬性 | 含義 |
|---|---|---|---|
| .bss | SHT_NOBITS | SHF_ALLOC SHF_WRITE | 包含將出現(xiàn)在程序的內(nèi)存映像中的為初始化數(shù)據(jù)。根據(jù)定義,當(dāng)程序開始執(zhí)行,系統(tǒng)將把這些數(shù)據(jù)初始化為 0。此節(jié)區(qū)不占用文件空間。 |
| .data | SHT_PROGBITS | (無) | 包含版本控制信息。 |
| .data1 | SHT_PROGBITS | SHF_ALLOC SHF_WRITE | 這些節(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_ALLOCSHF_EXECINSTR | 此節(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_ALLOCSHF_EXECINSTR | 此節(jié)區(qū)包含了可執(zhí)行指令,是進(jìn)程初始化代碼的一部分。當(dāng)程序開始執(zhí)行時(shí),系統(tǒng)要在SHF_EXECINSTR 開始調(diào)用主程序入口之前(通常指 C 語言的 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。 | |
| .rela name | SHT_RELA | ||
| .rodata | SHT_PROGBITS | SHF_ALLOC | 這些節(jié)區(qū)包含只讀數(shù)據(jù),這些數(shù)據(jù)通常參與進(jìn)程映像的不可寫段。 |
| .rodata1 | SHT_PROGBITS | SHF_ALLOC | |
| .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 | 此節(jié)區(qū)包含程序的可執(zhí)行指令。 |
3.5 segment類型:
| 程序段類型 | 取值 | 說明 |
|---|---|---|
| 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)鏈接信息。Dynamic Segment 是很重要的一個(gè)程序頭,里面存儲(chǔ)著函數(shù)名、使用過的動(dòng)態(tài)庫名、重定位表、函數(shù)代碼偏移等重要信息,不過不是直接記錄,而是通過一定的方法查詢得到,這個(gè)查詢過程是elf設(shè)計(jì)中巧妙且關(guān)鍵的核心所在。 |
| PT_INTERP | 3 | 該記錄的是鏈接器linker的路徑.數(shù)組元素給出一個(gè) NULL 結(jié)尾的字符串的位置和長度,該字符串將被當(dāng)作解釋器調(diào)用。這種段類型僅對(duì)與可執(zhí)行文件有意義(盡管也可能在共享目標(biāo)文件上發(fā)生)。在一個(gè)文件中不能出現(xiàn)一次以上。如果存在這種類型的段,它必須在所有可加載段項(xiàng)目的前面。 |
| PT_NOTE | 4 | 此數(shù)組元素給出附加信息的位置和大小。 |
| PT_SHLIB | 5 | 此段類型被保留,不過語義未指定。包含這種類型的段的程序與 ABI 不符。 |
| PT_PHDR | 6 | 此類型的數(shù)組元素如果存在,則給出了程序頭部表自身的大小和位置,既包括在文件中也包括在內(nèi)存中的信息。此類型的段在文件中不能出現(xiàn)一次以上。并且只有程序頭部表是程序的內(nèi)存映像的一部分時(shí)才起作用。如果存在此類型段,則必須在所有可加載段項(xiàng)目的前面。 |
| PT_LOPROC | 0x70000000 | 此范圍的類型保留給處理器專用語義。 |
| PT_HIPROC | 0x7ffffffff |
四. 舉個(gè)例子
- Cpp代碼:
//abc.cpp
#include<iostream>
using namespace std;
int main(){
int sum=0,value=0;
while(cin>>value){
sum+=value;
cout<<"sum:"<<sum;
}
return 0;
}
-
g++ abc.cpp生成.out文件,file看一下是ELF文件類型:
在這里插入圖片描述 -
使用readelf命令簡單查看該a.out這個(gè)elf文件的program header,可以看出一個(gè)segment對(duì)應(yīng)著多個(gè)section,readelf加參數(shù)可以查看更多其他信息:
在這里插入圖片描述
當(dāng)ELF文件被加載到內(nèi)存之后,系統(tǒng)會(huì)將多個(gè)具有相同權(quán)限的section合并到一個(gè)segment。
操作系統(tǒng)往往以頁為基本單位來管理內(nèi)存分配,一般頁的大小位4KB。同時(shí),內(nèi)存的權(quán)限管理的粒度也是以頁為單位,頁內(nèi)的內(nèi)存是具有同樣的權(quán)限,并且操作系統(tǒng)對(duì)內(nèi)存的管理往往追求高效和高利用率這樣的目標(biāo)。
ELF文件在被映射時(shí),是以系統(tǒng)的頁長度位單位的,那么每個(gè)section在映射時(shí)的長度都是系統(tǒng)頁長度的整數(shù)倍,如果section的長度不是其整數(shù)倍,則導(dǎo)致多余部分也將占用一個(gè)頁。
而我們從上面的例子中知道,一個(gè)ELF文件具有很多section,那么會(huì)導(dǎo)致內(nèi)存浪費(fèi)。將sections映射到segment可以減少頁面內(nèi)部的碎片,節(jié)省了空間,顯著提高內(nèi)存利用率。
詳細(xì)參考:
Linux ELF文件格式分析 https://blog.csdn.net/xj178926426/article/details/72825630
程序或-內(nèi)存區(qū)域分配(五個(gè)段)--終于搞明白了:https://blog.csdn.net/love_gaohz/article/details/41310597
C程序內(nèi)存區(qū)域分配(5個(gè)段作用): http://www.cnblogs.com/bigbigtree/archive/2012/11/23/2784137.html
學(xué)習(xí)資料:
specific 中英文版及 Learning Linux Binary Analysis 英文版百度云盤下載:
鏈接: https://pan.baidu.com/s/1vt5clx862dqmelYP8PWyLA 提取碼: amgs
