/* GNU/Linux on ARM native support. Copyright (C) 1999-2002, 2004-2012 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 "defs.h" #include "inferior.h" #include "gdbcore.h" #include "gdb_string.h" #include "regcache.h" #include "target.h" #include "linux-nat.h" #include "target-descriptions.h" #include "auxv.h" #include "observer.h" #include "gdbthread.h" #include "arm-tdep.h" #include "arm-linux-tdep.h" #include #include #include #include #include /* Prototypes for supply_gregset etc. */ #include "gregset.h" /* Defines ps_err_e, struct ps_prochandle. */ #include "gdb_proc_service.h" #ifndef PTRACE_GET_THREAD_AREA #define PTRACE_GET_THREAD_AREA 22 #endif #ifndef PTRACE_GETWMMXREGS #define PTRACE_GETWMMXREGS 18 #define PTRACE_SETWMMXREGS 19 #endif #ifndef PTRACE_GETVFPREGS #define PTRACE_GETVFPREGS 27 #define PTRACE_SETVFPREGS 28 #endif #ifndef PTRACE_GETHBPREGS #define PTRACE_GETHBPREGS 29 #define PTRACE_SETHBPREGS 30 #endif /* A flag for whether the WMMX registers are available. */ static int arm_linux_has_wmmx_registers; /* The number of 64-bit VFP registers we have (expect this to be 0, 16, or 32). */ static int arm_linux_vfp_register_count; extern int arm_apcs_32; /* On GNU/Linux, threads are implemented as pseudo-processes, in which case we may be tracing more than one process at a time. In that case, inferior_ptid will contain the main process ID and the individual thread (process) ID. get_thread_id () is used to get the thread id if it's available, and the process id otherwise. */ static int get_thread_id (ptid_t ptid) { int tid = TIDGET (ptid); if (0 == tid) tid = PIDGET (ptid); return tid; } #define GET_THREAD_ID(PTID) get_thread_id (PTID) /* Get the value of a particular register from the floating point state of the process and store it into regcache. */ static void fetch_fpregister (struct regcache *regcache, int regno) { int ret, tid; gdb_byte fp[ARM_LINUX_SIZEOF_NWFPE]; /* Get the thread id for the ptrace call. */ tid = GET_THREAD_ID (inferior_ptid); /* Read the floating point state. */ ret = ptrace (PT_GETFPREGS, tid, 0, fp); if (ret < 0) { warning (_("Unable to fetch floating point register.")); return; } /* Fetch fpsr. */ if (ARM_FPS_REGNUM == regno) regcache_raw_supply (regcache, ARM_FPS_REGNUM, fp + NWFPE_FPSR_OFFSET); /* Fetch the floating point register. */ if (regno >= ARM_F0_REGNUM && regno <= ARM_F7_REGNUM) supply_nwfpe_register (regcache, regno, fp); } /* Get the whole floating point state of the process and store it into regcache. */ static void fetch_fpregs (struct regcache *regcache) { int ret, regno, tid; gdb_byte fp[ARM_LINUX_SIZEOF_NWFPE]; /* Get the thread id for the ptrace call. */ tid = GET_THREAD_ID (inferior_ptid); /* Read the floating point state. */ ret = ptrace (PT_GETFPREGS, tid, 0, fp); if (ret < 0) { warning (_("Unable to fetch the floating point registers.")); return; } /* Fetch fpsr. */ regcache_raw_supply (regcache, ARM_FPS_REGNUM, fp + NWFPE_FPSR_OFFSET); /* Fetch the floating point registers. */ for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++) supply_nwfpe_register (regcache, regno, fp); } /* Save a particular register into the floating point state of the process using the contents from regcache. */ static void store_fpregister (const struct regcache *regcache, int regno) { int ret, tid; gdb_byte fp[ARM_LINUX_SIZEOF_NWFPE]; /* Get the thread id for the ptrace call. */ tid = GET_THREAD_ID (inferior_ptid); /* Read the floating point state. */ ret = ptrace (PT_GETFPREGS, tid, 0, fp); if (ret < 0) { warning (_("Unable to fetch the floating point registers.")); return; } /* Store fpsr. */ if (ARM_FPS_REGNUM == regno && REG_VALID == regcache_register_status (regcache, ARM_FPS_REGNUM)) regcache_raw_collect (regcache, ARM_FPS_REGNUM, fp + NWFPE_FPSR_OFFSET); /* Store the floating point register. */ if (regno >= ARM_F0_REGNUM && regno <= ARM_F7_REGNUM) collect_nwfpe_register (regcache, regno, fp); ret = ptrace (PTRACE_SETFPREGS, tid, 0, fp); if (ret < 0) { warning (_("Unable to store floating point register.")); return; } } /* Save the whole floating point state of the process using the contents from regcache. */ static void store_fpregs (const struct regcache *regcache) { int ret, regno, tid; gdb_byte fp[ARM_LINUX_SIZEOF_NWFPE]; /* Get the thread id for the ptrace call. */ tid = GET_THREAD_ID (inferior_ptid); /* Read the floating point state. */ ret = ptrace (PT_GETFPREGS, tid, 0, fp); if (ret < 0) { warning (_("Unable to fetch the floating point registers.")); return; } /* Store fpsr. */ if (REG_VALID == regcache_register_status (regcache, ARM_FPS_REGNUM)) regcache_raw_collect (regcache, ARM_FPS_REGNUM, fp + NWFPE_FPSR_OFFSET); /* Store the floating point registers. */ for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++) if (REG_VALID == regcache_register_status (regcache, regno)) collect_nwfpe_register (regcache, regno, fp); ret = ptrace (PTRACE_SETFPREGS, tid, 0, fp); if (ret < 0) { warning (_("Unable to store floating point registers.")); return; } } /* Fetch a general register of the process and store into regcache. */ static void fetch_register (struct regcache *regcache, int regno) { int ret, tid; elf_gregset_t regs; /* Get the thread id for the ptrace call. */ tid = GET_THREAD_ID (inferior_ptid); ret = ptrace (PTRACE_GETREGS, tid, 0, ®s); if (ret < 0) { warning (_("Unable to fetch general register.")); return; } if (regno >= ARM_A1_REGNUM && regno < ARM_PC_REGNUM) regcache_raw_supply (regcache, regno, (char *) ®s[regno]); if (ARM_PS_REGNUM == regno) { if (arm_apcs_32) regcache_raw_supply (regcache, ARM_PS_REGNUM, (char *) ®s[ARM_CPSR_GREGNUM]); else regcache_raw_supply (regcache, ARM_PS_REGNUM, (char *) ®s[ARM_PC_REGNUM]); } if (ARM_PC_REGNUM == regno) { regs[ARM_PC_REGNUM] = gdbarch_addr_bits_remove (get_regcache_arch (regcache), regs[ARM_PC_REGNUM]); regcache_raw_supply (regcache, ARM_PC_REGNUM, (char *) ®s[ARM_PC_REGNUM]); } } /* Fetch all general registers of the process and store into regcache. */ static void fetch_regs (struct regcache *regcache) { int ret, regno, tid; elf_gregset_t regs; /* Get the thread id for the ptrace call. */ tid = GET_THREAD_ID (inferior_ptid); ret = ptrace (PTRACE_GETREGS, tid, 0, ®s); if (ret < 0) { warning (_("Unable to fetch general registers.")); return; } for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++) regcache_raw_supply (regcache, regno, (char *) ®s[regno]); if (arm_apcs_32) regcache_raw_supply (regcache, ARM_PS_REGNUM, (char *) ®s[ARM_CPSR_GREGNUM]); else regcache_raw_supply (regcache, ARM_PS_REGNUM, (char *) ®s[ARM_PC_REGNUM]); regs[ARM_PC_REGNUM] = gdbarch_addr_bits_remove (get_regcache_arch (regcache), regs[ARM_PC_REGNUM]); regcache_raw_supply (regcache, ARM_PC_REGNUM, (char *) ®s[ARM_PC_REGNUM]); } /* Store all general registers of the process from the values in regcache. */ static void store_register (const struct regcache *regcache, int regno) { int ret, tid; elf_gregset_t regs; if (REG_VALID != regcache_register_status (regcache, regno)) return; /* Get the thread id for the ptrace call. */ tid = GET_THREAD_ID (inferior_ptid); /* Get the general registers from the process. */ ret = ptrace (PTRACE_GETREGS, tid, 0, ®s); if (ret < 0) { warning (_("Unable to fetch general registers.")); return; } if (regno >= ARM_A1_REGNUM && regno <= ARM_PC_REGNUM) regcache_raw_collect (regcache, regno, (char *) ®s[regno]); else if (arm_apcs_32 && regno == ARM_PS_REGNUM) regcache_raw_collect (regcache, regno, (char *) ®s[ARM_CPSR_GREGNUM]); else if (!arm_apcs_32 && regno == ARM_PS_REGNUM) regcache_raw_collect (regcache, ARM_PC_REGNUM, (char *) ®s[ARM_PC_REGNUM]); ret = ptrace (PTRACE_SETREGS, tid, 0, ®s); if (ret < 0) { warning (_("Unable to store general register.")); return; } } static void store_regs (const struct regcache *regcache) { int ret, regno, tid; elf_gregset_t regs; /* Get the thread id for the ptrace call. */ tid = GET_THREAD_ID (inferior_ptid); /* Fetch the general registers. */ ret = ptrace (PTRACE_GETREGS, tid, 0, ®s); if (ret < 0) { warning (_("Unable to fetch general registers.")); return; } for (regno = ARM_A1_REGNUM; regno <= ARM_PC_REGNUM; regno++) { if (REG_VALID == regcache_register_status (regcache, regno)) regcache_raw_collect (regcache, regno, (char *) ®s[regno]); } if (arm_apcs_32 && REG_VALID == regcache_register_status (regcache, ARM_PS_REGNUM)) regcache_raw_collect (regcache, ARM_PS_REGNUM, (char *) ®s[ARM_CPSR_GREGNUM]); ret = ptrace (PTRACE_SETREGS, tid, 0, ®s); if (ret < 0) { warning (_("Unable to store general registers.")); return; } } /* Fetch all WMMX registers of the process and store into regcache. */ #define IWMMXT_REGS_SIZE (16 * 8 + 6 * 4) static void fetch_wmmx_regs (struct regcache *regcache) { char regbuf[IWMMXT_REGS_SIZE]; int ret, regno, tid; /* Get the thread id for the ptrace call. */ tid = GET_THREAD_ID (inferior_ptid); ret = ptrace (PTRACE_GETWMMXREGS, tid, 0, regbuf); if (ret < 0) { warning (_("Unable to fetch WMMX registers.")); return; } for (regno = 0; regno < 16; regno++) regcache_raw_supply (regcache, regno + ARM_WR0_REGNUM, ®buf[regno * 8]); for (regno = 0; regno < 2; regno++) regcache_raw_supply (regcache, regno + ARM_WCSSF_REGNUM, ®buf[16 * 8 + regno * 4]); for (regno = 0; regno < 4; regno++) regcache_raw_supply (regcache, regno + ARM_WCGR0_REGNUM, ®buf[16 * 8 + 2 * 4 + regno * 4]); } static void store_wmmx_regs (const struct regcache *regcache) { char regbuf[IWMMXT_REGS_SIZE]; int ret, regno, tid; /* Get the thread id for the ptrace call. */ tid = GET_THREAD_ID (inferior_ptid); ret = ptrace (PTRACE_GETWMMXREGS, tid, 0, regbuf); if (ret < 0) { warning (_("Unable to fetch WMMX registers.")); return; } for (regno = 0; regno < 16; regno++) if (REG_VALID == regcache_register_status (regcache, regno + ARM_WR0_REGNUM)) regcache_raw_collect (regcache, regno + ARM_WR0_REGNUM, ®buf[regno * 8]); for (regno = 0; regno < 2; regno++) if (REG_VALID == regcache_register_status (regcache, regno + ARM_WCSSF_REGNUM)) regcache_raw_collect (regcache, regno + ARM_WCSSF_REGNUM, ®buf[16 * 8 + regno * 4]); for (regno = 0; regno < 4; regno++) if (REG_VALID == regcache_register_status (regcache, regno + ARM_WCGR0_REGNUM)) regcache_raw_collect (regcache, regno + ARM_WCGR0_REGNUM, ®buf[16 * 8 + 2 * 4 + regno * 4]); ret = ptrace (PTRACE_SETWMMXREGS, tid, 0, regbuf); if (ret < 0) { warning (_("Unable to store WMMX registers.")); return; } } /* Fetch and store VFP Registers. The kernel object has space for 32 64-bit registers, and the FPSCR. This is even when on a VFPv2 or VFPv3D16 target. */ #define VFP_REGS_SIZE (32 * 8 + 4) static void fetch_vfp_regs (struct regcache *regcache) { char regbuf[VFP_REGS_SIZE]; int ret, regno, tid; /* Get the thread id for the ptrace call. */ tid = GET_THREAD_ID (inferior_ptid); ret = ptrace (PTRACE_GETVFPREGS, tid, 0, regbuf); if (ret < 0) { warning (_("Unable to fetch VFP registers.")); return; } for (regno = 0; regno < arm_linux_vfp_register_count; regno++) regcache_raw_supply (regcache, regno + ARM_D0_REGNUM, (char *) regbuf + regno * 8); regcache_raw_supply (regcache, ARM_FPSCR_REGNUM, (char *) regbuf + 32 * 8); } static void store_vfp_regs (const struct regcache *regcache) { char regbuf[VFP_REGS_SIZE]; int ret, regno, tid; /* Get the thread id for the ptrace call. */ tid = GET_THREAD_ID (inferior_ptid); ret = ptrace (PTRACE_GETVFPREGS, tid, 0, regbuf); if (ret < 0) { warning (_("Unable to fetch VFP registers (for update).")); return; } for (regno = 0; regno < arm_linux_vfp_register_count; regno++) regcache_raw_collect (regcache, regno + ARM_D0_REGNUM, (char *) regbuf + regno * 8); regcache_raw_collect (regcache, ARM_FPSCR_REGNUM, (char *) regbuf + 32 * 8); ret = ptrace (PTRACE_SETVFPREGS, tid, 0, regbuf); if (ret < 0) { warning (_("Unable to store VFP registers.")); return; } } /* Fetch registers from the child process. Fetch all registers if regno == -1, otherwise fetch all general registers or all floating point registers depending upon the value of regno. */ static void arm_linux_fetch_inferior_registers (struct target_ops *ops, struct regcache *regcache, int regno) { if (-1 == regno) { fetch_regs (regcache); fetch_fpregs (regcache); if (arm_linux_has_wmmx_registers) fetch_wmmx_regs (regcache); if (arm_linux_vfp_register_count > 0) fetch_vfp_regs (regcache); } else { if (regno < ARM_F0_REGNUM || regno == ARM_PS_REGNUM) fetch_register (regcache, regno); else if (regno >= ARM_F0_REGNUM && regno <= ARM_FPS_REGNUM) fetch_fpregister (regcache, regno); else if (arm_linux_has_wmmx_registers && regno >= ARM_WR0_REGNUM && regno <= ARM_WCGR7_REGNUM) fetch_wmmx_regs (regcache); else if (arm_linux_vfp_register_count > 0 && regno >= ARM_D0_REGNUM && regno <= ARM_D0_REGNUM + arm_linux_vfp_register_count) fetch_vfp_regs (regcache); } } /* Store registers back into the inferior. Store all registers if regno == -1, otherwise store all general registers or all floating point registers depending upon the value of regno. */ static void arm_linux_store_inferior_registers (struct target_ops *ops, struct regcache *regcache, int regno) { if (-1 == regno) { store_regs (regcache); store_fpregs (regcache); if (arm_linux_has_wmmx_registers) store_wmmx_regs (regcache); if (arm_linux_vfp_register_count > 0) store_vfp_regs (regcache); } else { if (regno < ARM_F0_REGNUM || regno == ARM_PS_REGNUM) store_register (regcache, regno); else if ((regno >= ARM_F0_REGNUM) && (regno <= ARM_FPS_REGNUM)) store_fpregister (regcache, regno); else if (arm_linux_has_wmmx_registers && regno >= ARM_WR0_REGNUM && regno <= ARM_WCGR7_REGNUM) store_wmmx_regs (regcache); else if (arm_linux_vfp_register_count > 0 && regno >= ARM_D0_REGNUM && regno <= ARM_D0_REGNUM + arm_linux_vfp_register_count) store_vfp_regs (regcache); } } /* Wrapper functions for the standard regset handling, used by thread debugging. */ void fill_gregset (const struct regcache *regcache, gdb_gregset_t *gregsetp, int regno) { arm_linux_collect_gregset (NULL, regcache, regno, gregsetp, 0); } void supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp) { arm_linux_supply_gregset (NULL, regcache, -1, gregsetp, 0); } void fill_fpregset (const struct regcache *regcache, gdb_fpregset_t *fpregsetp, int regno) { arm_linux_collect_nwfpe (NULL, regcache, regno, fpregsetp, 0); } /* Fill GDB's register array with the floating-point register values in *fpregsetp. */ void supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregsetp) { arm_linux_supply_nwfpe (NULL, regcache, -1, fpregsetp, 0); } /* Fetch the thread-local storage pointer for libthread_db. */ ps_err_e ps_get_thread_area (const struct ps_prochandle *ph, lwpid_t lwpid, int idx, void **base) { if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0) return PS_ERR; /* IDX is the bias from the thread pointer to the beginning of the thread descriptor. It has to be subtracted due to implementation quirks in libthread_db. */ *base = (void *) ((char *)*base - idx); return PS_OK; } static const struct target_desc * arm_linux_read_description (struct target_ops *ops) { CORE_ADDR arm_hwcap = 0; arm_linux_has_wmmx_registers = 0; arm_linux_vfp_register_count = 0; if (target_auxv_search (ops, AT_HWCAP, &arm_hwcap) != 1) { return NULL; } if (arm_hwcap & HWCAP_IWMMXT) { arm_linux_has_wmmx_registers = 1; return tdesc_arm_with_iwmmxt; } if (arm_hwcap & HWCAP_VFP) { int pid; char *buf; const struct target_desc * result = NULL; /* NEON implies VFPv3-D32 or no-VFP unit. Say that we only support Neon with VFPv3-D32. */ if (arm_hwcap & HWCAP_NEON) { arm_linux_vfp_register_count = 32; result = tdesc_arm_with_neon; } else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3) { arm_linux_vfp_register_count = 32; result = tdesc_arm_with_vfpv3; } else { arm_linux_vfp_register_count = 16; result = tdesc_arm_with_vfpv2; } /* Now make sure that the kernel supports reading these registers. Support was added in 2.6.30. */ pid = GET_LWP (inferior_ptid); errno = 0; buf = alloca (VFP_REGS_SIZE); if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0 && errno == EIO) result = NULL; return result; } return NULL; } /* Information describing the hardware breakpoint capabilities. */ struct arm_linux_hwbp_cap { gdb_byte arch; gdb_byte max_wp_length; gdb_byte wp_count; gdb_byte bp_count; }; /* Get hold of the Hardware Breakpoint information for the target we are attached to. Returns NULL if the kernel doesn't support Hardware breakpoints at all, or a pointer to the information structure. */ static const struct arm_linux_hwbp_cap * arm_linux_get_hwbp_cap (void) { /* The info structure we return. */ static struct arm_linux_hwbp_cap info; /* Is INFO in a good state? -1 means that no attempt has been made to initialize INFO; 0 means an attempt has been made, but it failed; 1 means INFO is in an initialized state. */ static int available = -1; if (available == -1) { int tid; unsigned int val; tid = GET_THREAD_ID (inferior_ptid); if (ptrace (PTRACE_GETHBPREGS, tid, 0, &val) < 0) available = 0; else { info.arch = (gdb_byte)((val >> 24) & 0xff); info.max_wp_length = (gdb_byte)((val >> 16) & 0xff); info.wp_count = (gdb_byte)((val >> 8) & 0xff); info.bp_count = (gdb_byte)(val & 0xff); available = (info.arch != 0); } } return available == 1 ? &info : NULL; } /* How many hardware breakpoints are available? */ static int arm_linux_get_hw_breakpoint_count (void) { const struct arm_linux_hwbp_cap *cap = arm_linux_get_hwbp_cap (); return cap != NULL ? cap->bp_count : 0; } /* How many hardware watchpoints are available? */ static int arm_linux_get_hw_watchpoint_count (void) { const struct arm_linux_hwbp_cap *cap = arm_linux_get_hwbp_cap (); return cap != NULL ? cap->wp_count : 0; } /* Have we got a free break-/watch-point available for use? Returns -1 if there is not an appropriate resource available, otherwise returns 1. */ static int arm_linux_can_use_hw_breakpoint (int type, int cnt, int ot) { if (type == bp_hardware_watchpoint || type == bp_read_watchpoint || type == bp_access_watchpoint || type == bp_watchpoint) { if (cnt + ot > arm_linux_get_hw_watchpoint_count ()) return -1; } else if (type == bp_hardware_breakpoint) { if (cnt > arm_linux_get_hw_breakpoint_count ()) return -1; } else gdb_assert (FALSE); return 1; } /* Enum describing the different types of ARM hardware break-/watch-points. */ typedef enum { arm_hwbp_break = 0, arm_hwbp_load = 1, arm_hwbp_store = 2, arm_hwbp_access = 3 } arm_hwbp_type; /* Type describing an ARM Hardware Breakpoint Control register value. */ typedef unsigned int arm_hwbp_control_t; /* Structure used to keep track of hardware break-/watch-points. */ struct arm_linux_hw_breakpoint { /* Address to break on, or being watched. */ unsigned int address; /* Control register for break-/watch- point. */ arm_hwbp_control_t control; }; /* Structure containing arrays of the break and watch points which are have active in each thread. The Linux ptrace interface to hardware break-/watch-points presents the values in a vector centred around 0 (which is used fo generic information). Positive indicies refer to breakpoint addresses/control registers, negative indices to watchpoint addresses/control registers. The Linux vector is indexed as follows: -((i << 1) + 2): Control register for watchpoint i. -((i << 1) + 1): Address register for watchpoint i. 0: Information register. ((i << 1) + 1): Address register for breakpoint i. ((i << 1) + 2): Control register for breakpoint i. This structure is used as a per-thread cache of the state stored by the kernel, so that we don't need to keep calling into the kernel to find a free breakpoint. We treat break-/watch-points with their enable bit clear as being deleted. */ typedef struct arm_linux_thread_points { /* Thread ID. */ int tid; /* Breakpoints for thread. */ struct arm_linux_hw_breakpoint *bpts; /* Watchpoint for threads. */ struct arm_linux_hw_breakpoint *wpts; } *arm_linux_thread_points_p; DEF_VEC_P (arm_linux_thread_points_p); /* Vector of hardware breakpoints for each thread. */ VEC(arm_linux_thread_points_p) *arm_threads = NULL; /* Find the list of hardware break-/watch-points for a thread with id TID. If no list exists for TID we return NULL if ALLOC_NEW is 0, otherwise we create a new list and return that. */ static struct arm_linux_thread_points * arm_linux_find_breakpoints_by_tid (int tid, int alloc_new) { int i; struct arm_linux_thread_points *t; for (i = 0; VEC_iterate (arm_linux_thread_points_p, arm_threads, i, t); ++i) { if (t->tid == tid) return t; } t = NULL; if (alloc_new) { t = xmalloc (sizeof (struct arm_linux_thread_points)); t->tid = tid; t->bpts = xzalloc (arm_linux_get_hw_breakpoint_count () * sizeof (struct arm_linux_hw_breakpoint)); t->wpts = xzalloc (arm_linux_get_hw_watchpoint_count () * sizeof (struct arm_linux_hw_breakpoint)); VEC_safe_push (arm_linux_thread_points_p, arm_threads, t); } return t; } /* Initialize an ARM hardware break-/watch-point control register value. BYTE_ADDRESS_SELECT is the mask of bytes to trigger on; HWBP_TYPE is the type of break-/watch-point; ENABLE indicates whether the point is enabled. */ static arm_hwbp_control_t arm_hwbp_control_initialize (unsigned byte_address_select, arm_hwbp_type hwbp_type, int enable) { gdb_assert ((byte_address_select & ~0xffU) == 0); gdb_assert (hwbp_type != arm_hwbp_break || ((byte_address_select & 0xfU) != 0)); return (byte_address_select << 5) | (hwbp_type << 3) | (3 << 1) | enable; } /* Does the breakpoint control value CONTROL have the enable bit set? */ static int arm_hwbp_control_is_enabled (arm_hwbp_control_t control) { return control & 0x1; } /* Change a breakpoint control word so that it is in the disabled state. */ static arm_hwbp_control_t arm_hwbp_control_disable (arm_hwbp_control_t control) { return control & ~0x1; } /* Initialise the hardware breakpoint structure P. The breakpoint will be enabled, and will point to the placed address of BP_TGT. */ static void arm_linux_hw_breakpoint_initialize (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt, struct arm_linux_hw_breakpoint *p) { unsigned mask; CORE_ADDR address = bp_tgt->placed_address; /* We have to create a mask for the control register which says which bits of the word pointed to by address to break on. */ if (arm_pc_is_thumb (gdbarch, address)) mask = 0x3 << (address & 2); else mask = 0xf; p->address = (unsigned int) (address & ~3); p->control = arm_hwbp_control_initialize (mask, arm_hwbp_break, 1); } /* Get the ARM hardware breakpoint type from the RW value we're given when asked to set a watchpoint. */ static arm_hwbp_type arm_linux_get_hwbp_type (int rw) { if (rw == hw_read) return arm_hwbp_load; else if (rw == hw_write) return arm_hwbp_store; else return arm_hwbp_access; } /* Initialize the hardware breakpoint structure P for a watchpoint at ADDR to LEN. The type of watchpoint is given in RW. */ static void arm_linux_hw_watchpoint_initialize (CORE_ADDR addr, int len, int rw, struct arm_linux_hw_breakpoint *p) { const struct arm_linux_hwbp_cap *cap = arm_linux_get_hwbp_cap (); unsigned mask; gdb_assert (cap != NULL); gdb_assert (cap->max_wp_length != 0); mask = (1 << len) - 1; p->address = (unsigned int) addr; p->control = arm_hwbp_control_initialize (mask, arm_linux_get_hwbp_type (rw), 1); } /* Are two break-/watch-points equal? */ static int arm_linux_hw_breakpoint_equal (const struct arm_linux_hw_breakpoint *p1, const struct arm_linux_hw_breakpoint *p2) { return p1->address == p2->address && p1->control == p2->control; } /* Insert the hardware breakpoint (WATCHPOINT = 0) or watchpoint (WATCHPOINT =1) BPT for thread TID. */ static void arm_linux_insert_hw_breakpoint1 (const struct arm_linux_hw_breakpoint* bpt, int tid, int watchpoint) { struct arm_linux_thread_points *t = arm_linux_find_breakpoints_by_tid (tid, 1); gdb_byte count, i; struct arm_linux_hw_breakpoint* bpts; int dir; gdb_assert (t != NULL); if (watchpoint) { count = arm_linux_get_hw_watchpoint_count (); bpts = t->wpts; dir = -1; } else { count = arm_linux_get_hw_breakpoint_count (); bpts = t->bpts; dir = 1; } for (i = 0; i < count; ++i) if (!arm_hwbp_control_is_enabled (bpts[i].control)) { errno = 0; if (ptrace (PTRACE_SETHBPREGS, tid, dir * ((i << 1) + 1), &bpt->address) < 0) perror_with_name (_("Unexpected error setting breakpoint address")); if (ptrace (PTRACE_SETHBPREGS, tid, dir * ((i << 1) + 2), &bpt->control) < 0) perror_with_name (_("Unexpected error setting breakpoint")); memcpy (bpts + i, bpt, sizeof (struct arm_linux_hw_breakpoint)); break; } gdb_assert (i != count); } /* Remove the hardware breakpoint (WATCHPOINT = 0) or watchpoint (WATCHPOINT = 1) BPT for thread TID. */ static void arm_linux_remove_hw_breakpoint1 (const struct arm_linux_hw_breakpoint *bpt, int tid, int watchpoint) { struct arm_linux_thread_points *t = arm_linux_find_breakpoints_by_tid (tid, 0); gdb_byte count, i; struct arm_linux_hw_breakpoint *bpts; int dir; gdb_assert (t != NULL); if (watchpoint) { count = arm_linux_get_hw_watchpoint_count (); bpts = t->wpts; dir = -1; } else { count = arm_linux_get_hw_breakpoint_count (); bpts = t->bpts; dir = 1; } for (i = 0; i < count; ++i) if (arm_linux_hw_breakpoint_equal (bpt, bpts + i)) { errno = 0; bpts[i].control = arm_hwbp_control_disable (bpts[i].control); if (ptrace (PTRACE_SETHBPREGS, tid, dir * ((i << 1) + 2), &bpts[i].control) < 0) perror_with_name (_("Unexpected error clearing breakpoint")); break; } gdb_assert (i != count); } /* Insert a Hardware breakpoint. */ static int arm_linux_insert_hw_breakpoint (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) { struct lwp_info *lp; struct arm_linux_hw_breakpoint p; if (arm_linux_get_hw_breakpoint_count () == 0) return -1; arm_linux_hw_breakpoint_initialize (gdbarch, bp_tgt, &p); ALL_LWPS (lp) arm_linux_insert_hw_breakpoint1 (&p, TIDGET (lp->ptid), 0); return 0; } /* Remove a hardware breakpoint. */ static int arm_linux_remove_hw_breakpoint (struct gdbarch *gdbarch, struct bp_target_info *bp_tgt) { struct lwp_info *lp; struct arm_linux_hw_breakpoint p; if (arm_linux_get_hw_breakpoint_count () == 0) return -1; arm_linux_hw_breakpoint_initialize (gdbarch, bp_tgt, &p); ALL_LWPS (lp) arm_linux_remove_hw_breakpoint1 (&p, TIDGET (lp->ptid), 0); return 0; } /* Are we able to use a hardware watchpoint for the LEN bytes starting at ADDR? */ static int arm_linux_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) { const struct arm_linux_hwbp_cap *cap = arm_linux_get_hwbp_cap (); CORE_ADDR max_wp_length, aligned_addr; /* Can not set watchpoints for zero or negative lengths. */ if (len <= 0) return 0; /* Need to be able to use the ptrace interface. */ if (cap == NULL || cap->wp_count == 0) return 0; /* Test that the range [ADDR, ADDR + LEN) fits into the largest address range covered by a watchpoint. */ max_wp_length = (CORE_ADDR)cap->max_wp_length; aligned_addr = addr & ~(max_wp_length - 1); if (aligned_addr + max_wp_length < addr + len) return 0; /* The current ptrace interface can only handle watchpoints that are a power of 2. */ if ((len & (len - 1)) != 0) return 0; /* All tests passed so we must be able to set a watchpoint. */ return 1; } /* Insert a Hardware breakpoint. */ static int arm_linux_insert_watchpoint (CORE_ADDR addr, int len, int rw, struct expression *cond) { struct lwp_info *lp; struct arm_linux_hw_breakpoint p; if (arm_linux_get_hw_watchpoint_count () == 0) return -1; arm_linux_hw_watchpoint_initialize (addr, len, rw, &p); ALL_LWPS (lp) arm_linux_insert_hw_breakpoint1 (&p, TIDGET (lp->ptid), 1); return 0; } /* Remove a hardware breakpoint. */ static int arm_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw, struct expression *cond) { struct lwp_info *lp; struct arm_linux_hw_breakpoint p; if (arm_linux_get_hw_watchpoint_count () == 0) return -1; arm_linux_hw_watchpoint_initialize (addr, len, rw, &p); ALL_LWPS (lp) arm_linux_remove_hw_breakpoint1 (&p, TIDGET (lp->ptid), 1); return 0; } /* What was the data address the target was stopped on accessing. */ static int arm_linux_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p) { struct siginfo *siginfo_p = linux_nat_get_siginfo (inferior_ptid); int slot = siginfo_p->si_errno; /* This must be a hardware breakpoint. */ if (siginfo_p->si_signo != SIGTRAP || (siginfo_p->si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */) return 0; /* We must be able to set hardware watchpoints. */ if (arm_linux_get_hw_watchpoint_count () == 0) return 0; /* If we are in a positive slot then we're looking at a breakpoint and not a watchpoint. */ if (slot >= 0) return 0; *addr_p = (CORE_ADDR) (uintptr_t) siginfo_p->si_addr; return 1; } /* Has the target been stopped by hitting a watchpoint? */ static int arm_linux_stopped_by_watchpoint (void) { CORE_ADDR addr; return arm_linux_stopped_data_address (¤t_target, &addr); } static int arm_linux_watchpoint_addr_within_range (struct target_ops *target, CORE_ADDR addr, CORE_ADDR start, int length) { return start <= addr && start + length - 1 >= addr; } /* Handle thread creation. We need to copy the breakpoints and watchpoints in the parent thread to the child thread. */ static void arm_linux_new_thread (struct lwp_info *lp) { int tid = TIDGET (lp->ptid); const struct arm_linux_hwbp_cap *info = arm_linux_get_hwbp_cap (); if (info != NULL) { int i; struct arm_linux_thread_points *p; struct arm_linux_hw_breakpoint *bpts; if (VEC_empty (arm_linux_thread_points_p, arm_threads)) return; /* Get a list of breakpoints from any thread. */ p = VEC_last (arm_linux_thread_points_p, arm_threads); /* Copy that thread's breakpoints and watchpoints to the new thread. */ for (i = 0; i < info->bp_count; i++) if (arm_hwbp_control_is_enabled (p->bpts[i].control)) arm_linux_insert_hw_breakpoint1 (p->bpts + i, tid, 0); for (i = 0; i < info->wp_count; i++) if (arm_hwbp_control_is_enabled (p->wpts[i].control)) arm_linux_insert_hw_breakpoint1 (p->wpts + i, tid, 1); } } /* Handle thread exit. Tidy up the memory that has been allocated for the thread. */ static void arm_linux_thread_exit (struct thread_info *tp, int silent) { const struct arm_linux_hwbp_cap *info = arm_linux_get_hwbp_cap (); if (info != NULL) { int i; int tid = TIDGET (tp->ptid); struct arm_linux_thread_points *t = NULL, *p; for (i = 0; VEC_iterate (arm_linux_thread_points_p, arm_threads, i, p); i++) { if (p->tid == tid) { t = p; break; } } if (t == NULL) return; VEC_unordered_remove (arm_linux_thread_points_p, arm_threads, i); xfree (t->bpts); xfree (t->wpts); xfree (t); } } void _initialize_arm_linux_nat (void); void _initialize_arm_linux_nat (void) { struct target_ops *t; /* Fill in the generic GNU/Linux methods. */ t = linux_target (); /* Add our register access methods. */ t->to_fetch_registers = arm_linux_fetch_inferior_registers; t->to_store_registers = arm_linux_store_inferior_registers; /* Add our hardware breakpoint and watchpoint implementation. */ t->to_can_use_hw_breakpoint = arm_linux_can_use_hw_breakpoint; t->to_insert_hw_breakpoint = arm_linux_insert_hw_breakpoint; t->to_remove_hw_breakpoint = arm_linux_remove_hw_breakpoint; t->to_region_ok_for_hw_watchpoint = arm_linux_region_ok_for_hw_watchpoint; t->to_insert_watchpoint = arm_linux_insert_watchpoint; t->to_remove_watchpoint = arm_linux_remove_watchpoint; t->to_stopped_by_watchpoint = arm_linux_stopped_by_watchpoint; t->to_stopped_data_address = arm_linux_stopped_data_address; t->to_watchpoint_addr_within_range = arm_linux_watchpoint_addr_within_range; t->to_read_description = arm_linux_read_description; /* Register the target. */ linux_nat_add_target (t); /* Handle thread creation and exit */ observer_attach_thread_exit (arm_linux_thread_exit); linux_nat_set_new_thread (t, arm_linux_new_thread); }