state->dr_control_mirror == 0 failed assertion in gdbserver on Windows XP
authorJoel Brobecker <brobecker@adacore.com>
Tue, 14 Oct 2014 21:18:35 +0000 (23:18 +0200)
committerJoel Brobecker <brobecker@adacore.com>
Wed, 15 Oct 2014 14:27:54 +0000 (07:27 -0700)
When using GDBserver on Windows XP, GDBserver reports an assertion
failure after hitting a hardware watchpoint. The problem was reproduced
using the sources from gdb.ada/int_deref, but should probably reproduce
with any scenario involving hardware watchpoints.

In our scenario, we break on line 5, just before the increment, insert
a watchhpoint on it, and then continue:

    (gdb) b foo.adb:5
    Breakpoint 1 at 0x4017c2: file foo.adb, line 5.
    (gdb) cont
    Continuing.

    Breakpoint 1, foo () at foo.adb:5
    5          Pck.Watch := Pck.Watch + 1;
    (gdb) watch watch
    Hardware watchpoint 2: watch
    (gdb) c
    Continuing.
    Remote communication error.  Target disconnected.: Invalid argument.

The immediate cause for the communication error is easily explained,
gdbserver crashes due to a failed assertion:

    x86_remove_aligned_watchpoint: Assertion `state->dr_control_mirror == 0' failed.

The assertion occurs because debug_reg_state.dr_control_mirror gets
overwritten by the value read from the inferior, when processing
the watchpoint event in win32_wait: win32_wait finds that we stopped,
calls get_thread_regcache which causes i386_get_thread_context to
get called, which then...

  if (th->tid == current_event->dwThreadId)
    {
      /* Copy dr values from the current thread.  */
      struct x86_debug_reg_state *dr = &debug_reg_state;
      [...]
      dr->dr_control_mirror = th->context.Dr7;
    }

Both should be identical, normally making this a no-op, but
it turns out that bits 12-11-10 are documented as being fixed
and equal to 001. Our handling of dr_control_mirror does not
manage those bits, and leaves them as zeros instead. So, when
we overwrite the value from the thread's DR7 register, we
accidentally set bit 10, causing state->dr_control_mirror
to be 0x400 after we've cleared everything internally.

This patch fixes the issue by removing the statement setting
state->dr_control_mirror to the thread's DR7 register value.

gdb/gdbserver/ChangeLog:

        PR server/17487
        * win32-i386-low.c (i386_get_thread_context): Do not set
        dr->dr_control_mirror.

gdb/gdbserver/ChangeLog
gdb/gdbserver/win32-i386-low.c

index f662248..62ca872 100644 (file)
@@ -1,3 +1,9 @@
+2014-10-15  Joel Brobecker  <brobecker@adacore.com>
+
+       PR server/17487
+       * win32-i386-low.c (i386_get_thread_context): Do not set
+       dr->dr_control_mirror.
+
 2014-07-11  Pedro Alves  <palves@redhat.com>
 
        * linux-low.c (kill_wait_lwp): New function, based on
index 08242aa..8ab1b73 100644 (file)
@@ -212,7 +212,6 @@ i386_get_thread_context (win32_thread_info *th, DEBUG_EVENT* current_event)
       dr->dr_mirror[2] = th->context.Dr2;
       dr->dr_mirror[3] = th->context.Dr3;
       dr->dr_status_mirror = th->context.Dr6;
-      dr->dr_control_mirror = th->context.Dr7;
     }
 }