You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 04:05:05