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

磁盘加载至不同内存后汇编代码失效问题求助

问题

汇编代码在引导扇区内可成功切换至32位模式并打印字符,但通过磁盘读取加载到下一扇区后,即便使用了org指令,GDT表地址仍出现偏移,导致打印失败且程序进入启动循环。

已确认磁盘读取操作成功,两种场景下代码完全一致,仅引导扇区使用org 0x7C00,第二扇区使用org 0x7E00。尝试过移除org指令、将dd gdt_start替换为dd gdt_start + 0x7E00、硬编码精确地址,均未解决问题。

项目详情

  • 编写x86_64汇编代码
  • 使用QEMU运行

正常工作时GDT表地址为起始位置后3字节(即7C03);失效时GDT地址为000f61e0 00000037,与正确地址差距极大。

第二阶段代码

BITS 16
org 0x7E00

cli

jmp code

gdt_start:
    dq 0x0000000000000000   ; Null descriptor
    dq 0x00CF9A000000FFFF   ; Code segment
    dq 0x00CF92000000FFFF   ; Data segment
gdt_end:

gdt_descriptor:
    dw gdt_end - gdt_start - 1   ; Size
    dd gdt_start


code:

lgdt [gdt_descriptor]

mov eax, cr0
or eax, 1       ; Change PE (Protection Enable) bit if 0
mov cr0, eax

jmp 0x08:protected_mode

[BITS 32]

; Registers are 16-bit and map to 32-bit addresses through GDT table

protected_mode:
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

mov eax, 0xB8000
mov byte [eax], 'D'
mov byte [eax+1], 0x01


jmp $

第一阶段磁盘读取代码

BITS 16
org 0x7C00

jmp code

boot_drive: db 0

code:

mov byte [boot_drive], dl ;  dl is loaded at boot and has the source drive


xor ax, ax
mov ah, 0x02            ; Disk read
mov al, 3              ; Sector read count
mov ch, 0               ; Cylinder
mov cl, 2               ; Sector
mov dh, 0               ; Head
mov dl, [boot_drive]    ; Drive
mov es, ax              ; Segment
mov bx, 0x7E00          ; Offset
int 0x13                ; Disk service

jc error                ; Jmp if CF = 1


jmp 0x0000:0x7e00

error:

mov  ah, 0x0E
mov  al, 'E'
mov  bh, 00
mov  bl, 0x07
int  0x10

times 510 - ($ - $$) db 0

dw 0xAA55
解决方案

问题核心是实模式下未正确设置ds寄存器,导致lgdt指令计算GDT物理地址时使用了错误的段基址,最终指向了错误的内存位置。

具体原因

实模式中,lgdt [gdt_descriptor]会以当前ds寄存器的值作为段基址,加上gdt_descriptor的偏移得到物理地址。第二阶段代码跳转后,ds寄存器继承自第一阶段的未知值(BIOS启动时ds不一定为0),而非我们预期的0,所以计算出的GDT地址完全错误。

修复步骤

  1. 在第二阶段的code标签开头,先将ds寄存器设置为0,确保实模式下地址计算的正确性:
code:
    xor ax, ax
    mov ds, ax          ; 设置ds为0,匹配org 0x7E00的地址假设
    lgdt [gdt_descriptor]
  1. 保留org 0x7E00指令,汇编器会自动计算gdt_start的绝对物理地址,无需手动修改dd gdt_start。

修复后的第二阶段代码

BITS 16
org 0x7E00

cli

jmp code

gdt_start:
    dq 0x0000000000000000   ; Null descriptor
    dq 0x00CF9A000000FFFF   ; Code segment
    dq 0x00CF92000000FFFF   ; Data segment
gdt_end:

gdt_descriptor:
    dw gdt_end - gdt_start - 1   ; Size
    dd gdt_start


code:
    xor ax, ax
    mov ds, ax          ; 关键:确保ds段基址为0
    lgdt [gdt_descriptor]

    mov eax, cr0
    or eax, 1           ; 置位PE位,切换到保护模式
    mov cr0, eax

    jmp 0x08:protected_mode

[BITS 32]

protected_mode:
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    mov eax, 0xB8000
    mov byte [eax], 'D'
    mov byte [eax+1], 0x01

    jmp $

额外检查点

  • 第一阶段中mov al, 3表示读取3个扇区,若第二阶段代码小于512字节,可改为读取1个扇区,减少不必要的磁盘操作。
  • 可在第二阶段开头添加实模式打印代码(如调用int 0x10打印字符),验证代码是否在正确位置执行,同时确认ds设置是否生效。

内容的提问来源于stack exchange,提问作者Adi Mehmedičević

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 22:44:51