/* GNU/Linux/ARM specific low level interface, for the remote server for GDB.
- Copyright (C) 1995-2014 Free Software Foundation, Inc.
+ Copyright (C) 1995-2015 Free Software Foundation, Inc.
This file is part of GDB.
#include "server.h"
#include "linux-low.h"
+#include "arch/arm.h"
+#include "linux-aarch32-low.h"
+#include <sys/uio.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 <elf.h>
#endif
-#include <sys/ptrace.h>
+#include "nat/gdb_ptrace.h"
#include <signal.h>
+#include "arch/arm.h"
+
/* Defined in auto-generated files. */
void init_registers_arm (void);
extern const struct target_desc *tdesc_arm;
void init_registers_arm_with_vfpv3 (void);
extern const struct target_desc *tdesc_arm_with_vfpv3;
-void init_registers_arm_with_neon (void);
-extern const struct target_desc *tdesc_arm_with_neon;
-
#ifndef PTRACE_GET_THREAD_AREA
#define PTRACE_GET_THREAD_AREA 22
#endif
arm_hwbp_access = 3
} arm_hwbp_type;
+/* Enum describing the different kinds of breakpoints. */
+enum arm_breakpoint_kinds
+{
+ ARM_BP_KIND_THUMB = 2,
+ ARM_BP_KIND_THUMB2 = 3,
+ ARM_BP_KIND_ARM = 4,
+};
+
/* Type describing an ARM Hardware Breakpoint Control register value. */
typedef unsigned int arm_hwbp_control_t;
CORE_ADDR stopped_data_address;
};
-static unsigned long arm_hwcap;
-
/* These are in <asm/elf.h> in current kernels. */
#define HWCAP_VFP 64
#define HWCAP_IWMMXT 512
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))
+ if (regcache->tdesc != tdesc_arm_with_iwmmxt)
return;
for (i = 0; i < 16; i++)
{
int i;
- if (!(arm_hwcap & HWCAP_IWMMXT))
+ if (regcache->tdesc != tdesc_arm_with_iwmmxt)
return;
for (i = 0; i < 16; i++)
static void
arm_fill_vfpregset (struct regcache *regcache, void *buf)
{
- int i, num, base;
+ int num;
- if (!(arm_hwcap & HWCAP_VFP))
- return;
-
- if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
+ if (regcache->tdesc == tdesc_arm_with_neon
+ || regcache->tdesc == tdesc_arm_with_vfpv3)
num = 32;
- else
+ else if (regcache->tdesc == tdesc_arm_with_vfpv2)
num = 16;
+ else
+ return;
- base = find_regno (regcache->tdesc, "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);
+ arm_fill_vfpregset_num (regcache, buf, num);
}
static void
arm_store_vfpregset (struct regcache *regcache, const void *buf)
{
- int i, num, base;
-
- if (!(arm_hwcap & HWCAP_VFP))
- return;
+ int num;
- if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
+ if (regcache->tdesc == tdesc_arm_with_neon
+ || regcache->tdesc == tdesc_arm_with_vfpv3)
num = 32;
- else
+ else if (regcache->tdesc == tdesc_arm_with_vfpv2)
num = 16;
+ else
+ return;
- base = find_regno (regcache->tdesc, "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);
+ arm_store_vfpregset_num (regcache, buf, num);
}
extern int debug_threads;
unsigned long pc;
collect_register_by_name (regcache, "pc", &pc);
if (debug_threads)
- fprintf (stderr, "stop pc is %08lx\n", pc);
+ debug_printf ("stop pc is %08lx\n", pc);
return pc;
}
}
/* 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 };
+#define arm_abi_breakpoint 0xef9f0001UL
/* 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;
+#define arm_eabi_breakpoint 0xe7f001f0UL
+
+#ifndef __ARM_EABI__
+static const unsigned long arm_breakpoint = arm_abi_breakpoint;
+#else
+static const unsigned long arm_breakpoint = arm_eabi_breakpoint;
+#endif
+
+#define arm_breakpoint_len 4
+static const unsigned short thumb_breakpoint = 0xde01;
+#define thumb_breakpoint_len 2
+static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
+#define thumb2_breakpoint_len 4
static int
arm_breakpoint_at (CORE_ADDR where)
{
- struct regcache *regcache = get_thread_regcache (current_inferior, 1);
+ struct regcache *regcache = get_thread_regcache (current_thread, 1);
unsigned long cpsr;
collect_register_by_name (regcache, "cpsr", &cpsr);
unsigned long insn;
(*the_target->read_memory) (where, (unsigned char *) &insn, 4);
- if (insn == arm_breakpoint)
+ if (insn == arm_abi_breakpoint)
return 1;
if (insn == arm_eabi_breakpoint)
static CORE_ADDR
arm_reinsert_addr (void)
{
- struct regcache *regcache = get_thread_regcache (current_inferior, 1);
+ struct regcache *regcache = get_thread_regcache (current_thread, 1);
unsigned long pc;
collect_register_by_name (regcache, "lr", &pc);
return pc;
return p1->address == p2->address && p1->control == p2->control;
}
+/* Convert a raw breakpoint type to an enum arm_hwbp_type. */
+
+static arm_hwbp_type
+raw_bkpt_type_to_arm_hwbp_type (enum raw_bkpt_type raw_type)
+{
+ switch (raw_type)
+ {
+ case raw_bkpt_type_hw:
+ return arm_hwbp_break;
+ case raw_bkpt_type_write_wp:
+ return arm_hwbp_store;
+ case raw_bkpt_type_read_wp:
+ return arm_hwbp_load;
+ case raw_bkpt_type_access_wp:
+ return arm_hwbp_access;
+ default:
+ gdb_assert_not_reached ("unhandled raw type");
+ }
+}
+
/* 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, or -2 if the particular combination
of ADDR and LEN cannot be implemented. Otherwise, returns 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_linux_hw_point_initialize (enum raw_bkpt_type raw_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;
- }
+ hwbp_type = raw_bkpt_type_to_arm_hwbp_type (raw_type);
if (hwbp_type == arm_hwbp_break)
{
static int
update_registers_callback (struct inferior_list_entry *entry, void *arg)
{
- struct lwp_info *lwp = (struct lwp_info *) entry;
+ struct thread_info *thread = (struct thread_info *) entry;
+ struct lwp_info *lwp = get_thread_lwp (thread);
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)))
+ if (pid_of (thread) == pid_of (current_thread))
{
/* The actual update is done later just before resuming the lwp,
we just mark that the registers need updating. */
return 0;
}
+static int
+arm_supports_z_point_type (char z_type)
+{
+ switch (z_type)
+ {
+ case Z_PACKET_SW_BP:
+ case Z_PACKET_HW_BP:
+ case Z_PACKET_WRITE_WP:
+ case Z_PACKET_READ_WP:
+ case Z_PACKET_ACCESS_WP:
+ return 1;
+ default:
+ /* Leave the handling of sw breakpoints with the gdb client. */
+ return 0;
+ }
+}
+
/* Insert hardware break-/watchpoint. */
static int
-arm_insert_point (char type, CORE_ADDR addr, int len)
+arm_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int len, struct raw_breakpoint *bp)
{
struct process_info *proc = current_process ();
struct arm_linux_hw_breakpoint p, *pts;
if (watch)
{
count = arm_linux_get_hw_watchpoint_count ();
- pts = proc->private->arch_private->wpts;
+ pts = proc->priv->arch_private->wpts;
}
else
{
count = arm_linux_get_hw_breakpoint_count ();
- pts = proc->private->arch_private->bpts;
+ pts = proc->priv->arch_private->bpts;
}
for (i = 0; i < count; i++)
{
struct update_registers_data data = { watch, i };
pts[i] = p;
- find_inferior (&all_lwps, update_registers_callback, &data);
+ find_inferior (&all_threads, update_registers_callback, &data);
return 0;
}
/* Remove hardware break-/watchpoint. */
static int
-arm_remove_point (char type, CORE_ADDR addr, int len)
+arm_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int len, struct raw_breakpoint *bp)
{
struct process_info *proc = current_process ();
struct arm_linux_hw_breakpoint p, *pts;
if (watch)
{
count = arm_linux_get_hw_watchpoint_count ();
- pts = proc->private->arch_private->wpts;
+ pts = proc->priv->arch_private->wpts;
}
else
{
count = arm_linux_get_hw_breakpoint_count ();
- pts = proc->private->arch_private->bpts;
+ pts = proc->priv->arch_private->bpts;
}
for (i = 0; i < count; 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);
+ find_inferior (&all_threads, update_registers_callback, &data);
return 0;
}
static int
arm_stopped_by_watchpoint (void)
{
- struct lwp_info *lwp = get_thread_lwp (current_inferior);
+ struct lwp_info *lwp = get_thread_lwp (current_thread);
siginfo_t siginfo;
/* We must be able to set hardware watchpoints. */
/* Retrieve siginfo. */
errno = 0;
- ptrace (PTRACE_GETSIGINFO, lwpid_of (lwp), 0, &siginfo);
+ ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread), 0, &siginfo);
if (errno != 0)
return 0;
static CORE_ADDR
arm_stopped_data_address (void)
{
- struct lwp_info *lwp = get_thread_lwp (current_inferior);
+ struct lwp_info *lwp = get_thread_lwp (current_thread);
return lwp->arch_private->stopped_data_address;
}
static struct arch_process_info *
arm_new_process (void)
{
- struct arch_process_info *info = xcalloc (1, sizeof (*info));
+ struct arch_process_info *info = XCNEW (struct arch_process_info);
return info;
}
/* Called when a new thread is detected. */
-static struct arch_lwp_info *
-arm_new_thread (void)
+static void
+arm_new_thread (struct lwp_info *lwp)
{
- struct arch_lwp_info *info = xcalloc (1, sizeof (*info));
+ struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
int i;
for (i = 0; i < MAX_BPTS; i++)
for (i = 0; i < MAX_WPTS; i++)
info->wpts_changed[i] = 1;
- return info;
+ lwp->arch_private = info;
+}
+
+static void
+arm_new_fork (struct process_info *parent, struct process_info *child)
+{
+ struct arch_process_info *parent_proc_info;
+ struct arch_process_info *child_proc_info;
+ struct lwp_info *child_lwp;
+ struct arch_lwp_info *child_lwp_info;
+ int i;
+
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->priv != NULL
+ && parent->priv->arch_private != NULL);
+ gdb_assert (child->priv != NULL
+ && child->priv->arch_private != NULL);
+
+ parent_proc_info = parent->priv->arch_private;
+ child_proc_info = child->priv->arch_private;
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child_proc_info = *parent_proc_info;
+
+ /* Mark all the hardware breakpoints and watchpoints as changed to
+ make sure that the registers will be updated. */
+ child_lwp = find_lwp_pid (ptid_of (child));
+ child_lwp_info = child_lwp->arch_private;
+ for (i = 0; i < MAX_BPTS; i++)
+ child_lwp_info->bpts_changed[i] = 1;
+ for (i = 0; i < MAX_WPTS; i++)
+ child_lwp_info->wpts_changed[i] = 1;
}
/* Called when resuming a thread.
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 thread_info *thread = get_lwp_thread (lwp);
+ int pid = lwpid_of (thread);
+ struct process_info *proc = find_process_pid (pid_of (thread));
+ struct arch_process_info *proc_info = proc->priv->arch_private;
struct arch_lwp_info *lwp_info = lwp->arch_private;
int i;
static int
arm_get_hwcap (unsigned long *valp)
{
- unsigned char *data = alloca (8);
+ unsigned char *data = (unsigned char *) alloca (8);
int offset = 0;
while ((*the_target->read_auxv) (offset, data, 8) == 8)
static const struct target_desc *
arm_read_description (void)
{
- int pid = lwpid_of (get_thread_lwp (current_inferior));
+ int pid = lwpid_of (current_thread);
+ unsigned long arm_hwcap = 0;
/* Query hardware watchpoint/breakpoint capabilities. */
arm_linux_init_hwbp_cap (pid);
- arm_hwcap = 0;
if (arm_get_hwcap (&arm_hwcap) == 0)
return tdesc_arm;
/* Now make sure that the kernel supports reading these
registers. Support was added in 2.6.30. */
errno = 0;
- buf = xmalloc (32 * 8 + 4);
+ buf = (char *) xmalloc (32 * 8 + 4);
if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0
&& errno == EIO)
- {
- arm_hwcap = 0;
- result = tdesc_arm;
- }
+ result = tdesc_arm;
+
free (buf);
return result;
static void
arm_arch_setup (void)
{
+ int tid = lwpid_of (current_thread);
+ int gpregs[18];
+ struct iovec iov;
+
current_process ()->tdesc = arm_read_description ();
+
+ iov.iov_base = gpregs;
+ iov.iov_len = sizeof (gpregs);
+
+ /* Check if PTRACE_GETREGSET works. */
+ if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov) == 0)
+ have_ptrace_getregset = 1;
+ else
+ have_ptrace_getregset = 0;
}
+/* Register sets without using PTRACE_GETREGSET. */
+
static struct regset_info arm_regsets[] = {
{ PTRACE_GETREGS, PTRACE_SETREGS, 0, 18 * 4,
GENERAL_REGS,
{ PTRACE_GETVFPREGS, PTRACE_SETVFPREGS, 0, 32 * 8 + 4,
EXTENDED_REGS,
arm_fill_vfpregset, arm_store_vfpregset },
- { 0, 0, 0, -1, -1, NULL, NULL }
+ NULL_REGSET
};
static struct regsets_info arm_regsets_info =
arm_regmap,
};
-static struct regs_info regs_info =
+static struct regs_info regs_info_arm =
{
NULL, /* regset_bitmap */
&arm_usrregs_info,
static const struct regs_info *
arm_regs_info (void)
{
- return ®s_info;
+ const struct target_desc *tdesc = current_process ()->tdesc;
+
+ if (have_ptrace_getregset == 1
+ && (tdesc == tdesc_arm_with_neon || tdesc == tdesc_arm_with_vfpv3))
+ return ®s_info_aarch32;
+ else
+ return ®s_info_arm;
+}
+
+/* Implementation of linux_target_ops method "breakpoint_kind_from_pc".
+
+ Determine the type and size of breakpoint to insert at PCPTR. Uses the
+ program counter value to determine whether a 16-bit or 32-bit breakpoint
+ should be used. It returns the breakpoint's kind, and adjusts the program
+ counter (if necessary) to point to the actual memory location where the
+ breakpoint should be inserted. */
+
+static int
+arm_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
+{
+ if (IS_THUMB_ADDR (*pcptr))
+ {
+ gdb_byte buf[2];
+
+ *pcptr = UNMAKE_THUMB_ADDR (*pcptr);
+
+ /* Check whether we are replacing a thumb2 32-bit instruction. */
+ if ((*the_target->read_memory) (*pcptr, buf, 2) == 0)
+ {
+ unsigned short inst1 = 0;
+
+ (*the_target->read_memory) (*pcptr, (gdb_byte *) &inst1, 2);
+ if (thumb_insn_size (inst1) == 4)
+ return ARM_BP_KIND_THUMB2;
+ }
+ return ARM_BP_KIND_THUMB;
+ }
+ else
+ return ARM_BP_KIND_ARM;
+}
+
+/* Implementation of the linux_target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+arm_sw_breakpoint_from_kind (int kind , int *size)
+{
+ *size = arm_breakpoint_len;
+ /* 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. */
+ switch (kind)
+ {
+ case ARM_BP_KIND_THUMB:
+ *size = thumb_breakpoint_len;
+ return (gdb_byte *) &thumb_breakpoint;
+ case ARM_BP_KIND_THUMB2:
+ *size = thumb2_breakpoint_len;
+ return (gdb_byte *) &thumb2_breakpoint;
+ case ARM_BP_KIND_ARM:
+ *size = arm_breakpoint_len;
+ return (const gdb_byte *) &arm_breakpoint;
+ default:
+ return NULL;
+ }
+ return NULL;
}
struct linux_target_ops the_low_target = {
NULL, /* fetch_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_breakpoint_kind_from_pc,
+ arm_sw_breakpoint_from_kind,
arm_reinsert_addr,
0,
arm_breakpoint_at,
+ arm_supports_z_point_type,
arm_insert_point,
arm_remove_point,
arm_stopped_by_watchpoint,
NULL, /* siginfo_fixup */
arm_new_process,
arm_new_thread,
+ arm_new_fork,
arm_prepare_to_resume,
};
init_registers_arm_with_iwmmxt ();
init_registers_arm_with_vfpv2 ();
init_registers_arm_with_vfpv3 ();
- init_registers_arm_with_neon ();
+
+ initialize_low_arch_aarch32 ();
initialize_regsets_info (&arm_regsets_info);
}