From: Gary Benson Date: Tue, 24 Mar 2015 14:05:45 +0000 (+0000) Subject: Move low-level Linux x86 debug register code to a shared file X-Git-Url: http://git.efficios.com/?a=commitdiff_plain;h=8e5d40700492597d103ebdb68eab733456b66fad;p=deliverable%2Fbinutils-gdb.git Move low-level Linux x86 debug register code to a shared file This commit moves the now-identical low-level Linux x86 debug register code from gdb/x86-linux-nat.c and gdb/gdbserver/linux-x86-low.c into a new shared file gdb/nat/x86-linux-dregs.c. gdb/ChangeLog: * nat/x86-linux-dregs.h: New file. * nat/x86-linux-dregs.c: Likewise. * Makefile.in (HFILES_NO_SRCDIR): Add nat/x86-linux-dregs.h. (x86-linux-dregs.o): New rule. * config/i386/linux.mh (NATDEPFILES): Add x86-linux-dregs.o. * config/i386/linux64.mh (NATDEPFILES): Likewise. * x86-linux-nat.c: Include nat/x86-linux-dregs.h. (u_debugreg_offset): Moved to nat/x86-linux-dregs.c. (x86_linux_dr_get): Likewise. (x86_linux_dr_set): Likewise. (x86_linux_dr_get_addr): Likewise. (x86_linux_dr_get_control): Likewise. (x86_linux_dr_get_status): Likewise. (update_debug_registers_callback): Likewise. (x86_linux_dr_set_control): Likewise. (x86_linux_dr_set_addr): Likewise. (x86_linux_update_debug_registers): Likewise. gdb/gdbserver/ChangeLog: * Makefile.in (x86-linux-dregs.o): New rule. * configure.srv: Add x86-linux-dregs.o to relevant targets. * linux-x86-low.c: Include nat/x86-linux-dregs.h. (u_debugreg_offset): Moved to nat/x86-linux-dregs.c. (x86_linux_dr_get): Likewise. (x86_linux_dr_set): Likewise. (update_debug_registers_callback): Likewise. (x86_linux_dr_set_addr): Likewise. (x86_linux_dr_get_addr): Likewise. (x86_linux_dr_set_control): Likewise. (x86_linux_dr_get_control): Likewise. (x86_linux_dr_get_status): Likewise. (x86_linux_update_debug_registers): Likewise. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index a8ad8911af..7dcec1fdbe 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,23 @@ +2015-03-24 Gary Benson + + * nat/x86-linux-dregs.h: New file. + * nat/x86-linux-dregs.c: Likewise. + * Makefile.in (HFILES_NO_SRCDIR): Add nat/x86-linux-dregs.h. + (x86-linux-dregs.o): New rule. + * config/i386/linux.mh (NATDEPFILES): Add x86-linux-dregs.o. + * config/i386/linux64.mh (NATDEPFILES): Likewise. + * x86-linux-nat.c: Include nat/x86-linux-dregs.h. + (u_debugreg_offset): Moved to nat/x86-linux-dregs.c. + (x86_linux_dr_get): Likewise. + (x86_linux_dr_set): Likewise. + (x86_linux_dr_get_addr): Likewise. + (x86_linux_dr_get_control): Likewise. + (x86_linux_dr_get_status): Likewise. + (update_debug_registers_callback): Likewise. + (x86_linux_dr_set_control): Likewise. + (x86_linux_dr_set_addr): Likewise. + (x86_linux_update_debug_registers): Likewise. + 2015-03-24 Gary Benson * x86-linux-nat.c (x86_linux_update_debug_registers): diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 6cc41ffd34..ae50041053 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -968,7 +968,7 @@ i386-linux-nat.h common/common-defs.h common/errors.h common/common-types.h \ common/common-debug.h common/cleanups.h common/gdb_setjmp.h \ common/common-exceptions.h target/target.h common/symbol.h \ common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \ -common/common-remote-fileio.h nat/x86-linux.h +common/common-remote-fileio.h nat/x86-linux.h nat/x86-linux-dregs.h # Header files that already have srcdir in them, or which are in objdir. @@ -2303,6 +2303,10 @@ x86-linux.o: ${srcdir}/nat/x86-linux.c $(COMPILE) $(srcdir)/nat/x86-linux.c $(POSTCOMPILE) +x86-linux-dregs.o: ${srcdir}/nat/x86-linux-dregs.c + $(COMPILE) $(srcdir)/nat/x86-linux-dregs.c + $(POSTCOMPILE) + # # gdb/tui/ dependencies # diff --git a/gdb/config/i386/linux.mh b/gdb/config/i386/linux.mh index ae19f96265..9be2c5fc8a 100644 --- a/gdb/config/i386/linux.mh +++ b/gdb/config/i386/linux.mh @@ -5,7 +5,8 @@ NATDEPFILES= inf-ptrace.o fork-child.o \ x86-nat.o x86-dregs.o i386-linux-nat.o x86-linux-nat.o \ proc-service.o linux-thread-db.o \ linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \ - linux-btrace.o linux-waitpid.o linux-personality.o x86-linux.o + linux-btrace.o linux-waitpid.o linux-personality.o x86-linux.o \ + x86-linux-dregs.o NAT_CDEPS = $(srcdir)/proc-service.list # The dynamically loaded libthread_db needs access to symbols in the diff --git a/gdb/config/i386/linux64.mh b/gdb/config/i386/linux64.mh index 4a05f9116b..224a2a97e6 100644 --- a/gdb/config/i386/linux64.mh +++ b/gdb/config/i386/linux64.mh @@ -5,7 +5,8 @@ NATDEPFILES= inf-ptrace.o fork-child.o \ linux-nat.o linux-osdata.o \ proc-service.o linux-thread-db.o linux-fork.o \ linux-procfs.o linux-ptrace.o linux-btrace.o \ - linux-waitpid.o linux-personality.o x86-linux.o + linux-waitpid.o linux-personality.o x86-linux.o \ + x86-linux-dregs.o NAT_FILE= config/nm-linux.h NAT_CDEPS = $(srcdir)/proc-service.list diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index f58010377e..8d1d84464d 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,19 @@ +2015-03-24 Gary Benson + + * Makefile.in (x86-linux-dregs.o): New rule. + * configure.srv: Add x86-linux-dregs.o to relevant targets. + * linux-x86-low.c: Include nat/x86-linux-dregs.h. + (u_debugreg_offset): Moved to nat/x86-linux-dregs.c. + (x86_linux_dr_get): Likewise. + (x86_linux_dr_set): Likewise. + (update_debug_registers_callback): Likewise. + (x86_linux_dr_set_addr): Likewise. + (x86_linux_dr_get_addr): Likewise. + (x86_linux_dr_set_control): Likewise. + (x86_linux_dr_get_control): Likewise. + (x86_linux_dr_get_status): Likewise. + (x86_linux_update_debug_registers): Likewise. + 2015-03-24 Gary Benson * linux-x86-low.c (x86_linux_update_debug_registers): diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in index 7b1e52c02a..9d698c6a23 100644 --- a/gdb/gdbserver/Makefile.in +++ b/gdb/gdbserver/Makefile.in @@ -612,6 +612,9 @@ btrace-common.o: ../common/btrace-common.c x86-linux.o: ../nat/x86-linux.c $(COMPILE) $< $(POSTCOMPILE) +x86-linux-dregs.o: ../nat/x86-linux-dregs.c + $(COMPILE) $< + $(POSTCOMPILE) aarch64.c : $(srcdir)/../regformats/aarch64.dat $(regdat_sh) $(SHELL) $(regdat_sh) $(srcdir)/../regformats/aarch64.dat aarch64.c diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv index fcb29ff7fb..81dd235db7 100644 --- a/gdb/gdbserver/configure.srv +++ b/gdb/gdbserver/configure.srv @@ -110,6 +110,7 @@ case "${target}" in fi srv_tgtobj="$srv_linux_obj linux-x86-low.o x86-low.o x86-dregs.o i387-fp.o" srv_tgtobj="${srv_tgtobj} linux-btrace.o x86-linux.o" + srv_tgtobj="${srv_tgtobj} x86-linux-dregs.o" srv_linux_usrregs=yes srv_linux_regsets=yes srv_linux_thread_db=yes @@ -326,6 +327,7 @@ case "${target}" in x86_64-*-linux*) srv_regobj="$srv_amd64_linux_regobj $srv_i386_linux_regobj" srv_tgtobj="$srv_linux_obj linux-x86-low.o x86-low.o x86-dregs.o i387-fp.o" srv_tgtobj="${srv_tgtobj} linux-btrace.o x86-linux.o" + srv_tgtobj="${srv_tgtobj} x86-linux-dregs.o" srv_xmlfiles="$srv_i386_linux_xmlfiles $srv_amd64_linux_xmlfiles" srv_linux_usrregs=yes # This is for i386 progs. srv_linux_regsets=yes diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c index e371873296..8a3b5a81eb 100644 --- a/gdb/gdbserver/linux-x86-low.c +++ b/gdb/gdbserver/linux-x86-low.c @@ -39,6 +39,7 @@ #include "ax.h" #include "nat/linux-nat.h" #include "nat/x86-linux.h" +#include "nat/x86-linux-dregs.h" #ifdef __x86_64__ /* Defined in auto-generated file amd64-linux.c. */ @@ -524,125 +525,6 @@ x86_breakpoint_at (CORE_ADDR pc) return 0; } - -/* Return the offset of REGNUM in the u_debugreg field of struct - user. */ - -static int -u_debugreg_offset (int regnum) -{ - return (offsetof (struct user, u_debugreg) - + sizeof (((struct user *) 0)->u_debugreg[0]) * regnum); -} - - -/* Support for debug registers. */ - -/* Get debug register REGNUM value from the LWP specified by PTID. */ - -static unsigned long -x86_linux_dr_get (ptid_t ptid, int regnum) -{ - int tid; - unsigned long value; - - gdb_assert (ptid_lwp_p (ptid)); - tid = ptid_get_lwp (ptid); - - errno = 0; - value = ptrace (PTRACE_PEEKUSER, tid, u_debugreg_offset (regnum), 0); - if (errno != 0) - perror_with_name (_("Couldn't read debug register")); - - return value; -} - -/* Set debug register REGNUM to VALUE in the LWP specified by PTID. */ - -static void -x86_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) -{ - int tid; - - gdb_assert (ptid_lwp_p (ptid)); - tid = ptid_get_lwp (ptid); - - errno = 0; - ptrace (PTRACE_POKEUSER, tid, u_debugreg_offset (regnum), value); - if (errno != 0) - perror_with_name (_("Couldn't write debug register")); -} - -/* Callback for iterate_over_lwps. Mark that our local mirror of - LWP's debug registers has been changed, and cause LWP to stop if - it isn't already. Values are written from our local mirror to - the actual debug registers immediately prior to LWP resuming. */ - -static int -update_debug_registers_callback (struct lwp_info *lwp, void *arg) -{ - lwp_set_debug_registers_changed (lwp, 1); - - if (!lwp_is_stopped (lwp)) - linux_stop_lwp (lwp); - - /* Continue the iteration. */ - return 0; -} - -/* Store ADDR in debug register REGNUM of all LWPs of the current - inferior. */ - -static void -x86_linux_dr_set_addr (int regnum, CORE_ADDR addr) -{ - ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (current_lwp_ptid ())); - - gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); - - iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); -} - -/* Return the address stored in the current inferior's debug register - REGNUM. */ - -static CORE_ADDR -x86_linux_dr_get_addr (int regnum) -{ - gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); - - return x86_linux_dr_get (current_lwp_ptid (), regnum); -} - -/* Store CONTROL in the debug control registers of all LWPs of the - current inferior. */ - -static void -x86_linux_dr_set_control (unsigned long control) -{ - ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (current_lwp_ptid ())); - - iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); -} - -/* Return the value stored in the current inferior's debug control - register. */ - -static unsigned long -x86_linux_dr_get_control (void) -{ - return x86_linux_dr_get (current_lwp_ptid (), DR_CONTROL); -} - -/* Return the value stored in the current inferior's debug status - register. */ - -static unsigned long -x86_linux_dr_get_status (void) -{ - return x86_linux_dr_get (current_lwp_ptid (), DR_STATUS); -} - /* Low-level function vector. */ struct x86_dr_low_type x86_dr_low = { @@ -776,54 +658,6 @@ x86_debug_reg_state (pid_t pid) return &proc->priv->arch_private->debug_reg_state; } -/* Update the thread's debug registers if the values in our local - mirror have been changed. */ - -static void -x86_linux_update_debug_registers (struct lwp_info *lwp) -{ - ptid_t ptid = ptid_of_lwp (lwp); - int clear_status = 0; - - gdb_assert (lwp_is_stopped (lwp)); - - if (lwp_debug_registers_changed (lwp)) - { - struct x86_debug_reg_state *state - = x86_debug_reg_state (ptid_get_pid (ptid)); - int i; - - /* Prior to Linux kernel 2.6.33 commit - 72f674d203cd230426437cdcf7dd6f681dad8b0d, setting DR0-3 to - a value that did not match what was enabled in DR_CONTROL - resulted in EINVAL. To avoid this we zero DR_CONTROL before - writing address registers, only writing DR_CONTROL's actual - value once all the addresses are in place. */ - x86_linux_dr_set (ptid, DR_CONTROL, 0); - - ALL_DEBUG_ADDRESS_REGISTERS (i) - if (state->dr_ref_count[i] > 0) - { - x86_linux_dr_set (ptid, i, state->dr_mirror[i]); - - /* If we're setting a watchpoint, any change the inferior - has made to its debug registers needs to be discarded - to avoid x86_stopped_data_address getting confused. */ - clear_status = 1; - } - - /* If DR_CONTROL is supposed to be zero then it's already set. */ - if (state->dr_control_mirror != 0) - x86_linux_dr_set (ptid, DR_CONTROL, state->dr_control_mirror); - - lwp_set_debug_registers_changed (lwp, 0); - } - - if (clear_status - || lwp_stop_reason (lwp) == TARGET_STOPPED_BY_WATCHPOINT) - x86_linux_dr_set (ptid, DR_STATUS, 0); -} - /* Called prior to resuming a thread. */ static void diff --git a/gdb/nat/x86-linux-dregs.c b/gdb/nat/x86-linux-dregs.c new file mode 100644 index 0000000000..93898033a6 --- /dev/null +++ b/gdb/nat/x86-linux-dregs.c @@ -0,0 +1,183 @@ +/* Low-level debug register code for GNU/Linux x86 (i386 and x86-64). + + Copyright (C) 1999-2015 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "common-defs.h" +#include +#include +#include "target/waitstatus.h" +#include "nat/x86-linux.h" +#include "nat/x86-dregs.h" +#include "nat/x86-linux-dregs.h" + +/* Return the offset of REGNUM in the u_debugreg field of struct + user. */ + +static int +u_debugreg_offset (int regnum) +{ + return (offsetof (struct user, u_debugreg) + + sizeof (((struct user *) 0)->u_debugreg[0]) * regnum); +} + +/* Get debug register REGNUM value from the LWP specified by PTID. */ + +static unsigned long +x86_linux_dr_get (ptid_t ptid, int regnum) +{ + int tid; + unsigned long value; + + gdb_assert (ptid_lwp_p (ptid)); + tid = ptid_get_lwp (ptid); + + errno = 0; + value = ptrace (PTRACE_PEEKUSER, tid, u_debugreg_offset (regnum), 0); + if (errno != 0) + perror_with_name (_("Couldn't read debug register")); + + return value; +} + +/* Set debug register REGNUM to VALUE in the LWP specified by PTID. */ + +static void +x86_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) +{ + int tid; + + gdb_assert (ptid_lwp_p (ptid)); + tid = ptid_get_lwp (ptid); + + errno = 0; + ptrace (PTRACE_POKEUSER, tid, u_debugreg_offset (regnum), value); + if (errno != 0) + perror_with_name (_("Couldn't write debug register")); +} + +/* Callback for iterate_over_lwps. Mark that our local mirror of + LWP's debug registers has been changed, and cause LWP to stop if + it isn't already. Values are written from our local mirror to + the actual debug registers immediately prior to LWP resuming. */ + +static int +update_debug_registers_callback (struct lwp_info *lwp, void *arg) +{ + lwp_set_debug_registers_changed (lwp, 1); + + if (!lwp_is_stopped (lwp)) + linux_stop_lwp (lwp); + + /* Continue the iteration. */ + return 0; +} + +/* See nat/x86-linux-dregs.h. */ + +CORE_ADDR +x86_linux_dr_get_addr (int regnum) +{ + gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); + + return x86_linux_dr_get (current_lwp_ptid (), regnum); +} + +/* See nat/x86-linux-dregs.h. */ + +void +x86_linux_dr_set_addr (int regnum, CORE_ADDR addr) +{ + ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (current_lwp_ptid ())); + + gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); + + iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); +} + +/* See nat/x86-linux-dregs.h. */ + +unsigned long +x86_linux_dr_get_control (void) +{ + return x86_linux_dr_get (current_lwp_ptid (), DR_CONTROL); +} + +/* See nat/x86-linux-dregs.h. */ + +void +x86_linux_dr_set_control (unsigned long control) +{ + ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (current_lwp_ptid ())); + + iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); +} + +/* See nat/x86-linux-dregs.h. */ + +unsigned long +x86_linux_dr_get_status (void) +{ + return x86_linux_dr_get (current_lwp_ptid (), DR_STATUS); +} + +/* See nat/x86-linux-dregs.h. */ + +void +x86_linux_update_debug_registers (struct lwp_info *lwp) +{ + ptid_t ptid = ptid_of_lwp (lwp); + int clear_status = 0; + + gdb_assert (lwp_is_stopped (lwp)); + + if (lwp_debug_registers_changed (lwp)) + { + struct x86_debug_reg_state *state + = x86_debug_reg_state (ptid_get_pid (ptid)); + int i; + + /* Prior to Linux kernel 2.6.33 commit + 72f674d203cd230426437cdcf7dd6f681dad8b0d, setting DR0-3 to + a value that did not match what was enabled in DR_CONTROL + resulted in EINVAL. To avoid this we zero DR_CONTROL before + writing address registers, only writing DR_CONTROL's actual + value once all the addresses are in place. */ + x86_linux_dr_set (ptid, DR_CONTROL, 0); + + ALL_DEBUG_ADDRESS_REGISTERS (i) + if (state->dr_ref_count[i] > 0) + { + x86_linux_dr_set (ptid, i, state->dr_mirror[i]); + + /* If we're setting a watchpoint, any change the inferior + has made to its debug registers needs to be discarded + to avoid x86_stopped_data_address getting confused. */ + clear_status = 1; + } + + /* If DR_CONTROL is supposed to be zero then it's already set. */ + if (state->dr_control_mirror != 0) + x86_linux_dr_set (ptid, DR_CONTROL, state->dr_control_mirror); + + lwp_set_debug_registers_changed (lwp, 0); + } + + if (clear_status + || lwp_stop_reason (lwp) == TARGET_STOPPED_BY_WATCHPOINT) + x86_linux_dr_set (ptid, DR_STATUS, 0); +} diff --git a/gdb/nat/x86-linux-dregs.h b/gdb/nat/x86-linux-dregs.h new file mode 100644 index 0000000000..063e508185 --- /dev/null +++ b/gdb/nat/x86-linux-dregs.h @@ -0,0 +1,53 @@ +/* Low-level debug register code for GNU/Linux x86 (i386 and x86-64). + + Copyright (C) 1999-2015 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef X86_LINUX_DREGS_H +#define X86_LINUX_DREGS_H + +/* Return the address stored in the current inferior's debug register + REGNUM. */ + +extern CORE_ADDR x86_linux_dr_get_addr (int regnum); + +/* Store ADDR in debug register REGNUM of all LWPs of the current + inferior. */ + +extern void x86_linux_dr_set_addr (int regnum, CORE_ADDR addr); + +/* Return the value stored in the current inferior's debug control + register. */ + +extern unsigned long x86_linux_dr_get_control (void); + +/* Store CONTROL in the debug control registers of all LWPs of the + current inferior. */ + +extern void x86_linux_dr_set_control (unsigned long control); + +/* Return the value stored in the current inferior's debug status + register. */ + +extern unsigned long x86_linux_dr_get_status (void); + +/* Update the thread's debug registers if the values in our local + mirror have been changed. */ + +extern void x86_linux_update_debug_registers (struct lwp_info *lwp); + +#endif /* X86_LINUX_DREGS_H */ diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c index 9eb82c5cde..84021fb9cf 100644 --- a/gdb/x86-linux-nat.c +++ b/gdb/x86-linux-nat.c @@ -40,6 +40,7 @@ #include "nat/linux-btrace.h" #include "nat/linux-nat.h" #include "nat/x86-linux.h" +#include "nat/x86-linux-dregs.h" /* Per-thread arch-specific data we want to keep. */ @@ -51,173 +52,6 @@ struct arch_lwp_info /* Does the current host support PTRACE_GETREGSET? */ int have_ptrace_getregset = -1; - - -/* Return the offset of REGNUM in the u_debugreg field of struct - user. */ - -static int -u_debugreg_offset (int regnum) -{ - return (offsetof (struct user, u_debugreg) - + sizeof (((struct user *) 0)->u_debugreg[0]) * regnum); -} - -/* Support for debug registers. */ - -/* Get debug register REGNUM value from the LWP specified by PTID. */ - -static unsigned long -x86_linux_dr_get (ptid_t ptid, int regnum) -{ - int tid; - unsigned long value; - - gdb_assert (ptid_lwp_p (ptid)); - tid = ptid_get_lwp (ptid); - - errno = 0; - value = ptrace (PTRACE_PEEKUSER, tid, u_debugreg_offset (regnum), 0); - - if (errno != 0) - perror_with_name (_("Couldn't read debug register")); - - return value; -} - -/* Set debug register REGNUM to VALUE in the LWP specified by PTID. */ - -static void -x86_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) -{ - int tid; - - gdb_assert (ptid_lwp_p (ptid)); - tid = ptid_get_lwp (ptid); - - errno = 0; - ptrace (PTRACE_POKEUSER, tid, u_debugreg_offset (regnum), value); - if (errno != 0) - perror_with_name (_("Couldn't write debug register")); -} - -/* Return the address stored in the current inferior's debug register - REGNUM. */ - -static CORE_ADDR -x86_linux_dr_get_addr (int regnum) -{ - gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); - - return x86_linux_dr_get (current_lwp_ptid (), regnum); -} - -/* Return the value stored in the current inferior's debug control - register. */ - -static unsigned long -x86_linux_dr_get_control (void) -{ - return x86_linux_dr_get (current_lwp_ptid (), DR_CONTROL); -} - -/* Return the value stored in the current inferior's debug status - register. */ - -static unsigned long -x86_linux_dr_get_status (void) -{ - return x86_linux_dr_get (current_lwp_ptid (), DR_STATUS); -} - -/* Callback for iterate_over_lwps. Mark that our local mirror of - LWP's debug registers has been changed, and cause LWP to stop if - it isn't already. Values are written from our local mirror to - the actual debug registers immediately prior to LWP resuming. */ - -static int -update_debug_registers_callback (struct lwp_info *lwp, void *arg) -{ - lwp_set_debug_registers_changed (lwp, 1); - - if (!lwp_is_stopped (lwp)) - linux_stop_lwp (lwp); - - /* Continue the iteration. */ - return 0; -} - -/* Store CONTROL in the debug control registers of all LWPs of the - current inferior. */ - -static void -x86_linux_dr_set_control (unsigned long control) -{ - ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (current_lwp_ptid ())); - - iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); -} - -/* Store ADDR in debug register REGNUM of all LWPs of the current - inferior. */ - -static void -x86_linux_dr_set_addr (int regnum, CORE_ADDR addr) -{ - ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (current_lwp_ptid ())); - - gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); - - iterate_over_lwps (pid_ptid, update_debug_registers_callback, NULL); -} - -/* Update the thread's debug registers if the values in our local - mirror have been changed. */ - -static void -x86_linux_update_debug_registers (struct lwp_info *lwp) -{ - ptid_t ptid = ptid_of_lwp (lwp); - int clear_status = 0; - - gdb_assert (lwp_is_stopped (lwp)); - - if (lwp_debug_registers_changed (lwp)) - { - struct x86_debug_reg_state *state - = x86_debug_reg_state (ptid_get_pid (ptid)); - int i; - - /* Prior to Linux kernel 2.6.33 commit - 72f674d203cd230426437cdcf7dd6f681dad8b0d, setting DR0-3 to - a value that did not match what was enabled in DR_CONTROL - resulted in EINVAL. To avoid this we zero DR_CONTROL before - writing address registers, only writing DR_CONTROL's actual - value once all the addresses are in place. */ - x86_linux_dr_set (ptid, DR_CONTROL, 0); - - ALL_DEBUG_ADDRESS_REGISTERS (i) - if (state->dr_ref_count[i] > 0) - { - x86_linux_dr_set (ptid, i, state->dr_mirror[i]); - - /* If we're setting a watchpoint, any change the inferior - has made to its debug registers needs to be discarded - to avoid x86_stopped_data_address getting confused. */ - clear_status = 1; - } - - /* If DR_CONTROL is supposed to be zero then it's already set. */ - if (state->dr_control_mirror != 0) - x86_linux_dr_set (ptid, DR_CONTROL, state->dr_control_mirror); - - lwp_set_debug_registers_changed (lwp, 0); - } - - if (clear_status - || lwp_stop_reason (lwp) == TARGET_STOPPED_BY_WATCHPOINT) - x86_linux_dr_set (ptid, DR_STATUS, 0); -} /* Called prior to resuming a thread. */