/* GNU/Linux/ARM specific low level interface, for the remote server for GDB. Copyright (C) 1995-1996, 1998-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 "server.h" #include "linux-low.h" /* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h. On Bionic elf.h and linux/elf.h have conflicting definitions. */ #ifndef ELFMAG0 #include #endif #include #include /* Defined in auto-generated files. */ void init_registers_arm (void); void init_registers_arm_with_iwmmxt (void); void init_registers_arm_with_vfpv2 (void); void init_registers_arm_with_vfpv3 (void); void init_registers_arm_with_neon (void); #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 /* Information describing the hardware breakpoint capabilities. */ static struct { unsigned char arch; unsigned char max_wp_length; unsigned char wp_count; unsigned char bp_count; } arm_linux_hwbp_cap; /* 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; }; /* Since we cannot dynamically allocate subfields of arch_process_info, assume a maximum number of supported break-/watchpoints. */ #define MAX_BPTS 32 #define MAX_WPTS 32 /* Per-process arch-specific data we want to keep. */ struct arch_process_info { /* Hardware breakpoints for this process. */ struct arm_linux_hw_breakpoint bpts[MAX_BPTS]; /* Hardware watchpoints for this process. */ struct arm_linux_hw_breakpoint wpts[MAX_WPTS]; }; /* 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. */ char bpts_changed[MAX_BPTS]; char wpts_changed[MAX_WPTS]; /* Cached stopped data address. */ CORE_ADDR stopped_data_address; }; static unsigned long arm_hwcap; /* These are in in current kernels. */ #define HWCAP_VFP 64 #define HWCAP_IWMMXT 512 #define HWCAP_NEON 4096 #define HWCAP_VFPv3 8192 #define HWCAP_VFPv3D16 16384 #ifdef HAVE_SYS_REG_H #include #endif #define arm_num_regs 26 static int arm_regmap[] = { 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, 64 }; static int arm_cannot_store_register (int regno) { return (regno >= arm_num_regs); } static int arm_cannot_fetch_register (int regno) { return (regno >= arm_num_regs); } static void arm_fill_gregset (struct regcache *regcache, void *buf) { int i; for (i = 0; i < arm_num_regs; i++) if (arm_regmap[i] != -1) collect_register (regcache, i, ((char *) buf) + arm_regmap[i]); } static void arm_store_gregset (struct regcache *regcache, const void *buf) { int i; char zerobuf[8]; memset (zerobuf, 0, 8); for (i = 0; i < arm_num_regs; i++) if (arm_regmap[i] != -1) supply_register (regcache, i, ((char *) buf) + arm_regmap[i]); else supply_register (regcache, i, zerobuf); } static void arm_fill_wmmxregset (struct regcache *regcache, void *buf) { int i; if (!(arm_hwcap & HWCAP_IWMMXT)) return; for (i = 0; i < 16; i++) collect_register (regcache, arm_num_regs + i, (char *) buf + i * 8); /* We only have access to wcssf, wcasf, and wcgr0-wcgr3. */ for (i = 0; i < 6; i++) collect_register (regcache, arm_num_regs + i + 16, (char *) buf + 16 * 8 + i * 4); } static void arm_store_wmmxregset (struct regcache *regcache, const void *buf) { int i; if (!(arm_hwcap & HWCAP_IWMMXT)) return; for (i = 0; i < 16; i++) supply_register (regcache, arm_num_regs + i, (char *) buf + i * 8); /* We only have access to wcssf, wcasf, and wcgr0-wcgr3. */ for (i = 0; i < 6; i++) supply_register (regcache, arm_num_regs + i + 16, (char *) buf + 16 * 8 + i * 4); } static void arm_fill_vfpregset (struct regcache *regcache, void *buf) { int i, num, base; if (!(arm_hwcap & HWCAP_VFP)) return; if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3) num = 32; else num = 16; base = find_regno ("d0"); for (i = 0; i < num; i++) collect_register (regcache, base + i, (char *) buf + i * 8); collect_register_by_name (regcache, "fpscr", (char *) buf + 32 * 8); } static void arm_store_vfpregset (struct regcache *regcache, const void *buf) { int i, num, base; if (!(arm_hwcap & HWCAP_VFP)) return; if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3) num = 32; else num = 16; base = find_regno ("d0"); for (i = 0; i < num; i++) supply_register (regcache, base + i, (char *) buf + i * 8); supply_register_by_name (regcache, "fpscr", (char *) buf + 32 * 8); } extern int debug_threads; static CORE_ADDR arm_get_pc (struct regcache *regcache) { unsigned long pc; collect_register_by_name (regcache, "pc", &pc); if (debug_threads) fprintf (stderr, "stop pc is %08lx\n", pc); return pc; } static void arm_set_pc (struct regcache *regcache, CORE_ADDR pc) { unsigned long newpc = pc; supply_register_by_name (regcache, "pc", &newpc); } /* Correct in either endianness. */ static const unsigned long arm_breakpoint = 0xef9f0001; #define arm_breakpoint_len 4 static const unsigned short thumb_breakpoint = 0xde01; static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 }; /* For new EABI binaries. We recognize it regardless of which ABI is used for gdbserver, so single threaded debugging should work OK, but for multi-threaded debugging we only insert the current ABI's breakpoint instruction. For now at least. */ static const unsigned long arm_eabi_breakpoint = 0xe7f001f0; static int arm_breakpoint_at (CORE_ADDR where) { struct regcache *regcache = get_thread_regcache (current_inferior, 1); unsigned long cpsr; collect_register_by_name (regcache, "cpsr", &cpsr); if (cpsr & 0x20) { /* Thumb mode. */ unsigned short insn; (*the_target->read_memory) (where, (unsigned char *) &insn, 2); if (insn == thumb_breakpoint) return 1; if (insn == thumb2_breakpoint[0]) { (*the_target->read_memory) (where + 2, (unsigned char *) &insn, 2); if (insn == thumb2_breakpoint[1]) return 1; } } else { /* ARM mode. */ unsigned long insn; (*the_target->read_memory) (where, (unsigned char *) &insn, 4); if (insn == arm_breakpoint) return 1; if (insn == arm_eabi_breakpoint) return 1; } return 0; } /* We only place breakpoints in empty marker functions, and thread locking is outside of the function. So rather than importing software single-step, we can just run until exit. */ static CORE_ADDR arm_reinsert_addr (void) { struct regcache *regcache = get_thread_regcache (current_inferior, 1); unsigned long pc; collect_register_by_name (regcache, "lr", &pc); return pc; } /* 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; } /* Query Hardware Breakpoint information for the target we are attached to (using PID as ptrace argument) and set up arm_linux_hwbp_cap. */ static void arm_linux_init_hwbp_cap (int pid) { unsigned int val; if (ptrace (PTRACE_GETHBPREGS, pid, 0, &val) < 0) return; arm_linux_hwbp_cap.arch = (unsigned char)((val >> 24) & 0xff); if (arm_linux_hwbp_cap.arch == 0) return; arm_linux_hwbp_cap.max_wp_length = (unsigned char)((val >> 16) & 0xff); arm_linux_hwbp_cap.wp_count = (unsigned char)((val >> 8) & 0xff); arm_linux_hwbp_cap.bp_count = (unsigned char)(val & 0xff); if (arm_linux_hwbp_cap.wp_count > MAX_WPTS) internal_error (__FILE__, __LINE__, "Unsupported number of watchpoints"); if (arm_linux_hwbp_cap.bp_count > MAX_BPTS) internal_error (__FILE__, __LINE__, "Unsupported number of breakpoints"); } /* How many hardware breakpoints are available? */ static int arm_linux_get_hw_breakpoint_count (void) { return arm_linux_hwbp_cap.bp_count; } /* How many hardware watchpoints are available? */ static int arm_linux_get_hw_watchpoint_count (void) { return arm_linux_hwbp_cap.wp_count; } /* Maximum length of area watched by hardware watchpoint. */ static int arm_linux_get_hw_watchpoint_max_length (void) { return arm_linux_hwbp_cap.max_wp_length; } /* 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; } /* Is the breakpoint control value CONTROL initialized? */ static int arm_hwbp_control_is_initialized (arm_hwbp_control_t control) { return control != 0; } /* 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; } /* 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; } /* Initialize the hardware breakpoint structure P for a breakpoint or watchpoint at ADDR to LEN. The type of watchpoint is given in TYPE. Returns -1 if TYPE is unsupported, 0 if TYPE represents a breakpoint, and 1 if type represents a watchpoint. */ static int arm_linux_hw_point_initialize (char type, CORE_ADDR addr, int len, struct arm_linux_hw_breakpoint *p) { arm_hwbp_type hwbp_type; unsigned mask; /* Breakpoint/watchpoint types (GDB terminology): 0 = memory breakpoint for instructions (not supported; done via memory write instead) 1 = hardware breakpoint for instructions (supported) 2 = write watchpoint (supported) 3 = read watchpoint (supported) 4 = access watchpoint (supported). */ switch (type) { case '1': hwbp_type = arm_hwbp_break; break; case '2': hwbp_type = arm_hwbp_store; break; case '3': hwbp_type = arm_hwbp_load; break; case '4': hwbp_type = arm_hwbp_access; break; default: /* Unsupported. */ return -1; } if (hwbp_type == arm_hwbp_break) { /* For breakpoints, the length field encodes the mode. */ switch (len) { case 2: /* 16-bit Thumb mode breakpoint */ case 3: /* 32-bit Thumb mode breakpoint */ mask = 0x3 << (addr & 2); break; case 4: /* 32-bit ARM mode breakpoint */ mask = 0xf; break; default: /* Unsupported. */ return -1; } addr &= ~3; } else { CORE_ADDR max_wp_length = arm_linux_get_hw_watchpoint_max_length (); CORE_ADDR aligned_addr; /* Can not set watchpoints for zero or negative lengths. */ if (len <= 0) return -1; /* The current ptrace interface can only handle watchpoints that are a power of 2. */ if ((len & (len - 1)) != 0) return -1; /* Test that the range [ADDR, ADDR + LEN) fits into the largest address range covered by a watchpoint. */ aligned_addr = addr & ~(max_wp_length - 1); if (aligned_addr + max_wp_length < addr + len) return -1; mask = (1 << len) - 1; } p->address = (unsigned int) addr; p->control = arm_hwbp_control_initialize (mask, hwbp_type, 1); return hwbp_type != arm_hwbp_break; } /* Callback to mark a watch-/breakpoint to be updated in all threads of the current process. */ struct update_registers_data { int watch; int i; }; static int update_registers_callback (struct inferior_list_entry *entry, void *arg) { struct lwp_info *lwp = (struct lwp_info *) entry; struct update_registers_data *data = (struct update_registers_data *) arg; /* Only update the threads of the current process. */ if (pid_of (lwp) == pid_of (get_thread_lwp (current_inferior))) { /* The actual update is done later just before resuming the lwp, we just mark that the registers need updating. */ if (data->watch) lwp->arch_private->wpts_changed[data->i] = 1; else lwp->arch_private->bpts_changed[data->i] = 1; /* If the lwp isn't stopped, force it to momentarily pause, so we can update its breakpoint registers. */ if (!lwp->stopped) linux_stop_lwp (lwp); } return 0; } /* Insert hardware break-/watchpoint. */ static int arm_insert_point (char type, CORE_ADDR addr, int len) { struct process_info *proc = current_process (); struct arm_linux_hw_breakpoint p, *pts; int watch, i, count; watch = arm_linux_hw_point_initialize (type, addr, len, &p); if (watch < 0) { /* Unsupported. */ return 1; } if (watch) { count = arm_linux_get_hw_watchpoint_count (); pts = proc->private->arch_private->wpts; } else { count = arm_linux_get_hw_breakpoint_count (); pts = proc->private->arch_private->bpts; } for (i = 0; i < count; i++) if (!arm_hwbp_control_is_enabled (pts[i].control)) { struct update_registers_data data = { watch, i }; pts[i] = p; find_inferior (&all_lwps, update_registers_callback, &data); return 0; } /* We're out of watchpoints. */ return -1; } /* Remove hardware break-/watchpoint. */ static int arm_remove_point (char type, CORE_ADDR addr, int len) { struct process_info *proc = current_process (); struct arm_linux_hw_breakpoint p, *pts; int watch, i, count; watch = arm_linux_hw_point_initialize (type, addr, len, &p); if (watch < 0) { /* Unsupported. */ return -1; } if (watch) { count = arm_linux_get_hw_watchpoint_count (); pts = proc->private->arch_private->wpts; } else { count = arm_linux_get_hw_breakpoint_count (); pts = proc->private->arch_private->bpts; } for (i = 0; i < count; i++) if (arm_linux_hw_breakpoint_equal (&p, pts + i)) { struct update_registers_data data = { watch, i }; pts[i].control = arm_hwbp_control_disable (pts[i].control); find_inferior (&all_lwps, update_registers_callback, &data); return 0; } /* No watchpoint matched. */ return -1; } /* Return whether current thread is stopped due to a watchpoint. */ static int arm_stopped_by_watchpoint (void) { struct lwp_info *lwp = get_thread_lwp (current_inferior); struct siginfo siginfo; /* We must be able to set hardware watchpoints. */ if (arm_linux_get_hw_watchpoint_count () == 0) return 0; /* Retrieve siginfo. */ errno = 0; ptrace (PTRACE_GETSIGINFO, lwpid_of (lwp), 0, &siginfo); if (errno != 0) return 0; /* This must be a hardware breakpoint. */ if (siginfo.si_signo != SIGTRAP || (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */) return 0; /* If we are in a positive slot then we're looking at a breakpoint and not a watchpoint. */ if (siginfo.si_errno >= 0) return 0; /* Cache stopped data address for use by arm_stopped_data_address. */ lwp->arch_private->stopped_data_address = (CORE_ADDR) (uintptr_t) siginfo.si_addr; return 1; } /* Return data address that triggered watchpoint. Called only if arm_stopped_by_watchpoint returned true. */ static CORE_ADDR arm_stopped_data_address (void) { struct lwp_info *lwp = get_thread_lwp (current_inferior); return lwp->arch_private->stopped_data_address; } /* Called when a new process is created. */ static struct arch_process_info * arm_new_process (void) { struct arch_process_info *info = xcalloc (1, sizeof (*info)); return info; } /* Called when a new thread is detected. */ static struct arch_lwp_info * arm_new_thread (void) { struct arch_lwp_info *info = xcalloc (1, sizeof (*info)); int i; for (i = 0; i < MAX_BPTS; i++) info->bpts_changed[i] = 1; for (i = 0; i < MAX_WPTS; i++) info->wpts_changed[i] = 1; return info; } /* Called when resuming a thread. If the debug regs have changed, update the thread's copies. */ static void arm_prepare_to_resume (struct lwp_info *lwp) { int pid = lwpid_of (lwp); struct process_info *proc = find_process_pid (pid_of (lwp)); struct arch_process_info *proc_info = proc->private->arch_private; struct arch_lwp_info *lwp_info = lwp->arch_private; int i; for (i = 0; i < arm_linux_get_hw_breakpoint_count (); i++) if (lwp_info->bpts_changed[i]) { errno = 0; if (arm_hwbp_control_is_enabled (proc_info->bpts[i].control)) if (ptrace (PTRACE_SETHBPREGS, pid, ((i << 1) + 1), &proc_info->bpts[i].address) < 0) perror_with_name ("Unexpected error setting breakpoint address"); if (arm_hwbp_control_is_initialized (proc_info->bpts[i].control)) if (ptrace (PTRACE_SETHBPREGS, pid, ((i << 1) + 2), &proc_info->bpts[i].control) < 0) perror_with_name ("Unexpected error setting breakpoint"); lwp_info->bpts_changed[i] = 0; } for (i = 0; i < arm_linux_get_hw_watchpoint_count (); i++) if (lwp_info->wpts_changed[i]) { errno = 0; if (arm_hwbp_control_is_enabled (proc_info->wpts[i].control)) if (ptrace (PTRACE_SETHBPREGS, pid, -((i << 1) + 1), &proc_info->wpts[i].address) < 0) perror_with_name ("Unexpected error setting watchpoint address"); if (arm_hwbp_control_is_initialized (proc_info->wpts[i].control)) if (ptrace (PTRACE_SETHBPREGS, pid, -((i << 1) + 2), &proc_info->wpts[i].control) < 0) perror_with_name ("Unexpected error setting watchpoint"); lwp_info->wpts_changed[i] = 0; } } static int arm_get_hwcap (unsigned long *valp) { unsigned char *data = alloca (8); int offset = 0; while ((*the_target->read_auxv) (offset, data, 8) == 8) { unsigned int *data_p = (unsigned int *)data; if (data_p[0] == AT_HWCAP) { *valp = data_p[1]; return 1; } offset += 8; } *valp = 0; return 0; } static void arm_arch_setup (void) { int pid = lwpid_of (get_thread_lwp (current_inferior)); /* Query hardware watchpoint/breakpoint capabilities. */ arm_linux_init_hwbp_cap (pid); arm_hwcap = 0; if (arm_get_hwcap (&arm_hwcap) == 0) { init_registers_arm (); return; } if (arm_hwcap & HWCAP_IWMMXT) { init_registers_arm_with_iwmmxt (); return; } if (arm_hwcap & HWCAP_VFP) { char *buf; /* NEON implies either no VFP, or VFPv3-D32. We only support it with VFP. */ if (arm_hwcap & HWCAP_NEON) init_registers_arm_with_neon (); else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3) init_registers_arm_with_vfpv3 (); else init_registers_arm_with_vfpv2 (); /* Now make sure that the kernel supports reading these registers. Support was added in 2.6.30. */ errno = 0; buf = xmalloc (32 * 8 + 4); if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0 && errno == EIO) { arm_hwcap = 0; init_registers_arm (); } free (buf); return; } /* The default configuration uses legacy FPA registers, probably simulated. */ init_registers_arm (); } struct regset_info target_regsets[] = { { PTRACE_GETREGS, PTRACE_SETREGS, 0, 18 * 4, GENERAL_REGS, arm_fill_gregset, arm_store_gregset }, { PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS, 0, 16 * 8 + 6 * 4, EXTENDED_REGS, arm_fill_wmmxregset, arm_store_wmmxregset }, { PTRACE_GETVFPREGS, PTRACE_SETVFPREGS, 0, 32 * 8 + 4, EXTENDED_REGS, arm_fill_vfpregset, arm_store_vfpregset }, { 0, 0, 0, -1, -1, NULL, NULL } }; struct linux_target_ops the_low_target = { arm_arch_setup, arm_num_regs, arm_regmap, arm_cannot_fetch_register, arm_cannot_store_register, arm_get_pc, arm_set_pc, /* Define an ARM-mode breakpoint; we only set breakpoints in the C library, which is most likely to be ARM. If the kernel supports clone events, we will never insert a breakpoint, so even a Thumb C library will work; so will mixing EABI/non-EABI gdbserver and application. */ #ifndef __ARM_EABI__ (const unsigned char *) &arm_breakpoint, #else (const unsigned char *) &arm_eabi_breakpoint, #endif arm_breakpoint_len, arm_reinsert_addr, 0, arm_breakpoint_at, arm_insert_point, arm_remove_point, arm_stopped_by_watchpoint, arm_stopped_data_address, NULL, /* collect_ptrace_register */ NULL, /* supply_ptrace_register */ NULL, /* siginfo_fixup */ arm_new_process, arm_new_thread, arm_prepare_to_resume, };