最小工作示例源:
use16
org 0x7c00
jmp 0x0000:@start
@start:
cli
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0x7c00
sti
mov bp,sp
call @fails
jmp @start
@fails:
nop
retn
times 510-($-$$) db 0
dw 0xAA55
以上内容通过 FASM 或 NASM 组装成二进制文件,并写入 VHD (x.vhd) 的 MBR。QEMU 在启用调试支持的情况下启动:qemu-system-i386.exe -m 512 -boot c -net nic -net user -hda x.vhd -no-acpi -s -S -cpu 486
在Cygwin中,使用 GDB9.1-1,然后发出以下命令来运行 GDB 并附加到 QEMU,识别架构,跳过 BIOS 代码并在 MBR 开始时设置断点,在 0x7c00 加载,然后再继续:
gdb
target remote localhost:1234
set architecture i8086
stepi 11
break *0x7c00
continue
nexti 10
此时,$ip位于nop
线。
@fails:
nop ; <---
retn
这是遇到问题的地方。使用stepi
,在retn
行上放置一个临时断点,执行继续通过nop
并在应该中断的位置中断。但是,使用stepo
或nexti
,执行确实可以继续而不会出现问题,但不会到达断点。GDB只是进入了无休止的等待。如果在nop
之后手动插入断点(例如,在retn
或jmp @start
,调用返回的指令带有retn
),GDB 在该断点处中断而不会出现问题。此时列出所有断点不会产生新的断点,因此似乎nexti
没有在任何地方放置临时断点(或者未列出临时断点?
经过大量调查,以下是该问题的奇怪"修复程序">,nexti
自动跳过nop
并在那里中断:(1)set $ebp = 0x7bfa
然后nexti
或(2)注释掉mov bp,sp
行。
问题是:
- 为什么
nexti
似乎打破了该特定位置的断点机制?也许在指令之间放置了一个断点?或者nexti
以某种方式遇到 16 位代码的问题? - 为什么上面概述的奇怪"修复"有效?如果在初始化后的任何时候都没有使用$ebp,它怎么可能与任何东西有任何关系?GDB 是否在内部使用它?
以下 gdb 命令启用远程协议调试,并导致显示 gdb 和 QEMU 之间的交换 gdbserver:set debug remote 1
。 QEMU在交换中的回复看起来很合理,但是gdb最终在地址0x5ea7c17设置断点,这是错误的。看起来它将sp
处的 4 个字节解释为返回地址,而不仅仅是 2。0x7c17 是实际返回地址,0xea 和 0x5 是引导扇区代码的前两个字节。