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 helloELF Header:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64Data:2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type:EXEC (Executable file)Machine: Advanced Micro Devices X86-64Version: 0x1Entry point address:0x400750Start of program headers:64 (bytes into file)Start of section headers:65016 (bytes into file)Flags: 0x0Size of this header:64 (bytes)Size of program headers: 56 (bytes)Number of program headers: 9Size of section headers: 64 (bytes)Number of section headers: 37Section header string table index: 34
文件的最开始几个字节给出如何解释文件的提示信息。这些信息独立于处理器,也独立于文件中的其余内容。ELF Header 部分可以用以下的数据结构表示:
/* ELF Header */#define EI_NIDENT 16typedef 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_Offe_phoff;/* program header table offset */Elf32_Offe_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 helloElf file type is EXEC (Executable file)Entry point 0x400750There are 9 program headers, starting at offset 64Program Headers:Type Offset VirtAddr PhysAddrFileSiz MemSiz Flags AlignPHDR 0x0000000000000040 0x0000000000400040 0x00000000004000400x00000000000001f8 0x00000000000001f8 R E 8INTERP 0x0000000000000238 0x0000000000400238 0x00000000004002380x000000000000001c 0x000000000000001c R1[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]LOAD 0x0000000000000000 0x0000000000400000 0x00000000004000000x0000000000000ad4 0x0000000000000ad4 R E 200000LOAD 0x0000000000000df8 0x0000000000600df8 0x0000000000600df80x0000000000000268 0x0000000000000380 RW200000DYNAMIC 0x0000000000000e18 0x0000000000600e18 0x0000000000600e180x00000000000001e0 0x00000000000001e0 RW8NOTE 0x0000000000000254 0x0000000000400254 0x00000000004002540x0000000000000044 0x0000000000000044 R4GNU_EH_FRAME 0x0000000000000958 0x0000000000400958 0x00000000004009580x0000000000000044 0x0000000000000044 R4GNU_STACK0x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 RW10GNU_RELRO0x0000000000000df8 0x0000000000600df8 0x0000000000600df80x0000000000000208 0x0000000000000208 R1Section to Segment mapping:Segment Sections...0001.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 0708.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 helloThere are 37 section headers, starting at offset 0xfdf8:Section Headers:[Nr] Name Type Address OffsetSize EntSizeFlags Link Info Align[ 0] NULL 0000000000000000 000000000000000000000000 0000000000000000 000[ 1] .interp PROGBITS 0000000000400238 00000238000000000000001c 0000000000000000 A 001[ 2] .note.ABI-tagNOTE 0000000000400254 000002540000000000000020 0000000000000000 A 004[ 3] .note.gnu.build-i NOTE 0000000000400274 000002740000000000000024 0000000000000000 A 004[ 4] .gnu.hash GNU_HASH 0000000000400298 000002980000000000000030 0000000000000000 A 508[ 5] .dynsym DYNSYM 00000000004002c8 000002c80000000000000138 0000000000000018 A 618[ 6] .dynstr STRTAB 0000000000400400 000004000000000000000168 0000000000000000 A 001[ 7] .gnu.versionVERSYM 0000000000400568 00000568000000000000001a 0000000000000002 A 502[ 8] .gnu.version_r VERNEED0000000000400588 000005880000000000000040 0000000000000000 A 628[ 9] .rela.dyn RELA 00000000004005c8 000005c80000000000000030 0000000000000018 A 508[10] .rela.plt RELA 00000000004005f8 000005f800000000000000a8 0000000000000018 AI 5 248[11] .init PROGBITS 00000000004006a0 000006a0000000000000001a 0000000000000000 AX 004[12] .plt PROGBITS 00000000004006c0 000006c00000000000000080 0000000000000010 AX 0016[13] .plt.gotPROGBITS 0000000000400740 000007400000000000000008 0000000000000000 AX 008[14] .text PROGBITS 0000000000400750 0000075000000000000001e2 0000000000000000 AX 0016[15] .fini PROGBITS 0000000000400934 000009340000000000000009 0000000000000000 AX 004[16] .rodata PROGBITS 0000000000400940 000009400000000000000016 0000000000000000 A 004[17] .eh_frame_hdrPROGBITS 0000000000400958 000009580000000000000044 0000000000000000 A 004[18] .eh_frame PROGBITS 00000000004009a0 000009a00000000000000134 0000000000000000 A 008[19] .init_array INIT_ARRAY 0000000000600df8 00000df80000000000000010 0000000000000000 WA 008[20] .fini_array FINI_ARRAY 0000000000600e08 00000e080000000000000008 0000000000000000 WA 008[21] .jcr PROGBITS 0000000000600e10 00000e100000000000000008 0000000000000000 WA 008[22] .dynamicDYNAMIC0000000000600e18 00000e1800000000000001e0 0000000000000010 WA 608[23] .got PROGBITS 0000000000600ff8 00000ff80000000000000008 0000000000000008 WA 008[24] .got.pltPROGBITS 0000000000601000 000010000000000000000050 0000000000000008 WA 008[25] .data PROGBITS 0000000000601050 000010500000000000000010 0000000000000000 WA 008[26] .bss NOBITS 0000000000601060 000010600000000000000118 0000000000000000 WA 0032[27] .commentPROGBITS 0000000000000000 000010600000000000000034 0000000000000001 MS 001[28] .debug_aranges PROGBITS 0000000000000000 000010940000000000000030 0000000000000000 001[29] .debug_info PROGBITS 0000000000000000 000010c400000000000015ca 0000000000000000 001[30] .debug_abbrevPROGBITS 0000000000000000 0000268e00000000000003bf 0000000000000000 001[31] .debug_line PROGBITS 0000000000000000 00002a4d000000000000075e 0000000000000000 001[32] .debug_str PROGBITS 0000000000000000 000031ab0000000000009aad 0000000000000001 MS 001[33] .debug_macroPROGBITS 0000000000000000 0000cc580000000000002512 0000000000000000 001[34] .shstrtab STRTAB 0000000000000000 0000fc980000000000000159 0000000000000000 001[35] .symtab SYMTAB 0000000000000000 0000f17000000000000007b0 000000000000001836 568[36] .strtab STRTAB 0000000000000000 0000f9200000000000000378 0000000000000000 001Key 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 headerstring table section */Elf32_Word sh_type; /* type */Elf32_Word sh_flags; /* flags */Elf32_Addr sh_addr; /* address */Elf32_Offsh_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_Offsh_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;
三、参考
/xmphoenix/archive//10/23/2221879.html
http://bdxnote./blog/static/844423532911597959/
/wh8_/article/details/50812434
/a/1190000007103522?utm_source=tuicool&utm_medium=referral
如果觉得《ELF格式文件详细分析》对你有帮助,请点赞、收藏,并留下你的观点哦!