Skip to content

很好,这一步你做得非常对 👍 你现在其实已经积累了一整套**“真实踩坑经验”,我帮你整理成一份可以直接写进项目 README / 面试讲解的笔记**(我按“问题 → 本质 → 解决”结构给你梳理,保证你以后不再踩)。


🚀 Cinux Bootloader 踩坑总结(Real Mode 阶段)


🧨 坑1:段寄存器不匹配(最早那个)

❓现象

  • print_string 不输出
  • 单字符 int 0x10 正常

🧠 本质

text
物理地址 = segment << 4 + offset

但你当时:

text
CS ≠ DS

👉 字符串地址按 CS 计算 👉 访问用 DS:SI

👉 💥 读错地址


✅ 解决

asm
mov %cs, %ax
mov %ax, %ds
mov %ax, %es

🧨 坑2:Stage2 地址模型错误

❓现象

  • 能执行代码
  • 访问数据就炸

🧠 本质

你用了:

makefile
. = 0x8000

同时:

asm
DS = 0x0800

👉 地址变成:

text
0x8000 + 0x80xx → 💥 双重偏移

✅ 正确模型(二选一)

✔ 推荐(Stage2)

makefile
. = 0
asm
DS = CS

✔ MBR(特殊情况)

makefile
. = 0x7C00
DS = 0

🧨 坑3:栈踩内存(超级隐蔽)

❓现象

  • print 后飞
  • BIOS 调用后崩

🧠 本质

你把栈放在:

text
0x7B00(MBR 附近)

但:

  • BIOS 会用栈
  • 你的函数也在 push

👉 💥 覆盖关键内存


✅ 解决

asm
mov $0x9000, %sp

🧨 坑4:BIOS 会污染寄存器(重点)

❓现象

  • print 之后正常
  • 下一个函数炸

🧠 本质

text
BIOS int ≠ 函数调用

👉 会改:

  • DS / ES
  • FLAGS
  • BX / SI 等

✅ 解决(必须)

asm
pushf
push 所有寄存器
...
int 0x10
...
pop 全部

🧨 坑5:跨文件 + 16位重定位(你中期怀疑的)

❓现象

  • .data 引用炸
  • relocation truncated

🧠 本质

text
ELF + 16-bit + 多文件
→ R_386_16 限制

👉 symbol 地址可能不再是“段内偏移”


✅ 解决

👉 关键原则:

text
数据放调用者,函数放 common

🧨 坑6:MBR 超 512B(你最后这个大坑)

❓现象

text
放在 mbr.S 正常
放到 common.S → call 直接飞

🧠 本质(最关键)

text
BIOS 只加载 512B

但你:

text
mbr.S + serial.S 一起 link

👉 .text > 512B

👉 后半部分(common.S)根本没加载

👉 call 跳到:

text
未加载内存 → 💥

✅ 解决(标准架构)

✔ MBR:

text
极简(<512B)
不要链接 common.S

✔ Stage2:

text
所有功能都放这里

🧨 坑7:函数调用约定(你刚刚意识到的)

❓现象

  • 有时候能跑,有时候炸
  • 多调用就不稳定

🧠 本质

你从:

text
写代码

变成:

text
写“模块”

👉 必须定义:

  • 哪些寄存器能改
  • 哪些必须保存

✅ 当前阶段建议

👉 最简单策略:

text
所有函数:
保存全部寄存器

🚀 最终正确架构(你现在应该这样)


🔹 MBR(512B)

text
- 初始化段寄存器
- 设置栈
- 读 Stage2
- 跳转

👉 可选:

  • tiny print(无 push)

🔹 Stage2

text
- print_string(完整版)
- A20
- VESA
- GDT / PM

🔹 common.S

text
只放:
- 函数
不放:
- 关键数据(尤其给 MBR 用的)

🧠 最重要的三条认知(必须记住)


🧩 1️⃣ 地址模型

text
链接地址 ≠ 运行地址

🧩 2️⃣ BIOS 不是函数

text
会破坏寄存器

🧩 3️⃣ MBR 不是程序

text
MBR = 512B 跳板

035_multi_terminal-40-g5d72b8b · 5d72b8b · 2026-06-26