可执行文件
内容回顾
有了系统调用和 libc,我们就真的可以实现 “任何程序” 了——例如,你可以想一想,如果要实现一个 MicroPython 解释器,我们需要实现什么,又需要借助哪些系统调用?当然,我们需要编译器帮我们编译 python.c 到可执行文件——我们一直以来都 “默认” 了编译器工具链可以帮助我们实现高级语言到可执行文件的翻译。今天是时候 “打开” 这部分内容了。
可执行文件
什么是可执行文件?一个操作系统的对象、一个字节序列、一个描述了状态机初始状态的数据结构。
ELF 可执行文件:ELF 是 Executable 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 Header
和 Section Hearder
的信息:由于 main.o
是一个可重定位文件,本身并不能直接执行,故没有需要加载到内存的程序头,程序头表通常用于描述可执行文件中如何将程序映射到内存;同时可以看到,该 ELF 文件中包含 12 个节头表项,每个节头表项的大小是 64 字节,节头表一般用于链接阶段,或者给调试提供文件结构信息。
因此,整个 ELF 文件的组成就很清晰了。