本周阅读材料
首先,准备好手册随时查阅:
- System V ABI: System V Application Binary Interface (AMD64 Architecture Processor Supplement)。在看完代码之后,你对这份手册的理解会加深很多。
阅读理解示例代码
可执行完文件本质上是 “描述了状态机初始状态的数据结构”!就这么简单。为了理解这个数据结构,GNU binutils 里的许多工具都值得大家使用 (以及 RTFM)。
除此之外,可执行文件里还包含了运行时理解状态机 (将 low-level 状态机映射回 high-level 状态机) 的信息,例如严格来说执行不需要的符号表、调试信息等。你也可以试着调试一下代码:popcount.c 在不同优化等级、不同编译器下,你会看到很不一样的调试结果。与此同时,我们也可以自己实现 Stack Unwinding: unwind.c。
想要理解可执行文件的另一个途径是通过加载器。阅读和调试代码比资料更有效——代码在一定程度上是对手册的 “形式化”,并且相关的代码会被放在一起,反而比手册更容易阅读理解。你不妨可以把几个 ELF 加载器放在一起对比:
- loader-static.c 用户态加载器
- bootmain.c 启动加载器
-
kernel/fs/binfmt_elf.c Linux 内核加载器
-
dlbox.c (gcc, readdl, objdump, interp)
最后是一直以来都觉得最难教的动态链接。dlbox.c “全家桶” 代表了最小的动态加载刚需,配合 libc.S 动态链接库提供 putchar 和 exit、libhello.S 提供库函数和 main.S,再阅读手册,你会觉得豁然开朗。