摘要
本文描述了Linux系统的ELF文件格式。
ELF文件格式简介
ELF(Executable and Linkable Format)是一种常用的二进制文件格式,用于存储可执行文件、共享库和核心转储文件。它是由Linux操作系统所采用和支持的标准格式。
ELF文件共有四种形式:
1. 可执行文件(Executable file):是一种包含可直接执行程序的ELF文件,可以直接在操作系统上运行。
2. 共享目标文件(Shared object file):是一种包含函数和变量等可被多个可执行程序同时调用的ELF文件,通常被称为动态链接库或共享库(Shared Library)。多个程序可以共享同一份共享库,这样可以减少系统资源的浪费并提高程序的运行效率。
3. 目标文件(Object file):是一种包含已编译的代码和数据但还未被链接成可执行文件或共享库的ELF文件。目标文件通常会被多次编译和链接,直到最终生成可执行文件或共享库。
4. 崩溃转储文件(Core file):Core文件是在Linux和其他UNIX系统中生成的一种特殊文件,它记录了进程在崩溃或异常终止时的内部状态,包括进程的内存映像、寄存器状态、堆栈跟踪信息以及其他调试信息。Core文件通常用于调试崩溃或异常终止的程序。
ELF目标文件结构图
下图左侧是ELF的目标文件结构,右侧是ELF可执行文件结构。
ELF文件结构图
ELF文件结构定义
ELF文件结构的定义在头文件elf.h中,其典型路径为:
/usr/include/elf.h
ELF文件类型
ELF文件的四种类型定义如下:
#define ET_NONE 0 /* No file type */
#define ET_REL 1 /* Relocatable file type */
#define ET_EXEC 2 /* Executable file type */
#define ET_DYN 3 /* Shared object file type */
#define ET_CORE 4 /* Core file type */
ELF头
#define EI_NIDENT (16)
typedef struct elf32_hdr{
unsigned char e_ident[EI_NIDENT]; /* 魔数和相关信息 */
Elf32_Half e_type; /* ELF文件类型,参考上节的定义 */
Elf32_Half e_machine; /* 硬件体系,例如:Intel 80386 */
Elf32_Word e_version; /* 目标文件版本 */
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_shstrndx; /* 节头字符串表索引 */
} Elf32_Ehdr;
e_ident字段定义如下:
e_ident | Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |
Class: ELF32 | |
Data: 2’s complement, little end | |
Version: 1(current) | |
OS/ABI: UNIX - System V | |
ABI Version: 0 |
程序头表
程序头表属于ELF的可选内容,通常位于类型为ET_EXEC的可执行文件中。
类型为ETL_REL的可重定位文件(扩展名为.o)是没有成不头表的。
程序头表项定义:
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;
程序头表示例:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
-----------------------------------------------------------------------------
EXIDX 0x000884 0x00008884 0x00008884 0x00008 0x00008 R 0x4
PHDR 0x000034 0x00008034 0x00008034 0x00140 0x00140 R E 0x4
INTERP 0x000174 0x00008174 0x00008174 0x00013 0x00013 R 0x1
LOAD 0x000000 0x00008000 0x00008000 0x00890 0x00890 R E 0x8000
LOAD 0x000f04 0x00010f04 0x00010f04 0x0014c 0x00154 RW 0x8000
DYNAMIC 0x000f10 0x00010f10 0x00010f10 0x000f0 0x000f0 RW 0x4
NOTE 0x000188 0x00008188 0x00008188 0x00020 0x00020 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4
GNU_RELRO 0x000f04 0x00010f04 0x00010f04 0x000fc 0x000fc R 0x1
节头表
节头表通常位于ELF文件的尾部,其文件内偏移由ELF头结构的e_shoff字段指示。
typedef struct {
Elf64_Word sh_name; /* 节名称,是节区头字符串表节区中的索引。*/
Elf64_Word sh_type;
Elf64_Xword sh_flags; /* 内存访问属性:可度,可写,可执行,是否需要分配内存等。*/
Elf64_Addr sh_addr;
Elf64_Off sh_offset;
Elf64_Xword sh_size;
Elf64_Word sh_link;
Elf64_Word sh_info;
Elf64_Xword sh_addralign;
Elf64_Xword sh_entsize;
} Elf64_Shdr;
节内容
系统预定义的节名称及含义如下:
sh_name | sh_type | 说明 |
---|---|---|
.text |
SHT_PROGBITS |
代码段,包含程序的可执行指令 |
.data |
SHT_PROGBITS |
已初始化数据,将出现在程序的内存映像中 |
.bss |
SHT_NOBITS |
未初始化数据,因为只有符号所以 |
.rodata |
SHT_PROGBITS |
只读数据 |
.comment |
SHT_PROGBITS |
版本控制信息 |
.dynsym |
SHT_DYNSYM |
动态链接符号表 |
.shstrtab |
SHT_STRTAB |
节头字符串表 |
.strtab |
SHT_STRTAB |
字符串表 |
.symtab |
SHT_SYMTAB |
符号表 |
ELF文件分析
objdump是一个用于查看ELF文件中二进制代码、目标文件、共享库、静态库等信息的工具,是GNU Binutils套件中的一个组件。
反汇编应用程序
objdump -d hello.o
显示文件头信息
objdump -f hello.o
显示指定节名的信息
objdump -s -j .comment hello.o
参考资料
ELF与PE对比
ELF和PE都是COFF(Common File Format)格式的变种,两者对比如下:
- ELF文件开篇就是ELF头,无DOS头这类历史包袱;PE文件为了兼容DOS程序,开篇就是DOS头和DOS存根程序,由DOS头的e_lfanew字段指示PE头在文件内的偏移。
- ELF文件的ELF头相当于PE文件的映像文件头。
- ELF文件的程序头相当于PE文件的PE可选头。
- ELF文件中节头表位于节内容之后,偏移由ELF头的e_shoff字段指定,节头数量由e_shnum字段指定;PE的节头表位于节内容之前,紧跟在PE可选头之后,以全零的节头表项作为结束标志。
ELF文件格式比PE文件格式简单。
总结
ELF文件格式是现代操作系统和编程语言中最常见的可执行文件格式之一。了解ELF文件格式很重要,因为:
1. 在开发中需要知道如何生成ELF文件格式的输出。编译器和链接器通常使用ELF文件格式来生成可执行文件或共享库。了解ELF文件格式的细节可以帮助程序员了解编译器和链接器如何工作,并且可以通过对编译器和链接器参数的调整来更好地优化程序。
2. 在调试和优化程序时,了解ELF文件格式可以帮助我们识别和理解程序的符号表、调试信息、代码段、数据段等。这些信息可以帮助我们更好地理解程序的执行,从而更好地调试和修复程序。
3. 在开发反汇编或二进制分析工具时,了解ELF文件格式可以帮助我们识别可执行文件或共享库的结构和组成,从而更好地理解这些文件的内容,以及它们是如何工作的。
4. 在安全研究领域,了解ELF文件格式可以帮助我们分析和理解各种漏洞利用技术,例如栈溢出、格式字符串漏洞、ROP链等。同时,也可以帮助我们识别和分析各种恶意软件和代码,从而更好地保护我们的系统。
总之,对ELF文件格式的了解可以帮助我们在程序开发、调试、分析和安全等方面变得更加高效和熟练。