欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ELF文件格式(2):文件结构

程序员文章站 2024-03-25 11:41:07
...

概述

ELF可执行文件定义了可执行程序的静态文件格式,包括文件信息头、段及节等结构,并约定了程序在运行时,程序文件的内容是如何动态加载到内存中以及起始运行地址。了解ELF文件格式,可以帮助我们了解整个程序的生成和运行过程。

ELF文件布局

ELF文件格式提供了两种视图,分别是链接视图和执行视图。链接视图是以节(section)为单位,执行视图是以段(segment)为单位。链接视图就是在程序链接时用到的视图,而执行视图则是在程序被装载运行时用到的视图。
ELF文件格式(2):文件结构

从表中可以看出,ELF文件格式包含ELF文件头、节(或段)、节区头部表以及程序头部表等结构。其中,ELF文件头描述了整个文件的基本属性,包括ELF文件类型、运行平台以及其它头部表的属性信息等等。

ELF文件头

ELF文件头(ELF Header)主要标记了ELF文件类型、结构和程序开始执行的入口地址,并提供了其他ELF头(如节头表和程序头表)在文件中的偏移位置。使用readelf工具来查看一个ELF文件的文件头:
ELF文件格式(2):文件结构

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文件格式(2):文件结构

由上面的输出信息可以看出,程序头部表记录了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文件的所有节:
ELF文件格式(2):文件结构
与程序头部表类似,节区头部表是由节构成的数组。对于节的数据结构定义如下:

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》