Monday, December 14, 2009

Debugging non-native programs with QEMU + GDB

Here, I expand further on the topic of QEMU user-mode emulation.
You probably noticed that QEMU doc mentions the option "-g port", which mentions GDB, without much further info.

Also, if you've been curious enough, you might have tried running GDB within your (say) ARM Debian chroot. If you have not, let me demonstrate:
This GDB was configured as "arm-linux-gnueabi".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
...
Reading symbols from /root/test...done.
(gdb) break main
Breakpoint 1 at 0x83ac: file test.c, line 5.
(gdb) start
Temporary breakpoint 2 at 0x83ac: file test.c, line 5.
Starting program: /root/test
qemu: Unsupported syscall: 26
Hello World!
During startup program exited normally.
(gdb)

So - gdb doesn't work because of unsupported syscall sys_ptrace. But as QEMU supports the GDB remote protocol, you can use that for debugging.

Apart form QEMU you also need a cross GDB built for your target. You can either build your own, or look here for some tips for Gentoo or Debian. Or get it from CodeSourcery.

It might be a good idea to look into the GDB manual for some remote debugging background first.

Start QEMU like this (either on a static binary, or inside foreign chroot):
$ qemu-arm -g 1234 your-binary
You can use any port number you want (as long as it's not occupied already). Then you have to use the same when telling GDB to connect to this instance of QEMU.

Launch the cross GDB:
$ armel-unknown-linux-gnu-gdb
GNU gdb (Gentoo 7.0 p1) 7.0
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=armel-unknown-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.gentoo.org/>.
(gdb)

Now you need to tell GDB a few things:
  • what file you're debugging: use the file command for that
  • where to look for dynamic libraries: use the command set sysroot (you can get around this if the binary you're debugging is static; for older gdb use set solib-absolute-prefix)
  • where to look for sources: use the command dir (if it's not in the current directory)
  • how to connect to QEMU: use target remote localhost:1234 (host:port is the general syntax)
  • it seems GDB doesn't take sysroot into account when looking for debug symbols in case those were separated from the main libraries - so tell GDB explicitly where to look for them with set debug-file-directory
This implies you need to see (or have a current copy of) the binary and possible libraries the binary uses. In case of the foreign chroot that's easy -- you use the chroot path as the solib-absolute-prefix. Here's an example:
(gdb) file /mnt/data/debian-armel/root/test
Reading symbols from /mnt/data/debian-armel/root/test...done.
(gdb) set sysroot /mnt/data/debian-armel/
(gdb) set debug-file-directory /mnt/data/debian-armel/usr/lib/debug
(gdb) dir /mnt/data/debian-armel/root/
Source directories searched: /mnt/data/debian-armel/root:$cdir:$cwd
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
warning: Can not parse XML target description; XML support was disabled at compile time
[New Remote target]
[Switching to Remote target]
0x400817e0 in ?? () from /mnt/data/debian-armel/lib/ld-linux.so.3
(gdb) break main
Breakpoint 1 at 0x83ac: file test.c, line 5.
(gdb) c
Continuing.

Breakpoint 1, main () at test.c:5
5 puts("Hello World!");
(gdb) c
Continuing.

Program exited normally.
(gdb) quit
You can automate the set up via a .gdbinit file.

No comments:

Post a Comment