ELF(Executable and Linkable Format)是一種對象文件的格式,用於定義不同類型的對象文件(Object files)中都放了什麼東西、以及都以什麼樣的格式去放這些東西。
一、簡介
1、分類
可重定位文件(Relocatable File) .o)包含適合於與其他目標文件鏈接來創建可執行文件或者共享目標文件的代碼和數據。
可執行文件(Executable File) .exe) 包含適合於執行的一個程序,此文件規定了exec() 如何創建一個程序的進程映像。
共享目標文件(Shared Object File) .so) 包含可在兩種上下文中鏈接的代碼和數據。
- 首先鏈接編輯器可以將它和其它可重定位文件和共享目標文件一起處理, 生成另外一個目標文件。
- 其次動態鏈接器(Dynamic Linker)可能將它與某 個可執行文件以及其它共享目標一起組合,創建進程映像。
2、作用
ELF文件參與程序的連接(建立一個程序)和程序的執行(運行一個程序),所以可以從不同的角度來看待elf格式的文件:
如果用於編譯和鏈接(可重定位文件),則編譯器和鏈接器將把elf文件看作是節頭表描述的節的集合,程序頭表可選。
如果用於加載執行(可執行文件),則加載器則將把elf文件看作是程序頭表描述的段的集合,一個段可能包含多個節,節頭表可選。
如果是共享文件,則兩者都含有。
二、格式分析
1、源文件
//============================================================================
// Name : hello.cpp
// Author :
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <iostream>
using namespace std;
int main() {
cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
return 0;
}
$g++ hello.cpp -o hello
2、ELF頭部
$ readelf -h hello
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400750
Start of program headers: 64 (bytes into file)
Start of section headers: 65016 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 37
Section header string table index: 34
文件的最開始幾個字節給出如何解釋文件的提示信息。這些信息獨立於處理器,也獨立於文件中的其餘內容。ELF Header 部分可以用以下的數據結構表示:
/* ELF Header */
#define EI_NIDENT 16
typedef struct elfhdr {
unsigned char e_ident[EI_NIDENT]; /* ELF Identification */
Elf32_Half e_type; /* object file type */
Elf32_Half e_machine; /* machine */
Elf32_Word e_version; /* object file version */
Elf32_Addr e_entry; /* virtual entry point */
Elf32_Off e_phoff; /* program header table offset */
Elf32_Off e_shoff; /* section header table offset */
Elf32_Word e_flags; /* processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size */
Elf32_Half e_phentsize; /* program header entry size */
Elf32_Half e_phnum; /* number of program header entries */
Elf32_Half e_shentsize; /* section header entry size */
Elf32_Half e_shnum; /* number of section header entries */
Elf32_Half e_shstrndx; /* section header table's "section
header string table" entry offset */
} Elf32_Ehdr;
typedef struct {
unsigned char e_ident[EI_NIDENT]; /* Id bytes */
Elf64_Quarter e_type; /* file type */
Elf64_Quarter e_machine; /* machine type */
Elf64_Half e_version; /* version number */
Elf64_Addr e_entry; /* entry point */
Elf64_Off e_phoff; /* Program hdr offset */
Elf64_Off e_shoff; /* Section hdr offset */
Elf64_Half e_flags; /* Processor flags */
Elf64_Quarter e_ehsize; /* sizeof ehdr */
Elf64_Quarter e_phentsize; /* Program header entry size */
Elf64_Quarter e_phnum; /* Number of program headers */
Elf64_Quarter e_shentsize; /* Section header entry size */
Elf64_Quarter e_shnum; /* Number of section headers */
Elf64_Quarter e_shstrndx; /* String table index */
} Elf64_Ehdr;
e_ident 數組給出了 ELF 的一些標識信息,這個數組中不同下標的含義如表所示:
這些索引訪問包含以下數值的字節:
e_ident[EI_MAG0]~e_ident[EI_MAG3]
即e_ident[0]~e_ident[3]
被稱爲魔數(Magic Number),其值一般爲0x7f,'E','L','F'
。
e_ident[EI_CLASS](即e_ident[4])
識別目標文件運行在目標機器的類別,取值可爲三種值:ELFCLASSNONE(0)
非法類別;ELFCLASS32(1)
32位目標;ELFCLASS64(2)
64位目標。
e_ident[EI_DATA](即e_ident[5])
:給出處理器
特定數據的數據編碼方式。即大端還是小端方式。取值可爲3種:ELFDATANONE(0)
非法數據編碼;ELFDATA2LSB(1)
高位在前;ELFDATA2MSB(2)
低位在前。
ELF Header 中各個字段的說明如表:
3、程序頭部(Program Header)
$ readelf -l hello
Elf file type is EXEC (Executable file)
Entry point 0x400750
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000ad4 0x0000000000000ad4 R E 200000
LOAD 0x0000000000000df8 0x0000000000600df8 0x0000000000600df8
0x0000000000000268 0x0000000000000380 RW 200000
DYNAMIC 0x0000000000000e18 0x0000000000600e18 0x0000000000600e18
0x00000000000001e0 0x00000000000001e0 RW 8
NOTE 0x0000000000000254 0x0000000000400254 0x0000000000400254
0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x0000000000000958 0x0000000000400958 0x0000000000400958
0x0000000000000044 0x0000000000000044 R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x0000000000000df8 0x0000000000600df8 0x0000000000600df8
0x0000000000000208 0x0000000000000208 R 1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got
可執行文件或者共享目標文件的程序頭部是一個結構數組,每個結構描述了一個段 或者系統準備程序執行所必需的其它信息。目標文件的“段”包含一個或者多個“節區”, 也就是“段內容(Segment Contents)”。程序頭部僅對於可執行文件和共享目標文件 有意義。 可執行目標文件在 ELF 頭部的 e_phentsize
和e_phnum
成員中給出其自身程序頭部 的大小。程序頭部的數據結構:
/* Program Header */
typedef struct {
Elf32_Word p_type; /* segment type */
Elf32_Off p_offset; /* segment offset */
Elf32_Addr p_vaddr; /* virtual address of segment */
Elf32_Addr p_paddr; /* physical address - ignored? */
Elf32_Word p_filesz; /* number of bytes in file for seg. */
Elf32_Word p_memsz; /* number of bytes in mem. for seg. */
Elf32_Word p_flags; /* flags */
Elf32_Word p_align; /* memory alignment */
} Elf32_Phdr;
typedef struct {
Elf64_Half p_type; /* entry type */
Elf64_Half p_flags; /* flags */
Elf64_Off p_offset; /* offset */
Elf64_Addr p_vaddr; /* virtual address */
Elf64_Addr p_paddr; /* physical address */
Elf64_Xword p_filesz; /* file size */
Elf64_Xword p_memsz; /* memory size */
Elf64_Xword p_align; /* memory & file alignment */
} Elf64_Phdr;
其中各個字段說明:
- p_type 此數組元素描述的段的類型,或者如何解釋此數組元素的信息。具體如下圖。
- p_offset 此成員給出從文件頭到該段第一個字節的偏移。
- p_vaddr 此成員給出段的第一個字節將被放到內存中的虛擬地址。
- p_paddr 此成員僅用於與物理地址相關的系統中。因爲 System V 忽略所有應用程序的物理地址信息,此字段對與可執行文件和共享目標文件而言具體內容是指定的。
- p_filesz 此成員給出段在文件映像中所佔的字節數。可以爲 0。
- p_memsz 此成員給出段在內存映像中佔用的字節數。可以爲 0。
- p_flags 此成員給出與段相關的標誌。
- p_align 可加載的進程段的 p_vaddr 和 p_offset 取值必須合適,相對於對頁面大小的取模而言。此成員給出段在文件中和內存中如何 對齊。數值 0 和 1 表示不需要對齊。否則 p_align 應該是個正整數,並且是 2 的冪次數,p_vaddr 和 p_offset 對 p_align 取模後應該相等。
可執行 ELF 目標文件中的段類型:
4、節區(Sections)
$ readelf -S hello
There are 37 section headers, starting at offset 0xfdf8:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000400238 00000238
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000400254 00000254
0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.build-i NOTE 0000000000400274 00000274
0000000000000024 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400298 00000298
0000000000000030 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 00000000004002c8 000002c8
0000000000000138 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 0000000000400400 00000400
0000000000000168 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 0000000000400568 00000568
000000000000001a 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000400588 00000588
0000000000000040 0000000000000000 A 6 2 8
[ 9] .rela.dyn RELA 00000000004005c8 000005c8
0000000000000030 0000000000000018 A 5 0 8
[10] .rela.plt RELA 00000000004005f8 000005f8
00000000000000a8 0000000000000018 AI 5 24 8
[11] .init PROGBITS 00000000004006a0 000006a0
000000000000001a 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 00000000004006c0 000006c0
0000000000000080 0000000000000010 AX 0 0 16
[13] .plt.got PROGBITS 0000000000400740 00000740
0000000000000008 0000000000000000 AX 0 0 8
[14] .text PROGBITS 0000000000400750 00000750
00000000000001e2 0000000000000000 AX 0 0 16
[15] .fini PROGBITS 0000000000400934 00000934
0000000000000009 0000000000000000 AX 0 0 4
[16] .rodata PROGBITS 0000000000400940 00000940
0000000000000016 0000000000000000 A 0 0 4
[17] .eh_frame_hdr PROGBITS 0000000000400958 00000958
0000000000000044 0000000000000000 A 0 0 4
[18] .eh_frame PROGBITS 00000000004009a0 000009a0
0000000000000134 0000000000000000 A 0 0 8
[19] .init_array INIT_ARRAY 0000000000600df8 00000df8
0000000000000010 0000000000000000 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000000600e08 00000e08
0000000000000008 0000000000000000 WA 0 0 8
[21] .jcr PROGBITS 0000000000600e10 00000e10
0000000000000008 0000000000000000 WA 0 0 8
[22] .dynamic DYNAMIC 0000000000600e18 00000e18
00000000000001e0 0000000000000010 WA 6 0 8
[23] .got PROGBITS 0000000000600ff8 00000ff8
0000000000000008 0000000000000008 WA 0 0 8
[24] .got.plt PROGBITS 0000000000601000 00001000
0000000000000050 0000000000000008 WA 0 0 8
[25] .data PROGBITS 0000000000601050 00001050
0000000000000010 0000000000000000 WA 0 0 8
[26] .bss NOBITS 0000000000601060 00001060
0000000000000118 0000000000000000 WA 0 0 32
[27] .comment PROGBITS 0000000000000000 00001060
0000000000000034 0000000000000001 MS 0 0 1
[28] .debug_aranges PROGBITS 0000000000000000 00001094
0000000000000030 0000000000000000 0 0 1
[29] .debug_info PROGBITS 0000000000000000 000010c4
00000000000015ca 0000000000000000 0 0 1
[30] .debug_abbrev PROGBITS 0000000000000000 0000268e
00000000000003bf 0000000000000000 0 0 1
[31] .debug_line PROGBITS 0000000000000000 00002a4d
000000000000075e 0000000000000000 0 0 1
[32] .debug_str PROGBITS 0000000000000000 000031ab
0000000000009aad 0000000000000001 MS 0 0 1
[33] .debug_macro PROGBITS 0000000000000000 0000cc58
0000000000002512 0000000000000000 0 0 1
[34] .shstrtab STRTAB 0000000000000000 0000fc98
0000000000000159 0000000000000000 0 0 1
[35] .symtab SYMTAB 0000000000000000 0000f170
00000000000007b0 0000000000000018 36 56 8
[36] .strtab STRTAB 0000000000000000 0000f920
0000000000000378 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
節區中包含目標文件中的所有信息,除了:ELF 頭部、程序頭部表格、節區頭部表格。節區滿足以下條件:
- 目標文件中的每個節區都有對應的節區頭部描述它,反過來,有節區頭部不意 味着有節區。
- 每個節區佔用文件中一個連續字節區域(這個區域可能長度爲 0)。
- 文件中的節區不能重疊,不允許一個字節存在於兩個節區中的情況發生。
- 目標文件中可能包含非活動空間(INACTIVE SPACE)。這些區域不屬於任何頭部和節區,其內容指定。
節區頭部表格
ELF 頭部中,e_shoff 成員給出從文件頭到節區頭部表格的偏移字節數;e_shnum 給出表格中條目數目;e_shentsize 給出每個項目的字節數。從這些信息中可以確切地定 位節區的具體位置、長度。
每個節區頭部數據結構描述:
/* Section Header */
typedef struct {
Elf32_Word sh_name; /* name - index into section header
string table section */
Elf32_Word sh_type; /* type */
Elf32_Word sh_flags; /* flags */
Elf32_Addr sh_addr; /* address */
Elf32_Off sh_offset; /* file offset */
Elf32_Word sh_size; /* section size */
Elf32_Word sh_link; /* section header table index link */
Elf32_Word sh_info; /* extra information */
Elf32_Word sh_addralign; /* address alignment */
Elf32_Word sh_entsize; /* section entry size */
} Elf32_Shdr;
typedef struct {
Elf64_Half sh_name; /* section name */
Elf64_Half sh_type; /* section type */
Elf64_Xword sh_flags; /* section flags */
Elf64_Addr sh_addr; /* virtual address */
Elf64_Off sh_offset; /* file offset */
Elf64_Xword sh_size; /* section size */
Elf64_Half sh_link; /* link to another */
Elf64_Half sh_info; /* misc info */
Elf64_Xword sh_addralign; /* memory alignment */
Elf64_Xword sh_entsize; /* table entry size */
} Elf64_Shdr;
各個字段的解釋如下:
索引爲零(SHN_UNDEF)的節區頭部是存在的,儘管此索引標記的是未定義的節區應用。這個節區固定爲:
sh_type 字段 節區類型定義:
sh_flags 字段定義了一個節區中包含的內容是否可以修改、是否可以執行等信息。 如果一個標誌位被設置,則該位取值爲 1。 定義的各位都設置爲 0。
其中已經定義了的各位含義如下:
- SHF_WRITE: 節區包含進程執行過程中將可寫的數據。
- SHF_ALLOC: 此節區在進程執行過程中佔用內存。某些控制節區- 並不出現於目標文件的內存映像中,對於那些節區,此位應設置爲 0。
- SHF_EXECINSTR: 節區包含可執行的機器指令。
- SHF_MASKPROC: 所有包含於此掩碼中的四位都用於處理器專用的語義。
根據節區類型的不同,sh_link 和 sh_info 的具體含義也有所不同:
特殊節區
系統使用的節區,以及它們 的類型和屬性:
在分析這些節區的時候,需要注意如下事項:
- 以“.”開頭的節區名稱是系統保留的。應用程序可以使用沒有前綴的節區名稱,以避 免與系統節區衝突。
- 目標文件格式允許人們定義不在上述列表中的節區。
- 目標文件中也可以包含多個名字相同的節區。
- 保留給處理器體系結構的節區名稱一般構成爲:處理器體系結構名稱簡寫 + 節區名稱。
- 處理器名稱應該與 e_machine 中使用的名稱相同。例如 .FOO.psect 街區是由FOO 體系結構定義的 psect 節區。
5、字符串表(String Table)
字符串表節區包含以 NULL(ASCII 碼 0)結尾的字符序列,通常稱爲字符串。ELF 目標文件通常使用字符串來表示符號和節區名稱。對字符串的引用通常以字符串在字符 串表中的下標給出。
一般,第一個字節(索引爲 0)定義爲一個空字符串。類似的,字符串表的最後一 個字節也定義爲 NULL,以確保所有的字符串都以 NULL 結尾。索引爲 0 的字符串在 不同的上下文中可以表示無名或者名字爲 NULL 的字符串。
允許存在空的字符串表節區,其節區頭部的 sh_size 成員應該爲 0。對空的字符串 表而言,非 0 的索引值是非法的。
在使用、分析字符串表時,要注意以下幾點:
- 字符串表索引可以引用節區中任意字節。
- 字符串可以出現多次
- 可以存在對子字符串的引用
- 同一個字符串可以被引用多次。
- 字符串表中也可以存在未引用的字符串。
6、符號表(Symbol Table)
目標文件的符號表中包含用來定位、重定位程序中符號定義和引用的信息。符號表 索引是對此數組的索引。索引 0 表示表中的第一表項,同時也作爲 定義符號的索引。
/* Symbol Table Entry */
typedef struct elf32_sym {
Elf32_Word st_name; /* name - index into string table */
Elf32_Addr st_value; /* symbol value */
Elf32_Word st_size; /* symbol size */
unsigned char st_info; /* type and binding */
unsigned char st_other; /* 0 - no defined meaning */
Elf32_Half st_shndx; /* section header index */
} Elf32_Sym;
typedef struct {
Elf64_Half st_name; /* Symbol name index in str table */
Elf_Byte st_info; /* type / binding attrs */
Elf_Byte st_other; /* unused */
Elf64_Quarter st_shndx; /* section index of symbol */
Elf64_Xword st_value; /* value of symbol */
Elf64_Xword st_size; /* size of symbol */
} Elf64_Sym;
三、參考
http://www.cnblogs.com/xmphoenix/archive/2011/10/23/2221879.html
http://bdxnote.blog.163.com/blog/static/8444235201532911597959/
http://blog.csdn.net/wh8_2011/article/details/50812434
https://segmentfault.com/a/1190000007103522?utm_source=tuicool&utm_medium=referral