ELF文件的笔记
ELF
说明
ELF文件的英文全称是The Executable and Link Format
, 最初是由UNIX系统实验室开发、发布的ABI(Application Binary Interface)接口的一部分,也是Linux的主要可执行文件格式。
种类
从使用上来说,主要的ELF文件的种类主要有三类:
- 可执行文件(.out):Executable File,包含代码和数据,是可以直接运行的程序。其代码和数据都有固定的地址 (或相对于基地址的偏移 ),系统可根据这些地址信息把程序加载到内存执行
- 可重定位文件(.o):Relocatable File,包含基础代码和数据,但它的代码及数据都没有指定绝对地址,因此它适合于与其他目标文件链接来创建可执行文件或者共享目标文件
- 共享目标文件(.so):Shared Object File,也称动态库文件,包含了代码和数据,这些数据是在链接时被链接器(ld)和运行时动态链接器(ld.so.l、libc.so.l、ld-linux.so.l)使用的。
ELF文件格式
从文件的格式上来说,分为汇编器的链接视角与程序的执行视角两种去分析ELF文件
从程序执行视角来说,这就是Linux加载器加载的各种Segment的集合。比如只读代码段、数据的读写段、符号段等等。而从链接的视角上来看,elf又分为各种的sections。注意Section Header Table和Program Header Table并不是一定要位于文件开头和结尾的,其位置由ELF Header指出,上图这么画只是为了清晰。为了彻底的弄清楚elf文件的内容,可以先从ELF文件的头部开始分析。
文件头部信息
对于elf头部文件信息,首先可以可以查看一下内存的布局情况:(选择位于ubuntu系统的/usr/bin/中的可执行文件)
根据readelf可以得到该文件的头部信息的情况
根据定义,elf32的结构体定义,在Linux上可以在/usr/include/elf.h
中找到
#define EI_NIDENT (16)
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
文件的标识以及标识描述了elf如何编码等信息。`文件的标识以及标识描述了elf如何编码等信息。
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
关于该结构体的索引可以看下面的表格:
名称 | 取值 | 目的 |
---|---|---|
EI_MAG0 | 0 | 文件标识(0x7f) |
EI_MAG1 | 1 | 文件标识(E) |
EI_MAG2 | 2 | 文件标识(L) |
EI_MAG3 | 3 | 文件标识(F) |
EI_CLASS | 4 | 文件类 |
EI_DATA | 5 | 数据编码 |
EI_VERSION | 6 | 文件版本 |
EI_PAD | 7 | 补齐字节开始处 |
EI_NIDENT | 16 | e_ident[]大小 |
EI_CLASS的内容,当取值为0时,是非法类别,1是32位的目标,2是64位的目标。这里是1所以程序是32位的目标。
EI_DATA表示数据的编码,当为0时,表示非法数据编码,1表示高位在前,2表示低位在前。
EL_VERSION表示了elf的头部版本号码。
前面四个基本上确定的,内容第一个字符为7f,后面用ELF字符串表示该文件为ELF格式
e_type
该数据类型是uint16_t数据类型的,占两个字节。通过字段查看,可以看到这个值为00 03
。表格定义如下:
名称 | 取值 | 含义 |
---|---|---|
ET_NONE | 0x0000 | 未知目标文件格式 |
ET_ERL | 0x0001 | 可重定位文件 |
ET_EXEC | 0x0002 | 可执行文件 |
ET_DYN | 0x0003 | 共享目标文件 |
ET_CORE | 0x0004 | Core文件(转储格式) |
ET_LOPROC | 0xff00 | 特定处理器文件 |
ET_HIPROC | 0xffff | 特定处理器文件 |
对应表格内容,可以看到类型为so
即共享目标文件类型。
e_machine
由字段可以看到为00 28
,关于这个字段的解析,基本上就是表示该elf文件是针对哪个处理器架构的。
下面只列出几个常见的架构的序号
名称 | 取值 | 含义 |
---|---|---|
EM_NONE | 0 | No machine |
EM_SPARC | 2 | SPARC |
EM_386 | 3 | Intel 80386 |
EM_MIPS | 8 | MIPS I Architecture |
EM_PPC | 0x14 | PowerPC |
EM_ARM | 0x28 | Advanced RISC Machines ARM |
e_version
该字段占四个字节,表示当前文件版本的信息。现在取值为00 00 00 01
。从取值上来看
名称 | 取值 | 含义 |
---|---|---|
EV_NONE | 0 | 非法版本 |
EV_CURRENT | 1 | 当前版本 |
e_entry
这里表示程序的入口地址,目前为四字节,所以通过字段解析到的内容为00 40 40 00
。得到可执行程序的入口地址为0x4040
。
e_phoff
该字段表示程序表头偏移。占四个字节,根据字段解析,可以查看当前的偏移量为00 00 00 00
。
e_shoff
该区域比较重要,记录了section的偏移地址。为四字节,解析出来的字段为00 00 00 40
e_flags
特定处理器格式的标志
e_ehsize
elf文件的头部大小。该取值与头文件结构体的大小相关,
e_phentsize
程序头部表项大小
ELF的节区(section)
elf文件中的节是从编译器链接角度来看文件的组成的。从链接器的角度上来看,包括指令、数据、符号以及重定位表等等。
文件段(Section)一般可分为以下几个区域
.text. :代码段
.rodata:只读数据段
.data: 已初始化全局数据段
.bss:未初始化全局数据段。因为为初始化变量没有具体的值,所以无需在目标文件中分配用于保存值的空间,也就是在目标文件中不会占有实际的磁盘空间,仅仅是一个占位符。区分已初始化和为初始化全局变量是为了提高空间利用率。
.symtab:符号表(symbol table)。在程序中被定义和引用的函数名和全局变量名都属于符号,与这些符号相关的信息都保存在符号表中。
上面提到的段(Section)的内容在链接成可执行文件时,会分配不同的虚拟内存空间,也就是会映射到段(Segment)内。这个映射关系由段头表(Segment header table)来记录,它是一个结构数组。
本文来自博客园,作者:ivanlee717,转载请注明原文链接:https://www.cnblogs.com/ivanlee717/p/16669417.html