Index: gdb/i386-linux-nat.c |
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c |
index afe6f46be77185f0a36a86aa3ef5598a28334b73..5a65ef6a2f33c6e3093478c9d8ada944ece75ef5 100644 |
--- a/gdb/i386-linux-nat.c |
+++ b/gdb/i386-linux-nat.c |
@@ -46,22 +46,6 @@ |
#include <sys/debugreg.h> |
#endif |
-#ifndef DR_FIRSTADDR |
-#define DR_FIRSTADDR 0 |
-#endif |
- |
-#ifndef DR_LASTADDR |
-#define DR_LASTADDR 3 |
-#endif |
- |
-#ifndef DR_STATUS |
-#define DR_STATUS 6 |
-#endif |
- |
-#ifndef DR_CONTROL |
-#define DR_CONTROL 7 |
-#endif |
- |
/* Prototypes for supply_gregset etc. */ |
#include "gregset.h" |
@@ -82,6 +66,14 @@ |
#define PTRACE_SETREGSET 0x4205 |
#endif |
+/* Per-thread arch-specific data we want to keep. */ |
+ |
+struct arch_lwp_info |
+{ |
+ /* Non-zero if our copy differs from what's recorded in the thread. */ |
+ int debug_registers_changed; |
+}; |
+ |
/* Does the current host support PTRACE_GETREGSET? */ |
static int have_ptrace_getregset = -1; |
@@ -650,8 +642,6 @@ i386_linux_store_inferior_registers (struct target_ops *ops, |
/* Support for debug registers. */ |
-static unsigned long i386_linux_dr[DR_CONTROL + 1]; |
- |
/* Get debug register REGNUM value from only the one LWP of PTID. */ |
static unsigned long |
@@ -691,74 +681,127 @@ i386_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) |
perror_with_name (_("Couldn't write debug register")); |
} |
-/* Set DR_CONTROL to ADDR in all LWPs of LWP_LIST. */ |
+/* Return the inferior's debug register REGNUM. */ |
-static void |
-i386_linux_dr_set_control (unsigned long control) |
+static CORE_ADDR |
+i386_linux_dr_get_addr (int regnum) |
{ |
- struct lwp_info *lp; |
+ /* DR6 and DR7 are retrieved with some other way. */ |
+ gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); |
- i386_linux_dr[DR_CONTROL] = control; |
- ALL_LWPS (lp) |
- i386_linux_dr_set (lp->ptid, DR_CONTROL, control); |
+ return i386_linux_dr_get (inferior_ptid, regnum); |
} |
-/* Set address REGNUM (zero based) to ADDR in all LWPs of LWP_LIST. */ |
+/* Return the inferior's DR7 debug control register. */ |
-static void |
-i386_linux_dr_set_addr (int regnum, CORE_ADDR addr) |
+static unsigned long |
+i386_linux_dr_get_control (void) |
{ |
- struct lwp_info *lp; |
+ return i386_linux_dr_get (inferior_ptid, DR_CONTROL); |
+} |
- gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); |
+/* Get DR_STATUS from only the one LWP of INFERIOR_PTID. */ |
- i386_linux_dr[DR_FIRSTADDR + regnum] = addr; |
- ALL_LWPS (lp) |
- i386_linux_dr_set (lp->ptid, DR_FIRSTADDR + regnum, addr); |
+static unsigned long |
+i386_linux_dr_get_status (void) |
+{ |
+ return i386_linux_dr_get (inferior_ptid, DR_STATUS); |
} |
-/* Set address REGNUM (zero based) to zero in all LWPs of LWP_LIST. */ |
+/* Callback for linux_nat_iterate_watchpoint_lwps. Update the debug registers |
+ of LWP. */ |
+ |
+static int |
+update_debug_registers_callback (struct lwp_info *lwp, void *arg) |
+{ |
+ if (lwp->arch_private == NULL) |
+ lwp->arch_private = XCNEW (struct arch_lwp_info); |
+ |
+ /* The actual update is done later just before resuming the lwp, we |
+ just mark that the registers need updating. */ |
+ lwp->arch_private->debug_registers_changed = 1; |
+ |
+ /* If the lwp isn't stopped, force it to momentarily pause, so we |
+ can update its debug registers. */ |
+ if (!lwp->stopped) |
+ linux_stop_lwp (lwp); |
+ |
+ /* Continue the iteration. */ |
+ return 0; |
+} |
+ |
+/* Set DR_CONTROL to ADDR in all LWPs of the current inferior. */ |
static void |
-i386_linux_dr_reset_addr (int regnum) |
+i386_linux_dr_set_control (unsigned long control) |
{ |
- i386_linux_dr_set_addr (regnum, 0); |
+ linux_nat_iterate_watchpoint_lwps (update_debug_registers_callback, NULL); |
} |
-/* Get DR_STATUS from only the one LWP of INFERIOR_PTID. */ |
+/* Set address REGNUM (zero based) to ADDR in all LWPs of the current |
+ inferior. */ |
-static unsigned long |
-i386_linux_dr_get_status (void) |
+static void |
+i386_linux_dr_set_addr (int regnum, CORE_ADDR addr) |
{ |
- return i386_linux_dr_get (inferior_ptid, DR_STATUS); |
+ ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid)); |
+ |
+ gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); |
+ |
+ linux_nat_iterate_watchpoint_lwps (update_debug_registers_callback, NULL); |
} |
-/* Unset MASK bits in DR_STATUS in all LWPs of LWP_LIST. */ |
+/* Called when resuming a thread. |
+ If the debug regs have changed, update the thread's copies. */ |
static void |
-i386_linux_dr_unset_status (unsigned long mask) |
+i386_linux_prepare_to_resume (struct lwp_info *lwp) |
{ |
- struct lwp_info *lp; |
+ int clear_status = 0; |
- ALL_LWPS (lp) |
+ /* NULL means this is the main thread still going through the shell, |
+ or, no watchpoint has been set yet. In that case, there's |
+ nothing to do. */ |
+ if (lwp->arch_private == NULL) |
+ return; |
+ |
+ if (lwp->arch_private->debug_registers_changed) |
{ |
- unsigned long value; |
+ struct i386_debug_reg_state *state = i386_debug_reg_state (); |
+ int i; |
+ |
+ /* See amd64_linux_prepare_to_resume for Linux kernel note on |
+ i386_linux_dr_set calls ordering. */ |
- value = i386_linux_dr_get (lp->ptid, DR_STATUS); |
- value &= ~mask; |
- i386_linux_dr_set (lp->ptid, DR_STATUS, value); |
+ for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++) |
+ if (state->dr_ref_count[i] > 0) |
+ { |
+ i386_linux_dr_set (lwp->ptid, i, state->dr_mirror[i]); |
+ |
+ /* If we're setting a watchpoint, any change the inferior |
+ had done itself to the debug registers needs to be |
+ discarded, otherwise, i386_stopped_data_address can get |
+ confused. */ |
+ clear_status = 1; |
+ } |
+ |
+ i386_linux_dr_set (lwp->ptid, DR_CONTROL, state->dr_control_mirror); |
+ |
+ lwp->arch_private->debug_registers_changed = 0; |
} |
+ |
+ if (clear_status || lwp->stopped_by_watchpoint) |
+ i386_linux_dr_set (lwp->ptid, DR_STATUS, 0); |
} |
static void |
-i386_linux_new_thread (ptid_t ptid) |
+i386_linux_new_thread (struct lwp_info *lp) |
{ |
- int i; |
+ struct arch_lwp_info *info = XCNEW (struct arch_lwp_info); |
- for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++) |
- i386_linux_dr_set (ptid, i, i386_linux_dr[i]); |
+ info->debug_registers_changed = 1; |
- i386_linux_dr_set (ptid, DR_CONTROL, i386_linux_dr[DR_CONTROL]); |
+ lp->arch_private = info; |
} |
@@ -832,7 +875,7 @@ static const unsigned char linux_syscall[] = { 0xcd, 0x80 }; |
static void |
i386_linux_resume (struct target_ops *ops, |
- ptid_t ptid, int step, enum target_signal signal) |
+ ptid_t ptid, int step, enum gdb_signal signal) |
{ |
int pid = PIDGET (ptid); |
@@ -895,7 +938,7 @@ i386_linux_resume (struct target_ops *ops, |
} |
} |
- if (ptrace (request, pid, 0, target_signal_to_host (signal)) == -1) |
+ if (ptrace (request, pid, 0, gdb_signal_to_host (signal)) == -1) |
perror_with_name (("ptrace")); |
} |
@@ -965,6 +1008,9 @@ i386_linux_read_description (struct target_ops *ops) |
return tdesc_i386_linux; |
} |
+/* -Wmissing-prototypes */ |
+extern initialize_file_ftype _initialize_i386_linux_nat; |
+ |
void |
_initialize_i386_linux_nat (void) |
{ |
@@ -977,9 +1023,9 @@ _initialize_i386_linux_nat (void) |
i386_dr_low.set_control = i386_linux_dr_set_control; |
i386_dr_low.set_addr = i386_linux_dr_set_addr; |
- i386_dr_low.reset_addr = i386_linux_dr_reset_addr; |
+ i386_dr_low.get_addr = i386_linux_dr_get_addr; |
i386_dr_low.get_status = i386_linux_dr_get_status; |
- i386_dr_low.unset_status = i386_linux_dr_unset_status; |
+ i386_dr_low.get_control = i386_linux_dr_get_control; |
i386_set_debug_register_length (4); |
/* Override the default ptrace resume method. */ |
@@ -998,4 +1044,5 @@ _initialize_i386_linux_nat (void) |
/* Register the target. */ |
linux_nat_add_target (t); |
linux_nat_set_new_thread (t, i386_linux_new_thread); |
+ linux_nat_set_prepare_to_resume (t, i386_linux_prepare_to_resume); |
} |