I’ve written before about how you can use qemu + gdb to debug a guest. Today I was wondering how I was going to debug a problem in a BIOS option ROM, when Stefan Hajnoczi mentioned this tip: Insert
1: jmp 1b
into the code as a “poor man’s breakpoint”. In case you don’t know what that assembly code does, it causes a jump back (
b) to the previous
1 label. In other words, an infinite loop.
After inserting that into the option ROM, recompiling and rebooting the virtual machine, it hangs in the boot, and hitting
^C in gdb gets me straight to the place where I inserted the loop.
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x0000fff0 in ?? ()
(gdb) set architecture i8086
The target architecture is assumed to be i8086
Program received signal SIGINT, Interrupt.
0x00000045 in ?? ()
(gdb) info registers
eax 0xc100 49408
ecx 0x0 0
edx 0x0 0
ebx 0x0 0
esp 0x6f30 0x6f30
ebp 0x6f30 0x6f30
esi 0x0 0
edi 0x0 0
eip 0x45 0x45
eflags 0x2 [ ]
cs 0xc100 49408
ss 0x0 0
ds 0xc100 49408
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) disassemble 0xc1000,0xc1050
Dump of assembler code from 0xc1000 to 0xc1050:
0x000c103c: mov %cs,%ax
0x000c103e: mov %ax,%ds
0x000c1040: mov %esp,%ebp
0x000c1045: jmp 0xc1045
0x000c1047: jmp 0xc162c
0x000c104a: sub $0x4,%esp
0x000c104e: mov 0xc(%esp),%eax
End of assembler dump.
Look, my infinite loop!
I can then jump over the loop and keep single stepping*:
(gdb) set $eip=0x47
0x0000062c in ?? ()
0x0000062e in ?? ()
0x00000632 in ?? ()
I did wonder if I could take Stefan’s idea further and insert an actual breakpoint (
int $3) into the code, but that didn’t work for me.
Note to set breakpoints, the regular gdb
break command doesn’t work. You have to use hardware-assisted breakpoints instead:
(gdb) hbreak *0xc164a
Hardware assisted breakpoint 1 at 0xc164a
Program received signal SIGTRAP, Trace/breakpoint trap.
0x0000064a in ?? ()
* If you find that single stepping doesn’t work, make sure you are using qemu in TCG mode (
-M accel=tcg), as KVM code apparently cannot be single-stepped.