X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Famd64-linux-nat.c;h=f954483dac0ea924d43b04cad56126603345d9e3;hb=52323be946ffc98caecb4c76ec0801e424b913ae;hp=877072c671725588587295b20e33e7ff245d845f;hpb=7b6bb8daaceb9ecf3f42dea57ae82733d6a3b2f6;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c index 877072c671..f954483dac 100644 --- a/gdb/amd64-linux-nat.c +++ b/gdb/amd64-linux-nat.c @@ -1,7 +1,6 @@ /* Native-dependent code for GNU/Linux x86-64. - Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, - 2011 Free Software Foundation, Inc. + Copyright (C) 2001-2012 Free Software Foundation, Inc. Contributed by Jiri Smid, SuSE Labs. This file is part of GDB. @@ -40,7 +39,7 @@ because the latter redefines FS and GS for no apparent reason, and those definitions don't match the ones that libpthread_db uses, which come from . */ -/* ezannoni-2003-07-09: I think this is fixed. The extraneous defs have +/* ezannoni-2003-07-09: I think this is fixed. The extraneous defs have been removed from ptrace.h in the kernel. However, better safe than sorry. */ #include @@ -64,6 +63,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 +272,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) { @@ -277,20 +282,11 @@ amd64_linux_dr_get (ptid_t ptid, int regnum) if (tid == 0) tid = PIDGET (ptid); - /* FIXME: kettenis/2001-03-27: Calling perror_with_name if the - ptrace call fails breaks debugging remote targets. The correct - way to fix this is to add the hardware breakpoint and watchpoint - stuff to the target vector. For now, just return zero if the - ptrace call fails. */ errno = 0; value = ptrace (PTRACE_PEEKUSER, tid, offsetof (struct user, u_debugreg[regnum]), 0); if (errno != 0) -#if 0 perror_with_name (_("Couldn't read debug register")); -#else - return 0; -#endif return value; } @@ -313,78 +309,129 @@ 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; - ptid_t ptid; + /* 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, ptid) - amd64_linux_dr_set (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; - ptid_t ptid; + 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 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; - amd64_linux_dr[DR_FIRSTADDR + regnum] = addr; - ALL_LWPS (lp, ptid) - amd64_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr); + /* 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 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); + 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 -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); + 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 -amd64_linux_dr_unset_status (unsigned long mask) +amd64_linux_prepare_to_resume (struct lwp_info *lwp) { - struct lwp_info *lp; - ptid_t ptid; + int clear_status = 0; + + /* 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; - ALL_LWPS (lp, ptid) + if (lwp->arch_private->debug_registers_changed) { - unsigned long value; - - value = amd64_linux_dr_get (ptid, DR_STATUS); - value &= ~mask; - amd64_linux_dr_set (ptid, DR_STATUS, value); + struct i386_debug_reg_state *state = i386_debug_reg_state (); + int i; + + /* On Linux kernel before 2.6.33 commit + 72f674d203cd230426437cdcf7dd6f681dad8b0d + if you enable a breakpoint by the DR_CONTROL bits you need to have + already written the corresponding DR_FIRSTADDR...DR_LASTADDR registers. + + Ensure DR_CONTROL gets written as the very last register here. */ + + 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; } @@ -797,9 +844,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. */ @@ -816,4 +863,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); }