diff mbox

Workaround to the real-mode/protected-mode conflict in GDB.

Message ID 1327408823-7181-1-git-send-email-laurent@vivier.eu
State New
Headers show

Commit Message

Laurent Vivier Jan. 24, 2012, 12:40 p.m. UTC
When qemu is started with '-S' to freeze at startup and gdb is connected to
inner gdb server, gdb is using by default 16-bit real-mode. Then if we put
a breakpoint inside kernel linux, the CPU is switched to 32/64-bit protected
mode but GDB is always in 16 bit real-mode. The size of the registers dump
differs and GDB is not able to manage it.

See the following thread for more details:

"Failed to use gdb with qemu 15.1 (with and without kvm support)"
http://permalink.gmane.org/gmane.comp.emulators.qemu/133120

By adding a breakpoint at startup, we can connect GDB to qemu gdb server
when CPU is already in protected mode.

Example:

- find the address of the symbol to stop on.

  $ nm ../linux/vmlinux | grep start_kernel
ffffffff81ad391f T start_kernel
ffffffff81ad334a T x86_64_start_kernel
ffffffff81ad6a9c T xen_start_kernel

- Then start qemu with the breakpoint option (-hb):

  ./x86_64-softmmu/qemu-system-x86_64 -kernel ../linux/arch/x86/boot/bzImage \
                                      -append console=ttyS0 -nographic \
                                      -hb 0xffffffff81ad391f

- and now start GDB:

  $ gdb
  (gdb) set arch i386:x86-64
  The target architecture is assumed to be i386:x86-64
  (gdb) sym vmlinux
  Reading symbols   from /home/laurent/linux/vmlinux...done.
  (gdb) target remote :1234
  Remote debugging using :1234
  start_kernel () at init/main.c:468
  468   {
  (gdb) x/i $pc
  => 0xffffffff81ad391f <start_kernel>: push   %rbp
  (gdb) c

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 gdbstub.c       |    5 ++++-
 gdbstub.h       |    2 +-
 monitor.c       |    2 +-
 qemu-options.hx |   10 ++++++++++
 vl.c            |   11 ++++++++++-
 5 files changed, 26 insertions(+), 4 deletions(-)

Comments

Francis Moreau Jan. 24, 2012, 12:54 p.m. UTC | #1
Hello,

On Tue, Jan 24, 2012 at 1:40 PM, Laurent Vivier <laurent@vivier.eu> wrote:
>
> By adding a breakpoint at startup, we can connect GDB to qemu gdb server
> when CPU is already in protected mode.
>

Not tried yet, but usefull workaround IMHO.

Thanks !
diff mbox

Patch

diff --git a/gdbstub.c b/gdbstub.c
index 640cf4e..e27c262 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -2860,7 +2860,7 @@  static void gdb_sigterm_handler(int signal)
 }
 #endif
 
-int gdbserver_start(const char *device)
+int gdbserver_start(const char *device, unsigned long long hbreak)
 {
     GDBState *s;
     char gdbstub_device_name[128];
@@ -2916,6 +2916,9 @@  int gdbserver_start(const char *device)
     s->state = chr ? RS_IDLE : RS_INACTIVE;
     s->mon_chr = mon_chr;
 
+    if (hbreak != (unsigned long long)-1) {
+        gdb_breakpoint_insert(hbreak, 1, GDB_BREAKPOINT_HW);
+    }
     return 0;
 }
 #endif
diff --git a/gdbstub.h b/gdbstub.h
index d82334f..25d34ed 100644
--- a/gdbstub.h
+++ b/gdbstub.h
@@ -35,7 +35,7 @@  void gdb_register_coprocessor(CPUState *env,
 #ifdef CONFIG_USER_ONLY
 int gdbserver_start(int);
 #else
-int gdbserver_start(const char *port);
+int gdbserver_start(const char *port, unsigned long long hbreak);
 #endif
 
 /* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */
diff --git a/monitor.c b/monitor.c
index 1be222e..208624b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1137,7 +1137,7 @@  static void do_gdbserver(Monitor *mon, const QDict *qdict)
     const char *device = qdict_get_try_str(qdict, "device");
     if (!device)
         device = "tcp::" DEFAULT_GDBSTUB_PORT;
-    if (gdbserver_start(device) < 0) {
+    if (gdbserver_start(device, -1) < 0) {
         monitor_printf(mon, "Could not open gdbserver on device '%s'\n",
                        device);
     } else if (strcmp(device, "none") == 0) {
diff --git a/qemu-options.hx b/qemu-options.hx
index b3db10c..c0d2435 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2194,6 +2194,16 @@  Shorthand for -gdb tcp::1234, i.e. open a gdbserver on TCP port 1234
 (@pxref{gdb_usage}).
 ETEXI
 
+DEF("hb", HAS_ARG, QEMU_OPTION_breakpoint, \
+    "-hb addr        set an hardware breakpoint at address addr\n",
+    QEMU_ARCH_ALL)
+STEXI
+@item -hb @var{addr}
+@findex -hb
+Set an hardware breakpoint at address @var{addr}, by default implies "-s".
+ETEXI
+
+
 DEF("d", HAS_ARG, QEMU_OPTION_d, \
     "-d item1,...    output log to /tmp/qemu.log (use -d ? for a list of log items)\n",
     QEMU_ARCH_ALL)
diff --git a/vl.c b/vl.c
index 5372a96..9e5dc64 100644
--- a/vl.c
+++ b/vl.c
@@ -188,6 +188,7 @@  int mem_prealloc = 0; /* force preallocation of physical target memory */
 int nb_nics;
 NICInfo nd_table[MAX_NICS];
 int autostart;
+unsigned long long hbreak;
 static int rtc_utc = 1;
 static int rtc_date_offset = -1; /* -1 means no change */
 QEMUClock *rtc_clock;
@@ -2222,6 +2223,7 @@  int main(int argc, char **argv, char **envp)
     nb_nics = 0;
 
     autostart= 1;
+    hbreak = -1;
 
     /* first pass of option parsing */
     optind = 1;
@@ -2582,6 +2584,13 @@  int main(int argc, char **argv, char **envp)
             case QEMU_OPTION_S:
                 autostart = 0;
                 break;
+            case QEMU_OPTION_breakpoint:
+		hbreak = strtoull(optarg, NULL, 0);
+                /* hbreak needs a gdb server */
+                if (gdbstub_dev == NULL) {
+                   gdbstub_dev = "tcp::" DEFAULT_GDBSTUB_PORT;
+                }
+		break;
 	    case QEMU_OPTION_k:
 		keyboard_layout = optarg;
 		break;
@@ -3442,7 +3451,7 @@  int main(int argc, char **argv, char **envp)
     }
     text_consoles_set_display(ds);
 
-    if (gdbstub_dev && gdbserver_start(gdbstub_dev) < 0) {
+    if (gdbstub_dev && gdbserver_start(gdbstub_dev, hbreak) < 0) {
         fprintf(stderr, "qemu: could not open gdbserver on device '%s'\n",
                 gdbstub_dev);
         exit(1);