Tip: Poor man’s qemu breakpoint

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
(gdb) cont
Continuing.
^C
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
   0x000c1043:	cli    
   0x000c1044:	cld    
   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
(gdb) si
0x0000062c in ?? ()
(gdb) si
0x0000062e in ?? ()
(gdb) si
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
(gdb) cont
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
0x0000064a in ?? ()

Note:
* 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.

Advertisement

2 Comments

Filed under Uncategorized

2 responses to “Tip: Poor man’s qemu breakpoint

  1. Laszlo Ersek

    Years ago I tried to use this trick. Namely, I was trying to debug some hairy mode switch code (well, is there any other kind of mode switch code?) in edk2, that is, going from real mode to protected mode to long mode. Something was wrong and the mode switching caused the VM to reboot; I didn’t know which step exactly.

    Of course such assembly is mostly undebuggable, so IIRC I tried to add the kind of infinite loop you mention, between the adjacent steps, with the argument being, if the VM hangs (spinning) instead of rebooting, then the steps before the loop are okay — they don’t cause the reboot.

    My debugging attempts failed. With Paolo’s help (I think — I’m fuzzy by now) it became clear that the jump instruction *itself* caused a reboot (triple fault IIRC) *even if* the instructions right before it were okay. (Something about the code segment being incorrectly set up, or whatever.) So, the behavior was indistinguishable from the symptoms.

    That’s when I decided, paraphrasing a Hungarian saying in English, that mode switching be best written by whoever has two moms (“írjon ilyet, akinek két anyja van”) 🙂

    Hm, wait, since we’re doing open source, I can actually give you a link, if you are interested:

    http://thread.gmane.org/gmane.comp.bios.tianocore.devel/5343/focus=5386

    I do agree that this technique can be useful whenever a jump instruction is supposed to work. (Which is “almost always”, I guess :))

  2. rich

    Instruction prefetch buffer is filled and can run instructions, but as soon as you jump it needs to refill that, which fails because %cs not set up mumble mumble?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.