QEMU and GDB in long mode

From OSDev Wiki
Jump to navigation Jump to search

If you're running in Long Mode and using QEMU as your emulator you'll likely encounter the following error trying to use QEMU's gdb-stub:

Remote 'g' packet reply is too long: ...

This error happens when QEMU has switched to long mode. There are two workarounds for this: you can either disconnect and reconnect to force GDB to use the x86_64 architecture or patch GDB to switch the architecture on the fly.

Workaround 1: Reconnecting

When you start GDB, connect to QEMU with these commands:

$ gdb
(gdb) set arch i386:x86-64:intel
(gdb) target remote localhost:1234
(gdb) symbol-file YOUR_KERNEL_FILE
(gdb) break SOME_FUNCTION_IN_LONG_MODE
(gdb) continue

When the break point is hit and the "packet reply is too long" happens, run the following:

(gdb) disconnect
(gdb) set arch i386:x86-64
(gdb) target remote localhost:1234

Note that you must use different architecture names in those two "set arch" commands.

Workaround 2: Patching GDB

This is a slightly modified version of the patch posted in this thread. As a bonus, this patch produces a warning when it suspects that the mode switch happened.

[NOTE]: There is an updated version (for GDB 12.1) of this patch at https://github.com/mduft/tachyon3/blob/master/tools/patches/gdb-12.1-archswitch.patch - this is confirmed working with QEMU 7.2.0.

--- gdb/remote.c  	2016-04-14 11:13:49.962628700 +0300
+++ gdb/remote.c	2016-04-14 11:15:38.257783400 +0300
@@ -7181,8 +7181,28 @@
   buf_len = strlen (rs->buf);
 
   /* Further sanity checks, with knowledge of the architecture.  */
+// HACKFIX for changing architectures for qemu. It's ugly. Don't use, unless you have to.
+  // Just a tiny modification of the patch of Matias Vara (http://forum.osdev.org/viewtopic.php?f=13&p=177644)
   if (buf_len > 2 * rsa->sizeof_g_packet)
-    error (_("Remote 'g' packet reply is too long: %s"), rs->buf);
+    {
+      warning (_("Assuming long-mode change. [Remote 'g' packet reply is too long: %s]"), rs->buf);
+      rsa->sizeof_g_packet = buf_len ;
+
+      for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
+        {
+          if (rsa->regs[i].pnum == -1)
+            continue;
+
+          if (rsa->regs[i].offset >= rsa->sizeof_g_packet)
+            rsa->regs[i].in_g_packet = 0;
+          else
+            rsa->regs[i].in_g_packet = 1;
+        }
+
+      // HACKFIX: Make sure at least the lower half of EIP is set correctly, so the proper
+      // breakpoint is recognized (and triggered).
+      rsa->regs[8].offset = 16*8;
+    }
 
   /* Save the size of the packet sent to us by the target.  It is used
      as a heuristic when determining the max size of packets that the

The patched GDB emits the error as a warning but continues to function across the mode change. This patch has been tested with gdb 7.11 and 7.12.

Alternatively, you can patch QEMU so that you can use gdb unpatched. Be warned, this probably breaks support for debugging 32-bit code (also, compiling QEMU is hour-long task). In order to do this, replace each occurence of if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) { in the file gdbstub.c with if (TARGET_LONG_BITS == 64) {. However, this method originates from a thread back in 2011, and is untested since.

Workaround 3: Connecting from GDB after long mode has been enabled

This is an easy solution if you don't care to loose a few seconds of computation at the beginning of the emulation. The problem is that gdb is too dumb to understand that processor execution mode changed, so to workaround it you just connect after long mode has been enabled, and gdb never sees the switch. You can create this debug-with-qemu.sh script (or put this in a make target):

    #!/bin/env bash

    # use setsid so that ctrl+c in gdb doesn't kill qemu
    setsid qemu-system-x86_64 -s -boot d -cdrom kernel.iso &
    sleep 5
    gdb kernel.bin -x qemudbg

where your qemudbg should have something like this:

    target remote localhost:1234

and that's it (as long as you don't switch back and forth between modes).

Other issues

Some have experienced issues with breakpoints (using the break command) not firing. Use hbreak instead.

In case of using gentoo, this error might occur when emerging sys-devel/gdb [1] without the xml USE flag.

See also

Threads