x86实模式下远跳转操作错误定位问题求助
问题分析与解决
你的问题核心是引导扇区复制到0x90000后,远跳转指令执行结果不符合预期,实际跳转到0x0001C而非0x9001C。以下是具体分析和修复方案:
关键问题排查
1. 注释未闭合导致代码失效
看go:标签后的注释:
/* now we are in the 0x90000 mov %ax, %cs mov %ax, %ds mov %ax, %es mov %ax, %ss mov $0xffff, %sp
这段注释缺少闭合的*/,导致后续设置段寄存器的代码被全部注释。虽然这不是跳转错误的直接原因,但会导致后续访问数据时使用原段地址(0x07C0),而非新段地址(0x9000),引发潜在问题。
2. GDB反汇编的误导
调试输出中x/4i 0x7c17的反汇编结果是错误的,因为GDB默认按32位模式解析指令,而你的代码是16位实模式。查看机器码0xea 0x1c 0x00 0x00 0x90,这是正确的16位远跳转指令:
ea:16位远跳转操作码0x1c 0x00:小端序的IP偏移(0x001C)0x00 0x90:小端序的CS段值(0x9000)
对应的线性地址是0x9000*16 + 0x001C = 0x9001C,指令本身是正确的。
3. 跳转后段寄存器未正确设置
虽然远跳转已经将CS设置为0x9000,但DS、ES、SS仍保留原段值0x07C0,后续访问boot_dirver、packet等数据时会访问原引导扇区地址,而非复制后的新地址。此外,直接mov %ax, %cs是非法操作,实模式下CS只能通过跳转、调用指令修改。
修复方案
1. 修复注释,正确设置段寄存器
将go:后的注释闭合,并移除非法的mov %ax, %cs指令,正确设置段寄存器:
go: /* now we are in the 0x90000 */ mov $0x9000, %ax mov %ax, %ds mov %ax, %es mov %ax, %ss mov $0xffff, %sp mov $20, %cx mov $msg_boot, %ax call print_msg
2. 验证跳转后的寄存器状态
在GDB中执行跳转后,使用info registers查看CS和IP的值,确认是否为cs 0x9000、ip 0x001c。如果是,说明跳转实际正确,GDB的地址显示是因为未识别实模式导致的误导。
3. 确认复制操作正确性
在跳转前,对比原地址和目标地址的内容,确保复制完整:
x/512b 0x7c00 x/512b 0x90000
如果两段内容完全一致,说明复制操作正确。
4. 正确生成16位指令
确保汇编器正确生成16位指令,使用GAS时,可在代码开头添加.code16并指定入口:
.code16 .global start start: mov $0x07c0, %ax mov %ax, %ds ...
额外注意事项
- 实模式下访问数据时,必须确保DS/ES等段寄存器指向正确的段地址,避免访问原引导扇区数据。
- 远跳转指令会自动刷新指令流水线,无需额外操作。
- 0x90000属于1MB以内地址,无需开启A20线即可访问。
内容的提问来源于stack exchange,提问作者ColorGlass
相关产品推荐
相关产品推荐

