From e0d24f8d6e485843de9fac1ba21412491e07be7b Mon Sep 17 00:00:00 2001 From: Wu Zhou Date: Wed, 8 Feb 2006 05:41:06 +0000 Subject: [PATCH] * ppc-linux-nat.c (PTRACE_GET_DEBUGREG, PTRACE_SET_DEBUGREG, PTRACE_GETSIGINFO): Define. (last_stopped_data_address): New. (ppc_linux_check_watch_resources): New function. (ppc_linux_region_ok_for_hw_watchpoint): New function. (ppc_linux_insert_watchpoint): New function. (ppc_linux_remove_watchpoint): New function. (ppc_linux_stopped_data_address): New function. (ppc_linux_stopped_by_watchpoint): New function. (_initialize_ppc_linux_nat): Set the above hardware watchpoint related target vectors. * rs6000-tdep.c (rs6000_gdbarch_init): Set PPC architectures to have nonsteppable watchpoint. * target.c (default_region_ok_for_hw_watchpoint, debug_to_region_ok_for_hw_watchpoint): New prototypes. (update_current_target): Inherit to_region_ok_for_hw_watchpoint and set default to_region_ok_for_hw_watchpoint. (default_region_ok_for_hw_watchpoint): New function. (debug_to_region_ok_for_hw_watchpoint): New function. (setup_target_debug): Set to_region_ok_for_hw_watchpoint of debug_target. * target.h (struct target_ops): Add a new target vector to_region_ok_for_hw_watchpoint. (TARGET_REGION_OK_FOR_HW_WATCHPOINT): Define this if it is not defined anyplace else. --- gdb/ChangeLog | 30 +++++++++++- gdb/ppc-linux-nat.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++- gdb/rs6000-tdep.c | 2 + gdb/target.c | 29 +++++++++++ gdb/target.h | 6 +++ 5 files changed, 203 insertions(+), 2 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 2a51386..679131a 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,33 @@ -2005-02-07 Joel Brobecker +2006-02-08 Ben Elliston + Wu Zhou + + * ppc-linux-nat.c (PTRACE_GET_DEBUGREG, PTRACE_SET_DEBUGREG, + PTRACE_GETSIGINFO): Define. + (last_stopped_data_address): New. + (ppc_linux_check_watch_resources): New function. + (ppc_linux_region_ok_for_hw_watchpoint): New function. + (ppc_linux_insert_watchpoint): New function. + (ppc_linux_remove_watchpoint): New function. + (ppc_linux_stopped_data_address): New function. + (ppc_linux_stopped_by_watchpoint): New function. + (_initialize_ppc_linux_nat): Set the above hardware watchpoint + related target vectors. + * rs6000-tdep.c (rs6000_gdbarch_init): Set PPC architectures + to have nonsteppable watchpoint. + * target.c (default_region_ok_for_hw_watchpoint, + debug_to_region_ok_for_hw_watchpoint): New prototypes. + (update_current_target): Inherit to_region_ok_for_hw_watchpoint + and set default to_region_ok_for_hw_watchpoint. + (default_region_ok_for_hw_watchpoint): New function. + (debug_to_region_ok_for_hw_watchpoint): New function. + (setup_target_debug): Set to_region_ok_for_hw_watchpoint of + debug_target. + * target.h (struct target_ops): Add a new target vector + to_region_ok_for_hw_watchpoint. + (TARGET_REGION_OK_FOR_HW_WATCHPOINT): Define this if it is not + defined anyplace else. +2005-02-07 Joel Brobecker * symfile.c (add_symbol_file_command): Abort if the user forgot to provide the address when the file has been loaded. diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c index c8c9376..b52eca2 100644 --- a/gdb/ppc-linux-nat.c +++ b/gdb/ppc-linux-nat.c @@ -81,6 +81,16 @@ #define PTRACE_SETEVRREGS 21 #endif +/* Similarly for the hardware watchpoint support. */ +#ifndef PTRACE_GET_DEBUGREG +#define PTRACE_GET_DEBUGREG 25 +#endif +#ifndef PTRACE_SET_DEBUGREG +#define PTRACE_SET_DEBUGREG 26 +#endif +#ifndef PTRACE_GETSIGINFO +#define PTRACE_GETSIGINFO 0x4202 +#endif /* This oddity is because the Linux kernel defines elf_vrregset_t as an array of 33 16 bytes long elements. I.e. it leaves out vrsave. @@ -146,6 +156,7 @@ struct gdb_evrregset_t error. */ int have_ptrace_getvrregs = 1; +static CORE_ADDR last_stopped_data_address = 0; /* Non-zero if our kernel may support the PTRACE_GETEVRREGS and PTRACE_SETEVRREGS requests, for reading and writing the SPE @@ -153,7 +164,6 @@ int have_ptrace_getvrregs = 1; error. */ int have_ptrace_getsetevrregs = 1; - int kernel_u_size (void) { @@ -777,6 +787,124 @@ store_ppc_registers (int tid) store_spe_register (tid, -1); } +static int +ppc_linux_check_watch_resources (int type, int cnt, int ot) +{ + int tid; + ptid_t ptid = inferior_ptid; + + /* DABR (data address breakpoint register) is optional for PPC variants. + Some variants have one DABR, others have none. So CNT can't be larger + than 1. */ + if (cnt > 1) + return 0; + + /* We need to know whether ptrace supports PTRACE_SET_DEBUGREG and whether + the target has DABR. If either answer is no, the ptrace call will + return -1. Fail in that case. */ + tid = TIDGET (ptid); + if (tid == 0) + tid = PIDGET (ptid); + + if (ptrace (PTRACE_SET_DEBUGREG, tid, 0, 0) == -1) + return 0; + return 1; +} + +static int +ppc_linux_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) +{ + /* Handle sub-8-byte quantities. */ + if (len <= 0) + return 0; + + /* addr+len must fall in the 8 byte watchable region. */ + if ((addr + len) > (addr & ~7) + 8) + return 0; + + return 1; +} + +/* Set a watchpoint of type TYPE at address ADDR. */ +static long +ppc_linux_insert_watchpoint (CORE_ADDR addr, int len, int rw) +{ + int tid; + long dabr_value; + ptid_t ptid = inferior_ptid; + + dabr_value = addr & ~7; + switch (rw) + { + case hw_read: + /* Set read and translate bits. */ + dabr_value |= 5; + break; + case hw_write: + /* Set write and translate bits. */ + dabr_value |= 6; + break; + case hw_access: + /* Set read, write and translate bits. */ + dabr_value |= 7; + break; + } + + tid = TIDGET (ptid); + if (tid == 0) + tid = PIDGET (ptid); + + return ptrace (PTRACE_SET_DEBUGREG, tid, 0, dabr_value); +} + +static long +ppc_linux_remove_watchpoint (CORE_ADDR addr, int len) +{ + int tid; + ptid_t ptid = inferior_ptid; + + tid = TIDGET (ptid); + if (tid == 0) + tid = PIDGET (ptid); + + return ptrace (PTRACE_SET_DEBUGREG, tid, 0, 0); +} + +static int +ppc_linux_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p) +{ + if (last_stopped_data_address) + { + *addr_p = last_stopped_data_address; + last_stopped_data_address = 0; + return 1; + } + return 0; +} + +static int +ppc_linux_stopped_by_watchpoint (void) +{ + int tid; + struct siginfo siginfo; + ptid_t ptid = inferior_ptid; + CORE_ADDR *addr_p; + + tid = TIDGET(ptid); + if (tid == 0) + tid = PIDGET (ptid); + + errno = 0; + ptrace (PTRACE_GETSIGINFO, tid, (PTRACE_TYPE_ARG3) 0, &siginfo); + + if (errno != 0 || siginfo.si_signo != SIGTRAP || + (siginfo.si_code & 0xffff) != 0x0004) + return 0; + + last_stopped_data_address = (CORE_ADDR) siginfo.si_addr; + return 1; +} + static void ppc_linux_store_inferior_registers (int regno) { @@ -900,6 +1028,14 @@ _initialize_ppc_linux_nat (void) t->to_fetch_registers = ppc_linux_fetch_inferior_registers; t->to_store_registers = ppc_linux_store_inferior_registers; + /* Add our watchpoint methods. */ + t->to_can_use_hw_breakpoint = ppc_linux_check_watch_resources; + t->to_region_ok_for_hw_watchpoint = ppc_linux_region_ok_for_hw_watchpoint; + t->to_insert_watchpoint = ppc_linux_insert_watchpoint; + t->to_remove_watchpoint = ppc_linux_remove_watchpoint; + t->to_stopped_by_watchpoint = ppc_linux_stopped_by_watchpoint; + t->to_stopped_data_address = ppc_linux_stopped_data_address; + /* Register the target. */ add_target (t); } diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c index 1d4a437..19a4bc9 100644 --- a/gdb/rs6000-tdep.c +++ b/gdb/rs6000-tdep.c @@ -3387,6 +3387,8 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) _("rs6000_gdbarch_init: " "received unexpected BFD 'arch' value")); + set_gdbarch_have_nonsteppable_watchpoint (gdbarch, 1); + /* Sanity check on registers. */ gdb_assert (strcmp (tdep->regs[tdep->ppc_gp0_regnum].name, "r0") == 0); diff --git a/gdb/target.c b/gdb/target.c index 81f8716..d542bbf 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -48,6 +48,8 @@ static void kill_or_be_killed (int); static void default_terminal_info (char *, int); +static int default_region_ok_for_hw_watchpoint (CORE_ADDR, int); + static int default_region_size_ok_for_hw_watchpoint (int); static int nosymbol (char *, CORE_ADDR *); @@ -129,6 +131,8 @@ static int debug_to_stopped_by_watchpoint (void); static int debug_to_stopped_data_address (struct target_ops *, CORE_ADDR *); +static int debug_to_region_ok_for_hw_watchpoint (CORE_ADDR, int); + static int debug_to_region_size_ok_for_hw_watchpoint (int); static void debug_to_terminal_init (void); @@ -406,6 +410,7 @@ update_current_target (void) INHERIT (to_stopped_data_address, t); INHERIT (to_stopped_by_watchpoint, t); INHERIT (to_have_continuable_watchpoint, t); + INHERIT (to_region_ok_for_hw_watchpoint, t); INHERIT (to_region_size_ok_for_hw_watchpoint, t); INHERIT (to_terminal_init, t); INHERIT (to_terminal_inferior, t); @@ -532,6 +537,8 @@ update_current_target (void) de_fault (to_stopped_data_address, (int (*) (struct target_ops *, CORE_ADDR *)) return_zero); + de_fault (to_region_ok_for_hw_watchpoint, + default_region_ok_for_hw_watchpoint); de_fault (to_region_size_ok_for_hw_watchpoint, default_region_size_ok_for_hw_watchpoint); de_fault (to_terminal_init, @@ -1579,6 +1586,12 @@ find_default_create_inferior (char *exec_file, char *allargs, char **env, } static int +default_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) +{ + return TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT (len); +} + +static int default_region_size_ok_for_hw_watchpoint (int byte_count) { return (byte_count <= TYPE_LENGTH (builtin_type_void_data_ptr)); @@ -2119,6 +2132,21 @@ debug_to_can_use_hw_breakpoint (int type, int cnt, int from_tty) } static int +debug_to_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) +{ + CORE_ADDR retval; + + retval = debug_target.to_region_ok_for_hw_watchpoint (addr, len); + + fprintf_unfiltered (gdb_stdlog, + "TARGET_REGION_OK_FOR_HW_WATCHPOINT (%ld, %ld) = 0x%lx\n", + (unsigned long) addr, + (unsigned long) len, + (unsigned long) retval); + return retval; +} + +static int debug_to_region_size_ok_for_hw_watchpoint (int byte_count) { CORE_ADDR retval; @@ -2537,6 +2565,7 @@ setup_target_debug (void) current_target.to_remove_watchpoint = debug_to_remove_watchpoint; current_target.to_stopped_by_watchpoint = debug_to_stopped_by_watchpoint; current_target.to_stopped_data_address = debug_to_stopped_data_address; + current_target.to_region_ok_for_hw_watchpoint = debug_to_region_ok_for_hw_watchpoint; current_target.to_region_size_ok_for_hw_watchpoint = debug_to_region_size_ok_for_hw_watchpoint; current_target.to_terminal_init = debug_to_terminal_init; current_target.to_terminal_inferior = debug_to_terminal_inferior; diff --git a/gdb/target.h b/gdb/target.h index 9bb31f9..7a72c36 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -346,6 +346,7 @@ struct target_ops int (*to_stopped_by_watchpoint) (void); int to_have_continuable_watchpoint; int (*to_stopped_data_address) (struct target_ops *, CORE_ADDR *); + int (*to_region_ok_for_hw_watchpoint) (CORE_ADDR, int); int (*to_region_size_ok_for_hw_watchpoint) (int); void (*to_terminal_init) (void); void (*to_terminal_inferior) (void); @@ -1031,6 +1032,11 @@ extern void (*deprecated_target_new_objfile_hook) (struct objfile *); (*current_target.to_can_use_hw_breakpoint) (TYPE, CNT, OTHERTYPE); #endif +#ifndef TARGET_REGION_OK_FOR_HW_WATCHPOINT +#define TARGET_REGION_OK_FOR_HW_WATCHPOINT(addr, len) \ + (*current_target.to_region_ok_for_hw_watchpoint) (addr, len) +#endif + #if !defined(TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT) #define TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT(byte_count) \ (*current_target.to_region_size_ok_for_hw_watchpoint) (byte_count) -- 2.7.4