正常
🧠 一、项目背景(你可以这样开场)
我实现了一个基于 x86 架构的自研操作系统 Bootloader(Stage2),其职责是:
- 从 MBR 接管控制权(0x7C00 → 0x8000)
- 完成基础硬件初始化(A20 / VESA)
- 构建 GDT
- 从实模式切换到保护模式
- 为后续内核加载做准备
该阶段的核心目标是:
👉 安全、可控地完成从实模式到保护模式的过渡
🔥 二、核心流程(一定要按这个顺序讲)
整个流程必须严格遵循:
text
1. 关闭中断(CLI)
2. 初始化段寄存器
3. 构建 GDT
4. 加载 GDT(LGDT)
5. 设置 CR0.PE = 1
6. 执行 far jump(刷新 CS)
7. 进入保护模式初始化段寄存器⚠️ 三、核心技术点与坑点(重点)
🔴 1️⃣ 16 位 vs 32 位(本质冲突)
❗问题本质:
x86 在 boot 阶段是:
text
实模式(16位)但操作系统需要:
text
保护模式(32位)⚠️ 关键问题:
👉 指令宽度 + 调用约定必须一致
❌ 错误行为:
.code16gcc混用导致 32 位指令编码pushw / pushl混用call / ret不匹配
💥 后果:
- 栈错位
- SP 异常(例如 0x000a)
- 返回地址错误 → CPU 跑飞
✅ 正确做法:
统一:
asm
.code16
pushw / popw
call / ret(16位)💡 面试总结:
在 bootloader 中,必须严格统一指令宽度与调用约定,否则会导致栈结构被破坏,从而引发控制流错误。
🔴 2️⃣ 栈破坏问题(你已经踩过)
❌ 表现:
text
SP 从 0xFFFE → 0x000a🔥 本质:
text
push / call / ret 宽度不一致💥 结果:
- 返回地址错
- 函数调用链断裂
- CPU 跳到随机地址执行
✅ 解决:
- 统一 pushw
- 禁止 pushl(除非进入32位)
🔴 3️⃣ GDT 构建与加载(lgdt)
📌 GDT 是什么?
👉 全局描述符表(Global Descriptor Table)
用于定义:
- 代码段
- 数据段
- 内存权限
📌 GDTR 结构:
text
limit (16bit)
base (32bit)❗lgdt 的本质:
text
加载 GDTR👉 但:
- ❌ 不校验内容
- ❌ 不检查合法性
⚠️ 常见坑:
❌ GDT 地址错误
👉 因为:
text
real mode: 物理地址 = DS * 16 + offset❗必须确保:
asm
movw $0, %ds
lgdt gdt_ptr💡 面试说法:
lgdt 只是加载 GDT 描述符,并不会验证其内容正确性,错误的 GDT 会在后续段访问时导致异常。
🔴 4️⃣ 进入保护模式(CR0 + far jump)
❗步骤1:设置 PE 位
asm
movl %cr0, %eax
orl $0x1, %eax
movl %eax, %cr0❗步骤2:必须 far jump
asm
ljmp $0x08, $pm_entry🔥 为什么必须跳?
👉 因为:
- CS 没刷新
- CPU 仍在旧模式执行
💥 不跳会发生:
- 指令执行异常
- triple fault
- 系统重启
💡 面试金句:
设置 CR0.PE 后必须执行 far jump 来刷新 CS,否则 CPU 不会真正进入保护模式。
🔴 5️⃣ .code16 / .code32(汇编模式陷阱)
❗误区:
你之前写:
asm
.code32
lgdt gdt_ptr🔥 真相:
👉 .code16 / .code32 只是:
👉 汇编器指令,不是 CPU 指令
💥 CPU 仍然在:
- 实模式
👉 结果:
❗指令编码与 CPU 状态不匹配 → 执行错误
✅ 正确:
- 在切换前:
.code16 - 进入保护模式后:
.code32
🔴 6️⃣ far jump 地址错误(严重坑)
❌ 错误:
asm
pm_entry - 0x8000❗问题:
👉 这是“物理地址误差”
🔥 本质:
- bootloader 已经被加载到正确地址
- 不需要手动减偏移
💥 后果:
- 跳到错误地址
- CPU执行垃圾指令
✅ 正确:
asm
ljmp $selector, $offset🔴 7️⃣ GDB 无法识别寄存器(你遇到的)
❗现象:
text
Invalid register `ip`🔥 原因:
👉 当前 CPU 状态异常:
- 跑飞
- 非法执行流
- GDB架构不同步
✅ 正常情况:
| 模式 | 寄存器 |
|---|---|
| 实模式 | IP |
| 保护模式 | EIP |
🔴 8️⃣ CPU 跑飞(最终问题)
💥 本质:
❗控制流损坏(Control Flow Corruption)
触发原因:
- 栈错
- 指令错
- GDT错
- 跳转错
结果:
- 执行非法指令
- triple fault
- QEMU 重启
🧠 四、调试方法(工程能力体现)
✅ QEMU 调试
bash
qemu-system-x86_64 -s -S -no-reboot -d int✅ GDB 查看
plaintext
info registers
x/10i $cs:0✅ 断点
plaintext
b *0x8000
b *pm_entry