x86内核解压引导程序开发与地址重定位 - 第1节
1.1 x86 内核启动流程概述
x86 体系结构从加电复位到内核入口点经历多个阶段,每个阶段都涉及模式切换、地址空间重组与数据搬运。理解整条链路是后续解压缩与重定位分析的基础。
1.1.1 启动阶段全景
核心认知:x86 启动不是“一条直线”,而是“多次跳转 + 多次模式切换”的接力过程。BIOS → Bootloader → 内核入口之间,地址映射、CPU 模式、数据压缩状态都在变化。
1.1.2 启动阶段对比表
| 阶段 | CPU 模式 | 地址宽度 | 代码来源 | 典型入口 |
| BIOS | 实模式 | 20-bit | 主板 ROM | 0xFFFF0 |
| Bootloader 第一阶段 | 实模式 | 20-bit | MBR/VBR | 0x7C00 |
| Bootloader 第二阶段 | 保护模式 | 32-bit | 文件系统 | GRUB core |
| 解压缩 stub | 保护模式 | 32-bit | 内核映像头部 | startup_32 |
| 内核入口 | 分页模式 | 32/64-bit | 解压后内存 | start_kernel |
1.2 解压缩与重定位的重要性
1.2.1 为什么需要解压缩?
- 存储空间约束:早期 x86 内核映像存储在软盘/硬盘的有限扇区中,压缩可减小映像体积 2~4 倍。
- 加载速度平衡:I/O 读取压缩映像的时间 + 解压时间 < 直接读取未压缩映像的时间。
- 嵌入式场景:Flash/ROM 容量有限,压缩内核是标配。
工程事实:Linux 内核的 arch/x86/boot/compressed/ 目录专门存放解压缩 stub 代码,该 stub 在保护模式下执行,负责将内核主体从 .gz 或 .xz 格式解压到物理内存高位区域。
1.2.2 为什么需要地址重定位?
内核在编译链接时假设一个“链接地址”(Link Address),但实际加载到物理内存时可能不在该地址。重定位修正所有绝对地址引用,使代码能在任意基地址正确执行。
1.2.3 不解压缩/不重定位的后果
| 场景 | 后果 | 典型现象 |
| 跳过解压缩 | CPU 执行压缩数据流,指令乱码 | #UD 异常 / 三重故障 |
| 链接地址 ≠ 加载地址 | 绝对跳转指向错误地址 | Page Fault / 死循环 |
| 数据段未重定位 | 全局变量访问越界 | 内核 panic / 数据损坏 |
| 栈指针未修正 | 栈操作破坏内核代码 | 不可恢复崩溃 |
1.3 课程目标与前置知识
1.3.1 课程目标
- 掌握 x86 实模式 → 保护模式 → 分页模式的切换汇编实现。
- 理解 gzip/LZMA 解压缩算法在 x86 环境下的适配原理。
- 实现完整的地址重定位流程:从 ELF 解析到符号修正。
- 构建一个可运行的最小内核解压缩 + 重定位工程案例。
- 熟练使用 GDB + QEMU 调试启动阶段的汇编代码。
1.3.2 前置知识清单
| 领域 | 具体要求 | 推荐资源 |
| x86 汇编 | 熟悉 MOV/JMP/CALL/LODSB/STOSB 等基本指令;理解段寄存器、EFLAGS | 《x86 汇编语言:从实模式到保护模式》 |
| C 语言 | 指针运算、结构体、内联汇编(asm volatile) | 《C 程序设计语言》K&R |
| 计算机体系结构 | 内存层次、MMU、TLB、缓存一致性基础 | 《深入理解计算机系统》CS:APP |
| ELF 文件格式 | 节头表、程序头表、重定位表结构 | ELF 规范 / man 5 elf |
| 工具链 | GCC 交叉编译、LD 链接脚本、Makefile、QEMU、GDB | GNU 工具链文档 |
⚠ 重要提示:本课程不从头教授 x86 汇编基础。如果你对 lgdt、mov cr0, eax、jmp far ptr 等指令不熟悉,请先完成前置学习。课程中所有汇编代码均使用 AT&T 语法(GAS),与 Intel 语法有差异,请提前适应。
1.3.3 课程工程环境
- 主机:Linux (Ubuntu 22.04+ / Debian 12+)
- 交叉编译器:i386-elf-gcc (或 x86_64-linux-gnu-gcc -m32)
- 模拟器:QEMU 7.0+ (qemu-system-i386)
- 调试器:GDB 12+ (支持 target remote)
- 构建工具:GNU Make 4.0+
课程定位:这不是理论课,而是“手写汇编 + 调试 + 排错”的工程实战。每章都包含可编译、可运行、可调试的代码案例。你将在 QEMU 中亲眼看到解压缩前后的内存变化,以及重定位如何修正符号地址。