本文参考链接: https://segmentfault.com/a/1190000007103522?utm_source=tuicool&utm_medium=referral
非原创。 在学习ELF的过程中记录自己学习的历程。
参考数据: LINUX二进制分析
一个ELF文件可以被标记为以下几种类型
名词 | 属性 | 注解 |
---|---|---|
ET_NONE | 未知类型 | 这个标记表明了文件类型不确定或者还未定义 |
ET_REL | 重定位文件 | 如果elf文件标记为了relocatable那么意味着该文件被标记为了一段可冲定位的代码,也称为目标文件。可重定位目标文件通常是还未被链接到可执行程序的一段位置独立代码的代码(position independent code)。在编译完代码之后通常可以看到一个.o的格式的文件,这种文件包含了创建可执行文件所需要的代码和数据。 |
ET_EXEC | 可执行文件 | elf文件如果为executable,那么表明了这个文件被标记为了可执行文件。这种类型的文件也成为程序,是一个进程开始执行的入口。 |
ET_DYN | 共享目标文件 | ELF类型如果为dynamic意味着该文件被标记为了一个动态的可链接的目标文件,也称为共享库。这类共享库会在程序运行的时候被装载并且链接到程序的进程镜像中。 |
ET_CORE | 核心文件 | 在进程崩溃或者进程传递了一个SIGSEGV信号(分段违规)时,会在核心文件中记录整个进程的镜像信息。可以使用GDB读取这类文件来辅助调试并且查找程序崩溃的原因。 |
在linux有一个readelf的小程序。 可以来查看elf文件。 可以看到原始的elf文件头。
其实所谓的格式就是 C语言中的结构体来做表,并且赋予每个字段含义以及信息。
例子:
>>> readelf -h main.out ELF 头: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 类别: ELF32 数据: 2 补码,小端序 (little endian) Version: 1 (current) OS/ABI: UNIX - System V ABI 版本: 0 类型: DYN (共享目标文件) 系统架构: Intel 80386 版本: 0x1 入口点地址: 0x1070 程序头起点: 52 (bytes into file) Start of section headers: 13980 (bytes into file) 标志: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 12 Size of section headers: 40 (bytes) Number of section headers: 30 Section header string table index: 29
elf文件的结构如下
#define EI_NIDENT 16 typedef struct { unsigned char e_ident[EI_NIDENT]; uint16_t e_type; ;表示文件的类型,这里为3 就是ET_DYN类型 uint16_t e_machine; ;表示平台的版本,我使用gcc编译所以标识 intel 8086 uint32_t e_version; ; 表示版本号 ElfN_Addr e_entry; ; 重要字段标识了入口点位置程序的第一行代码位置 ElfN_Off e_phoff; ; 重要,标识了program table的在文件的偏移开始位置 ElfN_Off e_shoff; ;section header table的偏移位置 uint32_t e_flags; ;保存与文件相关 特定于处理器的标识。 uint16_t e_ehsize; uint16_t e_phentsize; ;重要字段,表明了program table 每个结构体的大小 uint16_t e_phnum; ;重要字段,表明programtable有多少项 uint16_t e_shentsize; ;表明了sectiontable每一项结构有多大 uint16_t e_shnum; ;表明了sectiontable有多少项 uint16_t e_shstrndx; ;节区头部表格中与节区名称字符串相关的表项的索引。 } ElfN_Ehdr;
其实重要的就几个字段
1.ELF文件标识头(4个字节即可)
其余的非必须的都可以设置为错误的值。对于程序来说是可以正常运行的。可以进行手动尝试
上面所说是实际应用过程中我们需要重点了解的内容
那么下面就给出详细的elf头。让我们更加的了解elf其他属性以及作用。
可制定链接格式 最初是由UNIX系统实验室开发并且发布的。是作为应用程序二进制接口的一部分。 工具接口委员会还在将发展的ELF标准选作为一种可移植的目标文件格式。 可以在32位的intel体系结构上的很多操作系统中使用。
其中目录文件有三种类型,分别如下表所示:
名词 | 文件后缀 | 注解 |
---|---|---|
可重定位文件(RelocatableFIle) | .o 结尾 | 包含适合于其他目标文件链接来创建可执行文件或者共享目标文件的代码和数据。 |
可执行文件(ExecutableFIle) | .exe | 包含适合于可执行的一个程序,此文件规定了exec()如何创建一个程序的进程映像 |
共享目标文件(Shared object FIle) | .so | 包含可在两种上下文中链接的代码和数据。首先链接编辑器可以将它和其它可重定位文件和共享目标文件一起处理, 生成另外一个目标文件。其次动态链接器(Dynamic Linker)可能将它与某 个可执行文件以及其它共享目标一起组合,创建进程映像。 |
特点:
目标文件全部是程序的二进制表示。 目的就是直接在某种处理器上直接执行。
目标文件格式支持8位字节/32位的体系架构。不过他们是可以扩展了。也就是可以运行在现在64位的机器上。
下图则标识各个结构信息的名称 大小 对其方式等等。
名称 | 大小 | 对齐 | 目的 |
---|---|---|---|
Elf32_Addr | 4 | 4 | 无符号程序地址 |
Elf32_Half | 2 | 2 | 无符号中等整数 也就是 unsigned short表示 |
Elf32_Off | 4 | 4 | 无符号文件偏移 |
Elf32_SWord | 4 | 4 | 有符号整数 |
Elf32_Word | 4 | 4 | 无符号整数 |
unsigned char | 1 | 1 | 无符号1字节 |
目标文件中所有的数据结构都要遵守自然大小和对齐的规则。
如果有必要则会进行对齐操作。
32位结构如下:
/* 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;
64位结构如下
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;
成员1: e_ident 是一些标识信息。总共大小是16个字节(32位的结构)
其中他是一个数组。 有一些ELF的标识信息,用来标识数组中不同下表的含义。
名称 | 值 | 目的 |
---|---|---|
EI_MAG0 | 0 | 文件标识 |
EI_MAG1 | 1 | 文件标识 |
EI_MAG2 | 2 | 文件标识 |
EI_MAG3 | 3 | 文件标识 |
EI_CLASS | 4 | 文件类 |
EI_DATA | 5 | 数据编码 |
EI_VERSION | 6 | 文件的版本 |
EI_PAD | 7 | 补齐字节开始处 |
EI_NIDENT | 16 | e_ident[]的大小 |
这就是每个下标的意义。 那么下面分别说下索引中每个值
其余字段意义如下
在LINUX下可以使用 readelf -h youelf 则可以看小elf文件头的信息