Skip to content

调试档案 001 · 实模式引导的几个老坑

这份档案从 document/notes/001/notes_mbr.md 的踩坑记录提炼而来,配套 001 · 实模式引导。实模式引导几乎每个坑都和"段式寻址"或"512 字节红线"有关,下面挑出最典型、最容易复发的三个,整理成可查的案例。

案例一:打印单字符正常,字符串却是一片乱码

  • 症状:INT 0x10 AH=0x0E 直接给 al 打单字符,屏幕能出来;一旦 movw $(msg), %si 去打字符串,出来的全是乱码,或者一个字都不动。
  • 原因:实模式下 物理地址 = 段 << 4 + 偏移。字符串标号 msg 链接后得到的是一个偏移,访问它靠的是 DS:SI。如果 CS 已经归零,但 DS 还是 BIOS 留下的值,DS:SI 算出来的物理地址根本不指向字符串,lodsb 读到的是垃圾。
  • 定位:先用单字符(AH=0x0E 直接给 al)验证输出通路通不通;单字符通、字符串不通,基本就锁定是段/指针问题。挂 GDB 看 DSSI 的实际值,手算 DS<<4+SI 是否落在字符串所在内存。
  • 修复:在 _start 开头把段寄存器统一理顺——movw %cs, %ax; movw %ax, %ds/es/ss/fs/gs,让"标号偏移"和"访问段"对得上。MBR 里还得先 ljmp $0, $real_startCS 钉成确定值,因为不同 BIOS 跳进 MBR 时给的 CS 不一样。
  • 防复发:把"段归一化"做成 MBR/Stage2 入口的固定开场动作,谁都不许省。写任何"标号 + 指针"的代码前,先确认指针的段寄存器指向正确。

案例二:Stage2 跳进去能执行,一访问数据就炸

  • 症状:MBR 远跳到 Stage2,_start 里前几条指令(设段、设栈)能跑;一旦访问 Stage2 自己的数据标号(字符串、常量),立刻死机或重启。
  • 原因:链接地址和运行时段没配合好,造成"双重偏移"。典型错误是把 Stage2 链接在 0x8000(绝对地址),运行时又设 DS=0x800,于是标号地址被算成 0x8000 + 0x80xx,double 了一次。
  • 定位:看链接脚本 . 的值和运行时设给 DS 的值,手算一个标号的最终物理地址,看是不是落在了 Stage2 实际加载的位置。GDB 单步到访问数据那条指令,看 DS:SI 算出的地址。
  • 修复:二选一的地址模型,别混用——
    • 绝对模型:链接 . = 实际载入地址,运行时 DS=0
    • 相对模型(Cinux 现行做法):链接 . = 0x0,运行时 DS=CS=载入地址>>4。这样 Stage2 不管被读到哪,只要段寄存器跟着改就行,更灵活。
  • 防复发:链接脚本里的 . 和入口设段的指令是一对契约,改其一必须同步另一个。Stage2 的 . = 0x0DS=0x800 是配死的,别只动一边。

案例三:MBR 里 call 一个函数就重启

  • 症状:某段打印/工具函数,放进 Stage2 里调用一切正常;只要让 MBR 去 call 它,就死机或三重故障重启。
  • 原因:BIOS 只加载第 0 扇区的 512 字节。如果把功能较多、带 VESA/A20 的 common/serial.S 也链进 MBR,MBR 的 .text 轻易就超过 512 字节,超出的部分根本没被读进内存。call 跳过去,执行的是一坨未初始化的随机内存。
  • 定位:objdump -d mbr.bin 或直接看 mbr.bin 文件大小;也可以看 .org 510 处的 0xAA55 魔数有没有被代码挤没(魔数错位 = 代码溢出)。
  • 修复:MBR 只链 mbr.S 自身,功能搬进 Stage2。MBR 里需要一个打印时,写一个极简、不 push 寄存器的版本(print_string_mbr),专为 512 字节预算服务;功能完整的 print_string(带保护)留给 Stage2 用。
  • 防复发:boot/CMakeLists.txt 里把这条红线钉死——add_executable(mbr mbr.S) 只有 mbr.S,common/serial.S 只进 stage2。构建期就可以用 mbr.bin 大小 = 512 来兜底,超了就让它构建失败,别留到运行时炸。

一句话总结这三个坑的共同根源

实模式引导阶段,90% 的玄学故障来自两件事:段式寻址把"地址"拆成了"段 + 偏移"两半(任一半错位都会算错地址),以及 512 字节的 MBR 是 BIOS 定的硬天花板(超出的代码等于不存在)。把这两条刻进脑子里,后面 002 进保护模式前还要再受它们最后一次折磨。

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