x86 AT&T汇编保护模式下字符输出失败问题排查
x86 AT&T汇编保护模式无输出问题排查
核心错误与修复方案
1. 显存地址加载语法错误
32位代码段PMode中的mov 0xb8000,%edi违反AT&T语法规则:
- AT&T语法中,立即数必须加
$前缀;不加$会被解析为内存寻址,即读取0xb8000地址的内容到EDI,而非将显存基址值加载到寄存器。 - 修正:
mov $0xb8000,%edi
2. 缺少数据段描述符及段寄存器初始化
进入保护模式后,仅CS通过远跳设置为合法代码段选择子,DS、ES、SS等寄存器仍保留实模式段值,会触发保护模式异常:
- 首先在GDT中添加数据段描述符:
gdt: .quad 0x0000000000000000 # Null descriptor # 内核代码段 .word 0xFFFF # lower 16 bits of segment size .word 0x0000 # lower 16 bits of base address .byte 0x00 # middle 8 bits of base address .byte 0x9A # access byte: ring 0, executable only .byte 0xCF # granularity (0xC) .byte 0x00 # higher 8 bits of base address # 内核数据段 .word 0xFFFF # 段大小低16位 .word 0x0000 # 基地址低16位 .byte 0x00 # 基地址中8位 .byte 0x92 # 访问字节:环0,可读可写 .byte 0xCF # 粒度+段大小高4位 .byte 0x00 # 基地址高8位 gdt_end:
- 进入保护模式后,初始化段寄存器:
.code32 PMode: mov $0x10,%eax # 数据段选择子(GDT中第三个描述符,索引1,RPL0) mov %eax,%ds mov %eax,%es mov %eax,%ss mov $0xb8000,%edi # 修正后的显存地址加载 movb $'!',(%edi) inc %edi movb $0x07,(%edi) # 添加$前缀,正确传递属性字节立即数 inc %edi
3. 实模式栈未初始化
_start开头未设置栈指针,实模式下call A20依赖栈保存返回地址,SP指向不确定位置会导致程序崩溃:
- 在
cli后添加栈初始化:
_start: cli mov $0x7c00,%sp # 将栈设置到MBR加载地址上方的安全区域 call A20 lgdt gdtd # ... 后续切换保护模式代码
4. A20启用顺序问题
当前先加载GDT再启用A20,正确顺序应为先开启A20,再加载GDT并切换保护模式,避免A20未开启时访问超1MB内存的潜在问题:
_start: cli mov $0x7c00,%sp call A20 # 优先启用A20 lgdt gdtd # 再加载GDT mov %cr0,%eax or $1,%al mov %eax,%cr0 ljmp $0x08, $PMode
5. 立即数前缀遗漏
movb 0x07,(%edi)中0x07是属性字节立即数,未加$会被解析为读取0x07地址的内容,修正为:movb $0x07,(%edi)
修正后完整代码
.code16 .section .text A20: call waitA20_1 mov $0xAD,%al out %al,$0x64 call waitA20_1 mov $0xD0,%al out %al,$0x64 call waitA20_2 in $0x60,%al push %eax call waitA20_1 mov $0xD1,%al out %al,$0x64 call waitA20_1 pop %eax or $2,%al out %al,$0x60 call waitA20_1 mov $0xAE,%al out %al,$0x64 call waitA20_1 ret waitA20_1: in $0x64,%al test $2,%al jnz waitA20_1 ret waitA20_2: in $0x64,%al test $1,%al jz waitA20_2 ret .globl _start _start: cli mov $0x7c00,%sp call A20 lgdt gdtd mov %cr0,%eax or $1,%al mov %eax,%cr0 ljmp $0x08, $PMode .code32 PMode: mov $0x10,%eax mov %eax,%ds mov %eax,%es mov %eax,%ss mov $0xb8000,%edi movb $'!',(%edi) inc %edi movb $0x07,(%edi) inc %edi hang: hlt jmp hang .section .data .align 8 gdt: .quad 0x0000000000000000 # Null descriptor # 内核代码段 .word 0xFFFF # lower 16 bits of segment size .word 0x0000 # lower 16 bits of base address .byte 0x00 # middle 8 bits of base address .byte 0x9A # access byte: ring 0, executable only .byte 0xCF # granularity (0xC) .byte 0x00 # higher 8 bits of base address # 内核数据段 .word 0xFFFF # 段大小低16位 .word 0x0000 # 基地址低16位 .byte 0x00 # 基地址中8位 .byte 0x92 # 访问字节:环0,可读可写 .byte 0xCF # 粒度+段大小高4位 .byte 0x00 # 基地址高8位 gdt_end: gdtd: .word gdt_end - gdt - 1 .long gdt
内容的提问来源于stack exchange,提问作者oahu8846
相关产品推荐
相关产品推荐

