From 05db5edd7923711a20c6225ea8e15f36e819d140 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Fri, 12 Sep 2014 20:29:11 +0200 Subject: [PATCH] Add hardware watchpoint support for x86 GNU Hurd. gdb/ * config/i386/i386gnu.mh (NATDEPFILES): Add x86-nat.o and x86-dregs.o. * gnu-nat.c (inf_threads): New function. * gnu-nat.h (inf_threads_ftype): New typedef. (inf_threads): New declaration. * i386gnu-nat.c: Include "x86-nat.h" and "inf-child.h". [i386_DEBUG_STATE] (i386_gnu_dr_get, i386_gnu_dr_set) (i386_gnu_dr_set_control_one, i386_gnu_dr_set_control) (i386_gnu_dr_set_addr_one, i386_gnu_dr_set_addr) (i386_gnu_dr_get_reg, i386_gnu_dr_get_addr, 386_gnu_dr_get_status) (i386_gnu_dr_get_control): New functions. (reg_addr): New structure. (_initialize_i386gnu_nat) [i386_DEBUG_STATE]: Initialize hardware i386 debugging register hooks. * NEWS: Mention this. --- gdb/ChangeLog | 18 +++++ gdb/NEWS | 2 + gdb/config/i386/i386gnu.mh | 3 +- gdb/gnu-nat.c | 11 +++ gdb/gnu-nat.h | 5 ++ gdb/i386gnu-nat.c | 150 +++++++++++++++++++++++++++++++++++++ 6 files changed, 188 insertions(+), 1 deletion(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 3231d69d3d3..125ab92bd91 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,21 @@ +2014-09-16 Samuel Thibault + + * config/i386/i386gnu.mh (NATDEPFILES): Add x86-nat.o and + x86-dregs.o. + * gnu-nat.c (inf_threads): New function. + * gnu-nat.h (inf_threads_ftype): New typedef. + (inf_threads): New declaration. + * i386gnu-nat.c: Include "x86-nat.h" and "inf-child.h". + [i386_DEBUG_STATE] (i386_gnu_dr_get, i386_gnu_dr_set) + (i386_gnu_dr_set_control_one, i386_gnu_dr_set_control) + (i386_gnu_dr_set_addr_one, i386_gnu_dr_set_addr) + (i386_gnu_dr_get_reg, i386_gnu_dr_get_addr, 386_gnu_dr_get_status) + (i386_gnu_dr_get_control): New functions. + (reg_addr): New structure. + (_initialize_i386gnu_nat) [i386_DEBUG_STATE]: Initialize hardware + i386 debugging register hooks. + * NEWS: Mention this. + 2014-08-13 Omair Javaid * arm-tdep.c (arm_record_vdata_transfer_insn): Added record handler for diff --git a/gdb/NEWS b/gdb/NEWS index d0a6ea557de..343ee49c0a1 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,8 @@ *** Changes since GDB 7.8 +* GDB now supports hardware watchpoints on x86 GNU Hurd. + * Python Scripting You can now access frame registers from Python scripts. diff --git a/gdb/config/i386/i386gnu.mh b/gdb/config/i386/i386gnu.mh index a3ea122b6d7..4cc23e4004e 100644 --- a/gdb/config/i386/i386gnu.mh +++ b/gdb/config/i386/i386gnu.mh @@ -1,5 +1,6 @@ # Host: Intel 386 running the GNU Hurd -NATDEPFILES= i386gnu-nat.o gnu-nat.o core-regset.o fork-child.o \ +NATDEPFILES= i386gnu-nat.o gnu-nat.o \ + x86-nat.o x86-dregs.o core-regset.o fork-child.o \ notify_S.o process_reply_S.o msg_reply_S.o \ msg_U.o exc_request_U.o exc_request_S.o HAVE_NATIVE_GCORE_HOST = 1 diff --git a/gdb/gnu-nat.c b/gdb/gnu-nat.c index c8164d60f30..d17a7503c60 100644 --- a/gdb/gnu-nat.c +++ b/gdb/gnu-nat.c @@ -983,6 +983,17 @@ inf_port_to_thread (struct inf *inf, mach_port_t port) return 0; } +/* See gnu-nat.h. */ + +void +inf_threads (struct inf *inf, inf_threads_ftype *f, void *arg) +{ + struct proc *thread; + + for (thread = inf->threads; thread; thread = thread->next) + f (thread, arg); +} + /* Make INF's list of threads be consistent with reality of TASK. */ void diff --git a/gdb/gnu-nat.h b/gdb/gnu-nat.h index 8e949eba985..43d5b753ac1 100644 --- a/gdb/gnu-nat.h +++ b/gdb/gnu-nat.h @@ -29,6 +29,11 @@ extern struct inf *gnu_current_inf; /* Converts a GDB pid to a struct proc. */ struct proc *inf_tid_to_thread (struct inf *inf, int tid); +typedef void (inf_threads_ftype) (struct proc *thread, void *arg); + +/* Call F for every thread in inferior INF, passing ARG as second parameter. */ +void inf_threads (struct inf *inf, inf_threads_ftype *f, void *arg); + /* Makes sure that INF's thread list is synced with the actual process. */ int inf_update_procs (struct inf *inf); diff --git a/gdb/i386gnu-nat.c b/gdb/i386gnu-nat.c index 8fad87117be..9317d29af81 100644 --- a/gdb/i386gnu-nat.c +++ b/gdb/i386gnu-nat.c @@ -18,6 +18,7 @@ along with this program. If not, see . */ #include "defs.h" +#include "x86-nat.h" #include "inferior.h" #include "floatformat.h" #include "regcache.h" @@ -30,6 +31,7 @@ #include "i386-tdep.h" #include "gnu-nat.h" +#include "inf-child.h" #include "i387-tdep.h" #ifdef HAVE_SYS_PROCFS_H @@ -304,6 +306,142 @@ gnu_store_registers (struct target_ops *ops, } } + +/* Support for debug registers. */ + +#ifdef i386_DEBUG_STATE +/* Get debug registers for thread THREAD. */ + +static void +i386_gnu_dr_get (struct i386_debug_state *regs, struct proc *thread) +{ + mach_msg_type_number_t count = i386_DEBUG_STATE_COUNT; + error_t err; + + err = thread_get_state (thread->port, i386_DEBUG_STATE, + (thread_state_t) regs, &count); + if (err != 0 || count != i386_DEBUG_STATE_COUNT) + warning (_("Couldn't fetch debug state from %s"), + proc_string (thread)); +} + +/* Set debug registers for thread THREAD. */ + +static void +i386_gnu_dr_set (const struct i386_debug_state *regs, struct proc *thread) +{ + error_t err; + + err = thread_set_state (thread->port, i386_DEBUG_STATE, + (thread_state_t) regs, i386_DEBUG_STATE_COUNT); + if (err != 0) + warning (_("Couldn't store debug state into %s"), + proc_string (thread)); +} + +/* Set DR_CONTROL in THREAD. */ + +static void +i386_gnu_dr_set_control_one (struct proc *thread, void *arg) +{ + unsigned long *control = arg; + struct i386_debug_state regs; + + i386_gnu_dr_get (®s, thread); + regs.dr[DR_CONTROL] = *control; + i386_gnu_dr_set (®s, thread); +} + +/* Set DR_CONTROL to CONTROL in all threads. */ + +static void +i386_gnu_dr_set_control (unsigned long control) +{ + inf_update_procs (gnu_current_inf); + inf_threads (gnu_current_inf, i386_gnu_dr_set_control_one, &control); +} + +/* Parameters to set a debugging address. */ + +struct reg_addr +{ + int regnum; /* Register number (zero based). */ + CORE_ADDR addr; /* Address. */ +}; + +/* Set address REGNUM (zero based) to ADDR in THREAD. */ + +static void +i386_gnu_dr_set_addr_one (struct proc *thread, void *arg) +{ + struct reg_addr *reg_addr = arg; + struct i386_debug_state regs; + + i386_gnu_dr_get (®s, thread); + regs.dr[reg_addr->regnum] = reg_addr->addr; + i386_gnu_dr_set (®s, thread); +} + +/* Set address REGNUM (zero based) to ADDR in all threads. */ + +static void +i386_gnu_dr_set_addr (int regnum, CORE_ADDR addr) +{ + struct reg_addr reg_addr; + + gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); + + reg_addr.regnum = regnum; + reg_addr.addr = addr; + + inf_update_procs (gnu_current_inf); + inf_threads (gnu_current_inf, i386_gnu_dr_set_addr_one, ®_addr); +} + +/* Get debug register REGNUM value from only the one LWP of PTID. */ + +static unsigned long +i386_gnu_dr_get_reg (ptid_t ptid, int regnum) +{ + struct i386_debug_state regs; + struct proc *thread; + + /* Make sure we know about new threads. */ + inf_update_procs (gnu_current_inf); + + thread = inf_tid_to_thread (gnu_current_inf, ptid_get_lwp (ptid)); + i386_gnu_dr_get (®s, thread); + + return regs.dr[regnum]; +} + +/* Return the inferior's debug register REGNUM. */ + +static CORE_ADDR +i386_gnu_dr_get_addr (int regnum) +{ + gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); + + return i386_gnu_dr_get_reg (inferior_ptid, regnum); +} + +/* Get DR_STATUS from only the one thread of INFERIOR_PTID. */ + +static unsigned long +i386_gnu_dr_get_status (void) +{ + return i386_gnu_dr_get_reg (inferior_ptid, DR_STATUS); +} + +/* Return the inferior's DR7 debug control register. */ + +static unsigned long +i386_gnu_dr_get_control (void) +{ + return i386_gnu_dr_get_reg (inferior_ptid, DR_CONTROL); +} +#endif /* i386_DEBUG_STATE */ + /* Provide a prototype to silence -Wmissing-prototypes. */ extern initialize_file_ftype _initialize_i386gnu_nat; @@ -315,6 +453,18 @@ _initialize_i386gnu_nat (void) /* Fill in the generic GNU/Hurd methods. */ t = gnu_target (); +#ifdef i386_DEBUG_STATE + x86_use_watchpoints (t); + + x86_dr_low.set_control = i386_gnu_dr_set_control; + gdb_assert (DR_FIRSTADDR == 0 && DR_LASTADDR < i386_DEBUG_STATE_COUNT); + x86_dr_low.set_addr = i386_gnu_dr_set_addr; + x86_dr_low.get_addr = i386_gnu_dr_get_addr; + x86_dr_low.get_status = i386_gnu_dr_get_status; + x86_dr_low.get_control = i386_gnu_dr_get_control; + x86_set_debug_register_length (4); +#endif /* i386_DEBUG_STATE */ + t->to_fetch_registers = gnu_fetch_registers; t->to_store_registers = gnu_store_registers; -- 2.30.2