/* GNU/Linux/ARM specific low level interface, for the remote server for GDB.
- Copyright (C) 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
- 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+ Copyright (C) 1995-2015 Free Software Foundation, Inc.
This file is part of GDB.
/* Defined in auto-generated files. */
void init_registers_arm (void);
+extern const struct target_desc *tdesc_arm;
+
void init_registers_arm_with_iwmmxt (void);
+extern const struct target_desc *tdesc_arm_with_iwmmxt;
+
void init_registers_arm_with_vfpv2 (void);
+extern const struct target_desc *tdesc_arm_with_vfpv2;
+
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
/* Information describing the hardware breakpoint capabilities. */
-struct arm_linux_hwbp_cap
+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
else
num = 16;
- base = find_regno ("d0");
+ base = find_regno (regcache->tdesc, "d0");
for (i = 0; i < num; i++)
collect_register (regcache, base + i, (char *) buf + i * 8);
else
num = 16;
- base = find_regno ("d0");
+ base = find_regno (regcache->tdesc, "d0");
for (i = 0; i < num; i++)
supply_register (regcache, base + i, (char *) buf + i * 8);
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;
}
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);
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;
}
-/* 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)
+/* 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)
{
- /* 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;
+ unsigned int val;
- if (available == -1)
- {
- int pid = lwpid_of (get_thread_lwp (current_inferior));
- unsigned int val;
+ if (ptrace (PTRACE_GETHBPREGS, pid, 0, &val) < 0)
+ return;
- if (ptrace (PTRACE_GETHBPREGS, pid, 0, &val) < 0)
- available = 0;
- else
- {
- info.arch = (unsigned char)((val >> 24) & 0xff);
- info.max_wp_length = (unsigned char)((val >> 16) & 0xff);
- info.wp_count = (unsigned char)((val >> 8) & 0xff);
- info.bp_count = (unsigned char)(val & 0xff);
- available = (info.arch != 0);
- }
+ arm_linux_hwbp_cap.arch = (unsigned char)((val >> 24) & 0xff);
+ if (arm_linux_hwbp_cap.arch == 0)
+ return;
- if (available)
- {
- if (info.wp_count > MAX_WPTS)
- internal_error (__FILE__, __LINE__,
- "Unsupported number of watchpoints");
- if (info.bp_count > MAX_BPTS)
- internal_error (__FILE__, __LINE__,
- "Unsupported number of breakpoints");
- }
- }
+ 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);
- return available == 1 ? &info : NULL;
+ 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)
{
- const struct arm_linux_hwbp_cap *cap = arm_linux_get_hwbp_cap ();
- return cap != NULL ? cap->bp_count : 0;
+ return arm_linux_hwbp_cap.bp_count;
}
/* 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;
+ 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)
{
- const struct arm_linux_hwbp_cap *cap = arm_linux_get_hwbp_cap ();
- return cap != NULL ? cap->max_wp_length : 0;
+ return arm_linux_hwbp_cap.max_wp_length;
}
/* Initialize an ARM hardware break-/watch-point control register value.
return p1->address == p2->address && p1->control == p2->control;
}
+/* Convert a raw breakpoint type to an enum arm_hwbp_type. */
+
+static int
+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, 0 if TYPE represents a breakpoint,
- and 1 if type represents a watchpoint. */
+ 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)
{
{
case 2: /* 16-bit Thumb mode breakpoint */
case 3: /* 32-bit Thumb mode breakpoint */
- mask = 0x3 << (addr & 2);
+ mask = 0x3;
+ addr &= ~1;
break;
case 4: /* 32-bit ARM mode breakpoint */
mask = 0xf;
+ addr &= ~3;
break;
default:
/* Unsupported. */
- return -1;
+ return -2;
}
-
- addr &= ~3;
}
else
{
/* Can not set watchpoints for zero or negative lengths. */
if (len <= 0)
- return -1;
+ return -2;
/* The current ptrace interface can only handle watchpoints that are a
power of 2. */
if ((len & (len - 1)) != 0)
- return -1;
+ return -2;
/* 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;
+ return -2;
mask = (1 << len) - 1;
}
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_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 < 0)
{
/* Unsupported. */
- return 1;
+ return watch == -1 ? 1 : -1;
}
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 siginfo siginfo;
+ struct lwp_info *lwp = get_thread_lwp (current_thread);
+ siginfo_t siginfo;
/* We must be able to set hardware watchpoints. */
if (arm_linux_get_hw_watchpoint_count () == 0)
/* 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;
}
}
/* 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));
int i;
for (i = 0; i < MAX_WPTS; i++)
info->wpts_changed[i] = 1;
- return info;
+ lwp->arch_private = info;
}
/* 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;
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)
- error (_("Unexpected error setting breakpoint address"));
+ if (ptrace (PTRACE_SETHBPREGS, pid,
+ (PTRACE_TYPE_ARG3) ((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)
- error (_("Unexpected error setting breakpoint"));
+ if (ptrace (PTRACE_SETHBPREGS, pid,
+ (PTRACE_TYPE_ARG3) ((i << 1) + 2),
+ &proc_info->bpts[i].control) < 0)
+ perror_with_name ("Unexpected error setting breakpoint");
lwp_info->bpts_changed[i] = 0;
}
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)
- error (_("Unexpected error setting watchpoint address"));
+ if (ptrace (PTRACE_SETHBPREGS, pid,
+ (PTRACE_TYPE_ARG3) -((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)
- error (_("Unexpected error setting watchpoint"));
+ if (ptrace (PTRACE_SETHBPREGS, pid,
+ (PTRACE_TYPE_ARG3) -((i << 1) + 2),
+ &proc_info->wpts[i].control) < 0)
+ perror_with_name ("Unexpected error setting watchpoint");
lwp_info->wpts_changed[i] = 0;
}
return 0;
}
-static void
-arm_arch_setup (void)
+static const struct target_desc *
+arm_read_description (void)
{
+ int pid = lwpid_of (current_thread);
+
+ /* 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;
- }
+ return tdesc_arm;
if (arm_hwcap & HWCAP_IWMMXT)
- {
- init_registers_arm_with_iwmmxt ();
- return;
- }
+ return tdesc_arm_with_iwmmxt;
if (arm_hwcap & HWCAP_VFP)
{
- int pid;
+ const struct target_desc *result;
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 ();
+ result = tdesc_arm_with_neon;
else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
- init_registers_arm_with_vfpv3 ();
+ result = tdesc_arm_with_vfpv3;
else
- init_registers_arm_with_vfpv2 ();
+ result = tdesc_arm_with_vfpv2;
/* Now make sure that the kernel supports reading these
registers. Support was added in 2.6.30. */
- pid = lwpid_of (get_thread_lwp (current_inferior));
errno = 0;
buf = xmalloc (32 * 8 + 4);
if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0
&& errno == EIO)
{
arm_hwcap = 0;
- init_registers_arm ();
+ result = tdesc_arm;
}
free (buf);
- return;
+ return result;
}
/* The default configuration uses legacy FPA registers, probably
simulated. */
- init_registers_arm ();
+ return tdesc_arm;
}
-struct regset_info target_regsets[] = {
+static void
+arm_arch_setup (void)
+{
+ current_process ()->tdesc = arm_read_description ();
+}
+
+static struct regset_info arm_regsets[] = {
{ PTRACE_GETREGS, PTRACE_SETREGS, 0, 18 * 4,
GENERAL_REGS,
arm_fill_gregset, arm_store_gregset },
{ 0, 0, 0, -1, -1, NULL, NULL }
};
+static struct regsets_info arm_regsets_info =
+ {
+ arm_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct usrregs_info arm_usrregs_info =
+ {
+ arm_num_regs,
+ arm_regmap,
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ &arm_usrregs_info,
+ &arm_regsets_info
+ };
+
+static const struct regs_info *
+arm_regs_info (void)
+{
+ return ®s_info;
+}
+
struct linux_target_ops the_low_target = {
arm_arch_setup,
- arm_num_regs,
- arm_regmap,
+ arm_regs_info,
arm_cannot_fetch_register,
arm_cannot_store_register,
+ NULL, /* fetch_register */
arm_get_pc,
arm_set_pc,
arm_reinsert_addr,
0,
arm_breakpoint_at,
+ arm_supports_z_point_type,
arm_insert_point,
arm_remove_point,
arm_stopped_by_watchpoint,
arm_new_thread,
arm_prepare_to_resume,
};
+
+void
+initialize_low_arch (void)
+{
+ /* Initialize the Linux target descriptions. */
+ init_registers_arm ();
+ init_registers_arm_with_iwmmxt ();
+ init_registers_arm_with_vfpv2 ();
+ init_registers_arm_with_vfpv3 ();
+ init_registers_arm_with_neon ();
+
+ initialize_regsets_info (&arm_regsets_info);
+}