Skip to content

调试档案 003 · 进长模式的顺序与标志位陷阱

document/notes/003/1.md 提炼,配套 003 · 跨进长模式。从 32 位保护模式进 64 位长模式,几乎所有故障都归结到两类:进入序列的顺序错了,或某个页表/段描述符的标志位错了。下面是最典型的几个。

案例一:CR0.PG 一置位就三重故障

  • 症状:CR0 |= PG 那条指令执行完(或紧接着),机器三重故障重启,根本到不了远跳。
  • 原因:分页一开,所有地址翻译都走我们搭的页表;页表要是有问题,CPU 连下一条指令都翻译不出来。典型是:(1) 某层表没清零,残留字节被当成有效项去查;(2) PD 的大页项漏了 Large(PS)位,CPU 以为还要往下查 PT,查到不存在的 PT → 缺页;(3) 中间层指针写错地址,指向的不是下一张表。
  • 定位:置 CR0.PG 那条设断点,用 GDB 看三张表——x/1gx 0x1000PML4[0] 应是 0x2003;x/1gx 0x2000PDPT[0] 应是 0x3003;x/4gx 0x3000PD[0..3] 应是 0x83/0x200083/0x400083/0x600083 这种带 PS 位的 2MB 页。
  • 修复:setup_page_tables 里三张表先 rep stosl 清零;中间层项 |0x03、大页项 |0x83(务必带 0x80 的 Large 位)。
  • 防复发:搭页表时先清零再填,是铁律;大页项的 PS 位用常量 PAGE_FLAGS(present|writable|large)拼,别手写裸数字。

案例二:置 EFER.LME 或开 PG 时 #GP

  • 症状:执行到 wrmsrCR0.PG 置位时触发 #GP,而非页表缺页那种三重故障。
  • 原因:进入长模式的顺序是 Intel 定死的:CR4.PAEEFER.LMECR0.PG,缺一不可、不可乱序。常见错误:忘了开 CR4.PAE 就置 LME;或先开 CR0.PG 再置 LME(等于在还没"请求"长模式时分页就开了)。CPU 对这条序列的检查是硬件级的,违反就 #GP。
  • 定位:看 enter_long_mode 里几条 movl %cr* / rdmsr 的先后,和 SDM §9.8.1.1 的序列逐条对。
  • 修复:严格按 CR3CR4.PAEEFER.LMElgdtCR0.PG→远跳的顺序;改控制寄存器一律 orl 保留其它位。
  • 防复发:把这串序列当模板,顺序记死;EFER.LME 设了不生效、要等 CR0.PG 那拍才激活——理解了这点就不会乱排。

案例三:远跳进 long_mode_entry 后又崩

  • 症状:远跳看似成功(到了 64 位入口),但执行几条就崩,或 GDB 反汇编全乱。
  • 原因:64 位代码段描述符的 L/D 位错了。L=1(长模式代码段)时 D 必须为 0;0x00AF9A000000FFFF 的 flags 高 nibble 是 0xA(1010:G=1,D=0,L=1)。要是写成 0xC(1100:D=1,L=0),它就退化成普通 32 位段,CPU 不认它是长模式,用 64 位译码去跑 32 位段(或反之),译码错位崩掉。
  • 定位:GDB x/8bx &gdt_code64 看字节,第 7 个字节(byte[6])应是 0xAF(不是 0xCF/0x8F)。
  • 修复:确认 64 位代码描述符是 0x00AF9A000000FFFF(flags=0xA,L=1,D=0);远跳用 0x18 选子。
  • 防复发:32 位段、64 位段、数据段的 byte[6] 区分清楚——0xCF(32 位 code/data)、0xAF(64 位 code,L=1)、0x8F(64 位 data)。别混用。

案例四:链接报"64 位重定位"错误

  • 症状:链接 Stage2 时报 relocation truncated 或 unsupported 64-bit relocation 一类错误,根本产不出 stage2.bin
  • 原因:Stage2 按 32 位 ELF(elf_i386)链接,但 gdt64_ptr 里用了 .quad gdt 想表达 64 位 base,触发 32 位 ELF 不支持的 64 位重定位。这是纯工具链问题,和 CPU 无关。
  • 定位:看 gdt64_ptr 的定义,是不是用了 .quad
  • 修复:用 .long gdt + .long 0 两段拼出 64 位 base——GDT 在低地址,高 32 位是 0。
  • 防复发:32 位 ELF 里凡是"需要 64 位地址"的地方,都用 .long+.long 拼,不用 .quad

一句话总结

进长模式的故障,十有八九是顺序(PAE→LME→PG)标志位(大页 PS 位、64 位段的 L/D 位)。把"页表先清零再填 + 大页带 PS 位"、"CR 改位用 orl 保留其它位"、"64 位代码段 flags=0xA"这三条钉死,这一跳就稳了。

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