本文是作为 Rust 的练手项目 rself(https://github.com/chengzhycn/rself) 的背景知识记录。rself 使用 Rust 重新造了一个 readelf 的轮子。

ELF全称为Executable and Linkable Format,是Linux上可执行文件的通用文件格式。主要有三种类型的ELF文件:

  • Relocatable file: 存储用于和其它对象文件链接的代码和数据,创建一个可执行文件或者共享对象文件
  • Executable file: 存储的是用于执行的程序。它指定了exec如何创建一个程序的进程镜像
  • Shared object file: 存储了在两种语义下用于链接的代码和数据。第一种是让链接器将共享对象文件和其它的可重定位文件/共享对象文件一起创建一个新的对象文件;第二种是动态链接器将它和可执行文件以及其它的共享对象一起创建一个进程镜像。

ELF构成

ELF主要由如下几个部分组成:

  • ELF Header: 位于文件的起始,描述了整个文件的组织结构
  • Sections/Segments: 存储了指令、数据、符号表、可重定位信息等。sections 是这段数据在链接时的表现;segments 是数据在执行时的表现。
  • Program Header Table: 用于告诉系统如何创建一个进程镜像。对于可执行程序,Program Header Table是必须的,但是对于可重定位文件就不需要了。
  • Section Header Table: 存储描述文件中sections的信息。每个section都在这个表中有一个条目,存储了如section name, section size等信息。用于链接的文件必须得有Section Header Table,其它的对象文件可有可无。

d02c13c1fa4bd228f698cf355ec47377_MD5

上面的图中虽然描述文件各个部分的位置,但实际上只有ELF Header的位置是固定的,其它的部分在文件中的位置都在ELF Header中指定。

ELF Header

#define EI_NIDENT 16

typedef struct {
        unsigned char   e_ident[EI_NIDENT];
        Elf32_Half      e_type;
        Elf32_Half      e_machine;
        Elf32_Word      e_version;
        Elf32_Addr      e_entry;
        Elf32_Off       e_phoff;
        Elf32_Off       e_shoff;
        Elf32_Word      e_flags;
        Elf32_Half      e_ehsize;
        Elf32_Half      e_phentsize;
        Elf32_Half      e_phnum;
        Elf32_Half      e_shentsize;
        Elf32_Half      e_shnum;
        Elf32_Half      e_shstrndx;
} Elf32_Ehdr;

typedef struct {
        unsigned char   e_ident[EI_NIDENT];
        Elf64_Half      e_type;
        Elf64_Half      e_machine;
        Elf64_Word      e_version;
        Elf64_Addr      e_entry;
        Elf64_Off       e_phoff;
        Elf64_Off       e_shoff;
        Elf64_Word      e_flags;
        Elf64_Half      e_ehsize;
        Elf64_Half      e_phentsize;
        Elf64_Half      e_phnum;
        Elf64_Half      e_shentsize;
        Elf64_Half      e_shnum;
        Elf64_Half      e_shstrndx;
} Elf64_Ehdr;

ELF header通常由一个Elfxx_Ehdr结构体表示。它存储了当前二进制文件的一些元数据: