diff options
Diffstat (limited to 'gdb/amd64-linux-nat.c')
-rw-r--r-- | gdb/amd64-linux-nat.c | 130 |
1 files changed, 89 insertions, 41 deletions
diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c index c673965..288160b 100644 --- a/gdb/amd64-linux-nat.c +++ b/gdb/amd64-linux-nat.c @@ -64,6 +64,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; @@ -265,8 +273,6 @@ amd64_linux_store_inferior_registers (struct target_ops *ops, /* Support for debug registers. */ -static unsigned long amd64_linux_dr[DR_CONTROL + 1]; - static unsigned long amd64_linux_dr_get (ptid_t ptid, int regnum) { @@ -304,75 +310,116 @@ amd64_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 -amd64_linux_dr_set_control (unsigned long control) +static CORE_ADDR +amd64_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); - amd64_linux_dr[DR_CONTROL] = control; - ALL_LWPS (lp) - amd64_linux_dr_set (lp->ptid, DR_CONTROL, control); + return amd64_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 -amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr) +static unsigned long +amd64_linux_dr_get_control (void) { - struct lwp_info *lp; + return amd64_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. */ + +static unsigned long +amd64_linux_dr_get_status (void) +{ + return amd64_linux_dr_get (inferior_ptid, DR_STATUS); +} + +/* Callback for iterate_over_lwps. Update the debug registers of + LWP. */ + +static int +update_debug_registers_callback (struct lwp_info *lwp, void *arg) +{ + /* 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); - amd64_linux_dr[DR_FIRSTADDR + regnum] = addr; - ALL_LWPS (lp) - amd64_linux_dr_set (lp->ptid, DR_FIRSTADDR + regnum, addr); + return 0; } -/* Set address REGNUM (zero based) to zero in all LWPs of LWP_LIST. */ +/* Set DR_CONTROL to CONTROL in all LWPs of the current inferior. */ static void -amd64_linux_dr_reset_addr (int regnum) +amd64_linux_dr_set_control (unsigned long control) { - amd64_linux_dr_set_addr (regnum, 0); + ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid)); + + iterate_over_lwps (pid_ptid, 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 -amd64_linux_dr_get_status (void) +static void +amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr) { - return amd64_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); + + iterate_over_lwps (pid_ptid, 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 -amd64_linux_dr_unset_status (unsigned long mask) +amd64_linux_prepare_to_resume (struct lwp_info *lwp) { - struct lwp_info *lp; + int clear_status = 0; - ALL_LWPS (lp) + if (lwp->arch_private->debug_registers_changed) { - unsigned long value; - - value = amd64_linux_dr_get (lp->ptid, DR_STATUS); - value &= ~mask; - amd64_linux_dr_set (lp->ptid, DR_STATUS, value); + struct i386_debug_reg_state *state = i386_debug_reg_state (); + int i; + + for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++) + if (state->dr_ref_count[i] > 0) + { + amd64_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; + } + + amd64_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) + amd64_linux_dr_set (lwp->ptid, DR_STATUS, 0); +} static void -amd64_linux_new_thread (ptid_t ptid) +amd64_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++) - amd64_linux_dr_set (ptid, i, amd64_linux_dr[i]); + info->debug_registers_changed = 1; - amd64_linux_dr_set (ptid, DR_CONTROL, amd64_linux_dr[DR_CONTROL]); + lp->arch_private = info; } @@ -785,9 +832,9 @@ _initialize_amd64_linux_nat (void) i386_dr_low.set_control = amd64_linux_dr_set_control; i386_dr_low.set_addr = amd64_linux_dr_set_addr; - i386_dr_low.reset_addr = amd64_linux_dr_reset_addr; + i386_dr_low.get_addr = amd64_linux_dr_get_addr; i386_dr_low.get_status = amd64_linux_dr_get_status; - i386_dr_low.unset_status = amd64_linux_dr_unset_status; + i386_dr_low.get_control = amd64_linux_dr_get_control; i386_set_debug_register_length (8); /* Override the GNU/Linux inferior startup hook. */ @@ -804,4 +851,5 @@ _initialize_amd64_linux_nat (void) linux_nat_add_target (t); linux_nat_set_new_thread (t, amd64_linux_new_thread); linux_nat_set_siginfo_fixup (t, amd64_linux_siginfo_fixup); + linux_nat_set_prepare_to_resume (t, amd64_linux_prepare_to_resume); } |