ELF文件格式(2):文件结构
概述
ELF可执行文件定义了可执行程序的静态文件格式,包括文件信息头、段及节等结构,并约定了程序在运行时,程序文件的内容是如何动态加载到内存中以及起始运行地址。了解ELF文件格式,可以帮助我们了解整个程序的生成和运行过程。
ELF文件布局
ELF文件格式提供了两种视图,分别是链接视图和执行视图。链接视图是以节(section)为单位,执行视图是以段(segment)为单位。链接视图就是在程序链接时用到的视图,而执行视图则是在程序被装载运行时用到的视图。
从表中可以看出,ELF文件格式包含ELF文件头、节(或段)、节区头部表以及程序头部表等结构。其中,ELF文件头描述了整个文件的基本属性,包括ELF文件类型、运行平台以及其它头部表的属性信息等等。
ELF文件头
ELF文件头(ELF Header)主要标记了ELF文件类型、结构和程序开始执行的入口地址,并提供了其他ELF头(如节头表和程序头表)在文件中的偏移位置。使用readelf工具来查看一个ELF文件的文件头:
ELF文件头的数据结构定义如下:
typedef struct elf32_hdr{
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type; //目标文件类型
Elf32_Half e_machine; //硬件平台
Elf32_Word e_version; //elf头部版本
Elf32_Addr e_entry; //程序执行的入口地址
Elf32_Off e_phoff; //程序头表偏移量
Elf32_Off e_shoff; //节头表偏移量
Elf32_Word e_flags; //处理器特定标志
Elf32_Half e_ehsize; //elf头部长度
Elf32_Half e_phentsize; //程序头表中一个条目的长度
Elf32_Half e_phnum; //程序头表条目数目
Elf32_Half e_shentsize; //节头表中一个条目的长度
Elf32_Half e_shnum; //节头表条目个数
Elf32_Half e_shstrmdx; //节头表字符索引
}Elf32_Ehdr;
程序头部表
ELF程序头部表(Program Header Table)是对二进制文件中段的描述,本质上是记录段表项的数组,是程序装载必需的一部分。段在程序被系统加载运行时被解析,其描述了磁盘上可执行文件的内存布局以及如何映射到内存中。ELF文件头中名为e_phoff
(程序头部表偏移量)的偏移量来得到程序头表。使用readelf工具查看ELF文件中的程序头部表信息:
由上面的输出信息可以看出,程序头部表记录了ELF文件中所有段的属性信息,包括段的类型、文件偏移以及映射到内存时的地址信息。程序头部表本质上是段信息的数组,对于每个段(数组项),ELF使用如下数据结构进行描述:
typedef struct elf32_phdr{
Elf32_Word p_type; //段的类型
Elf32_Off p_offset; //段的位置相对于文件开始处的偏移
Elf32_Addr p_vaddr; //段在内存中的首字节地址,即虚拟地址
Elf32_Addr p_paddr; //段的物理地址
Elf32_Word p_filesz; //段在文件映像中的字节数
Elf32_Word p_memsz; //段在内存映像中的字节数
Elf32_Word p_flags; //段的标记
Elf32_Word p_align;, //段在内存中的对齐标记
)Elf32_Phdr;
段
ELF标准定义了5中不同类型的段,其描述如下:
段(segment)类型 | 描述 |
---|---|
PT_LOAD | 描述可装载的段,该类型的段会被装载或被映射到内存中。典型的包括代码段和数据段 |
PT_DYNAMIC | 动态链接文件特有,包含了动态链接所必需的一些信息 |
PT_NOTE | 保存了与特定供应商或者系统相关的附加信息 |
PT_INTERP | 记录动态链接器的位置信息,本质是一个以null为终止符的字符串 |
PT_PHDR | 保存程序头部表本身的位置和大小 |
节区头部表
节头表(Section Header Table)用于描述ELF文件的各个节区的位置和大小,主要用于链接和调试。须注意的是,节头对于程序的执行不是必需的,没有节头表,程序仍可以正常执行。使用readelf工具查看ELF文件的所有节:
与程序头部表类似,节区头部表是由节构成的数组。对于节的数据结构定义如下:
typedef struct{
Elf32_Word sh_name; //小节名在字符表中的索引
E1t32_Word sh_type; //小节的类型
Elf32_Word sh_flags; //小节属性
Elf32_Addr sh_addr; //小节在运行时的虚拟地址
Elf32_Off sh_offset; //小节的文件偏移
Elf32_Word sh_size; //小节的大小.以字节为单位
Elf32_Word sh_link; //链接的另外一小节的索引
Elf32 Word sh_info; //附加的小节信息
Elf32 Word sh_addralign; //小节的对齐
Elf32 Word sh_entsize;
}Elf32_Shdr;
节
ELF文件中定义的一些比较重要的节和节类型罗列如下:
节(section)类型 | 描述 |
---|---|
.text | 已编译程序的机器代码,即代码段 |
.rodata | 保存了只读的数据 |
.data | 已初始化的全局和静态C变量,即数据段 |
.bss | 未初始化的全局和静态C变量,以及所有被初始化为0的全局或静态变量 |
.rel.text | 被模块引用或定义的外部函数的重定位信息,对应于.text
|
.rel.data | 被模块引用或定义的所有全局变量的重定位信息,对应于.data
|
.debug | 调试符号表,其条目是程序中定义的局部变量和类型定义,程序中定义和引用的全局变量,以及原始的C源文件。只有以-g选项调用编译器驱动程序时,才会得到这张表 |
.line | 原始C源程序中行号和.text节中机器指令之间的映射,用于调试 |
.symtab | 符号表,存放在程序中定义和引用的函数和全局变量的信息 |
.strtab | 字符串表,包含.symtab和.debug节中的符号表,以及节头部的节名字。字符串表是以null结尾的字符串的序列 |
.shstrtab | 节名字符串表,在可执行文件是必须存在的 |
.ctors .dtors | C++构造函数和析构函数存放的段 |
.dynsym | 用于保存与动态链接相关的符号 |
.dynstr | 动态符号字符串表,与静态链接使用.strtab对应 |
.hash | 在动态链接的情况下,用于加快符号查找过程 |
.interp | 保存可执行文件使用的动态链接器的路径 |
.plt | 程序链接表,和.got.plt配合实现函数调用的延迟绑定 |
.got | 用于动态链接,保存外部全局变量引用的地址 |
.got.plt | 用于动态链接,用于保存外部函数引用的地址 |
参考
- 《程序员的自我修养——链接、装载与库》
- 《Linux二进制分析》
- 《EFL Object File Format》