在 GDB(实模式,附加到 QEMU)中,在特定条件下的特定点 stepo 和 nexti 似乎打破了断点机制



最小工作示例源:

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并在应该中断的位置中断。但是,使用steponexti,执行确实可以继续而不会出现问题,但不会到达断点。GDB只是进入了无休止的等待。如果在nop之后手动插入断点(例如,在retnjmp @start,调用返回的指令带有retn),GDB 在该断点处中断而不会出现问题。此时列出所有断点不会产生新的断点,因此似乎nexti没有在任何地方放置临时断点(或者未列出临时断点?

经过大量调查,以下是该问题的奇怪"修复程序">nexti自动跳过nop并在那里中断:(1)set $ebp = 0x7bfa然后nexti(2)注释掉mov bp,sp行。

问题是:

  1. 为什么nexti似乎打破了该特定位置的断点机制?也许在指令之间放置了一个断点?或者nexti以某种方式遇到 16 位代码的问题?
  2. 为什么上面概述的奇怪"修复"有效?如果在初始化后的任何时候都没有使用$ebp,它怎么可能与任何东西有任何关系?GDB 是否在内部使用它?

以下 gdb 命令启用远程协议调试,并导致显示 gdb 和 QEMU 之间的交换 gdbserver:set debug remote 1。 QEMU在交换中的回复看起来很合理,但是gdb最终在地址0x5ea7c17设置断点,这是错误的。看起来它将sp处的 4 个字节解释为返回地址,而不仅仅是 2。0x7c17 是实际返回地址,0xea 和 0x5 是引导扇区代码的前两个字节。

最新更新