操作系统-第十讲-可执行文件

可执行文件

jyy 老师的讲义


内容回顾

有了系统调用和 libc,我们就真的可以实现 “任何程序” 了——例如,你可以想一想,如果要实现一个 MicroPython 解释器,我们需要实现什么,又需要借助哪些系统调用?当然,我们需要编译器帮我们编译 python.c 到可执行文件——我们一直以来都 “默认” 了编译器工具链可以帮助我们实现高级语言到可执行文件的翻译。今天是时候 “打开” 这部分内容了。

可执行文件

什么是可执行文件?一个操作系统的对象、一个字节序列、一个描述了状态机初始状态的数据结构。

ELF 可执行文件ELFExecutable and Linkable Format 的缩写,是 Linux 系统上的一种可执行文件格式。我们可以试着去构造、理解、可视化 ELF 文件的结构。

ELF, E = Executable, L = Linkable, 是一种标准。

从 C 程序到可执行文件一般经历下述几个过程:

main.c
Pre-processor
(cpp)
Stage 1
预处理
main.i
Compiler
(cc1)
Stage 2
编译
main.s
Assembler
(as)
Stage 3
汇编
main.o
Linker
(ld)
prog
Stage 4
链接

通过 readelf -h main.o 可以看到 elf 头的内容:

Output readelf -h main.o 输出
1ELF Header:
2 Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 // ELF 魔数标识
3 Class: ELF64 // 64位 ELF 文件
4 Data: 2's complement, little endian // 小端序
5 Version: 1 (current) // ELF 版本
6 OS/ABI: UNIX - System V // 操作系统/ABI 标识
7 ABI Version: 0 // ABI 版本
8 Type: REL (Relocatable file) // 可重定位文件类型
9 Machine: Advanced Micro Devices X86-64 // 目标架构
10 Version: 0x1 // 目标版本
11 Entry point address: 0x0 // 入口地址(可重定位文件为0)
12 Start of program headers: 0 (bytes into file) // 程序头表偏移
13 Start of section headers: 448 (bytes into file) // 节头表偏移
14 Flags: 0x0 // 处理器特定标志
15 Size of this header: 64 (bytes) // ELF 头大小
16 Size of program headers: 0 (bytes) // 程序头表项大小
17 Number of program headers: 0 // 程序头表项数量
18 Size of section headers: 64 (bytes) // 节头表项大小
19 Number of section headers: 12 // 节头表项数量
20 Section header string table index: 11 // 字符串表索引

可以看到 elf 头的长度是 64 字节,所以 Sections 部分开始于地址 0x40

同时我们可以看到 Program HeaderSection Hearder 的信息:由于 main.o 是一个可重定位文件,本身并不能直接执行,故没有需要加载到内存的程序头,程序头表通常用于描述可执行文件中如何将程序映射到内存;同时可以看到,该 ELF 文件中包含 12 个节头表项,每个节头表项的大小是 64 字节,节头表一般用于链接阶段,或者给调试提供文件结构信息。

因此,整个 ELF 文件的组成就很清晰了。