What
ELF
- 是一种文件格式 ABI,一种被广泛接受的 SPEC,
- 主要由四个部分组成:ELF Header (必须且位置固定)、Program Header Table、Segment (Sections) 和 Section Header Table
- 提供的能力(储存的信息)
- 32 bit / 64 bit
- 大端小端(MSB / LSB)
- 文件类型
- executable
- relocateable library
- shared library
- core dump
- Machine Architecture (eg: x86-64)
- 程序起始地址
- 链接,调试等元数据 TODO
ELF Header
由于 ELF 的部份字段是 optional 的,所以只有一个固定位置的头部 ELF Header,这里打出头 64 个字节
$ hexdump -e '16/1 "%02x " "\n"' -v -n 64 quine
7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
02 00 3e 00 01 00 00 00 78 00 40 00 00 00 00 00
40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 40 00 38 00 01 00 00 00 00 00 00 00
- 第 1 至 4 字节 始终相同,是 elf 的 magic number,用于识别。
7f 45 4c 46
// The second, third, and fourth bytes [45 4c 46] are the string “ELF” encoded in ASCII.
-
第 5 个字节 (
02
: 64-bit) 告诉我们使用的是 32 位还是 64 位 ELF 格式 -
第 6 个字节 (
01
: LSB) 告诉我们文件其余部分的 encoding 方式 (LSB or MSB) -
第 7 个字节 (
01
) 告诉我们该文件使用的是哪个版本的 ELF 格式,目前所有文件都使用 ELF 版本 1 -
第 8 到第 16 个字节置零。是没有实际意义的 buffer (部份
OS Specific
的内容视作不重要),用于 对齐 -
对齐:ELF 的 field 都是对齐的,所有 4 字节字段的起始位置都必须是 4 的倍数,所有 X 字节字段的起始位置都必须是 X 的倍数
-
第 17 和第 18 个字节是一个两字节字段 (
02 00
),由于采用 LSB,值为 2- 该字段表示 ELF 文件的 type。值为 2 意味着我们的文件是一个 executable
- 从索引 16 开始,2 对齐
-
19-20 又是两字节字段 (
3e 00
) 为 machine 字段,值为 62,表示x86-64
-
21-24 是个四字节字段 (
01 00 00 00
): 又是 ELF 版本 1 -
25-32 是一个八字节字段,它从索引 24 开始 (8 对齐)
78 00 40 00 00 00 00 00
- 这个字段指明了程序应该从何处开始执行。它是进程启动时开始执行的内存地址。
-
33-40 是一个八字节字段
40 00 00 00 00 00 00 00 = 64
,是 program header table offset,是一个指针,告诉我们 program header table 的位置是 index (offset) 64 -
41-48 是一个八字节字段为 0,是 section header table offset,也是指针,代表本文件没有 section header table
-
49-52 是一个四字节字段为 0,会写处理器特定标志,如区分 ARM,MIPS,RISC-V,PowerPC。
- x86-64 架构不使用 e_flags 字段
-
53-54 二字节字段,是 ELF 头大小,为 64,取决于是 32 位 ELF 还是 64 位 ELF,是固定值,差在了哪里可以想一想🤔
-
接下来几个如下
38 00
- e_phentsize:程序头表项大小 56 字节01 00
- e_phnum:1 个程序头表项00 00
- e_shentsize:节头表项大小为 000 00
- e_shnum:0 个节头表项
-
最后的
00 00
是 Section Header STRing table iNDeX,为 0 代表没有 Section Header String Table
// 32 位 ELF Struct
# define ELF_NIDENT 16
typedef struct {
uint8_t e_ident[ELF_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; // Section Header STRing table iNDeX
} Elf32_Ehdr;
Loader
ELF 会告诉 Linux 如何创建一个新进程。当我们 执行 (execute) 一个文件时,Linux 会读取该文件,利用其中的信息来设置一个新进程,然后启动该进程。这个过程被称为 加载 (load),而 Linux 中负责将 ELF 文件转换为进程的部分称为 加载器 (loader)。
Program Header Table
Section
Why
提供了一种标准化的方式来存储和组织可执行代码、数据和元信息,让操作系统知道如何处理二进制文件。
Alternatives: