/* Native-dependent code for GNU/Linux AArch64.
- Copyright (C) 2011-2013 Free Software Foundation, Inc.
+ Copyright (C) 2011-2015 Free Software Foundation, Inc.
Contributed by ARM Ltd.
This file is part of GDB.
#include <sys/ptrace.h>
#include <sys/utsname.h>
+#include <asm/ptrace.h>
#include "gregset.h"
static int
get_thread_id (ptid_t ptid)
{
- int tid = TIDGET (ptid);
+ int tid = ptid_get_lwp (ptid);
if (0 == tid)
- tid = PIDGET (ptid);
+ tid = ptid_get_pid (ptid);
return tid;
}
static int aarch64_num_bp_regs;
static int aarch64_num_wp_regs;
-/* Debugging of hardware breakpoint/watchpoint support. */
-
-static int debug_hw_points;
-
/* Each bit of a variable of this type is used to indicate whether a
hardware breakpoint or watchpoint setting has been changed since
the last update.
ptrace calls to the kernel, i.e. avoid asking the kernel to write
to the debug registers with unchanged values. */
-typedef unsigned long long dr_changed_t;
+typedef ULONGEST dr_changed_t;
/* Set each of the lower M bits of X to 1; assert X is wide enough. */
unsigned int dr_ref_count_wp[AARCH64_HWP_MAX_NUM];
};
-/* Clear the reference counts and forget everything we knew about the
- debug registers. */
+/* Per-process data. We don't bind this to a per-inferior registry
+ because of targets like x86 GNU/Linux that need to keep track of
+ processes that aren't bound to any inferior (e.g., fork children,
+ checkpoints). */
-static void
-aarch64_init_debug_reg_state (struct aarch64_debug_reg_state *state)
+struct aarch64_process_info
{
- int i;
+ /* Linked list. */
+ struct aarch64_process_info *next;
- for (i = 0; i < AARCH64_HBP_MAX_NUM; ++i)
- {
- state->dr_addr_bp[i] = 0;
- state->dr_ctrl_bp[i] = 0;
- state->dr_ref_count_bp[i] = 0;
- }
+ /* The process identifier. */
+ pid_t pid;
- for (i = 0; i < AARCH64_HWP_MAX_NUM; ++i)
- {
- state->dr_addr_wp[i] = 0;
- state->dr_ctrl_wp[i] = 0;
- state->dr_ref_count_wp[i] = 0;
- }
+ /* Copy of aarch64 hardware debug registers. */
+ struct aarch64_debug_reg_state state;
+};
+
+static struct aarch64_process_info *aarch64_process_list = NULL;
+
+/* Find process data for process PID. */
+
+static struct aarch64_process_info *
+aarch64_find_process_pid (pid_t pid)
+{
+ struct aarch64_process_info *proc;
+
+ for (proc = aarch64_process_list; proc; proc = proc->next)
+ if (proc->pid == pid)
+ return proc;
+
+ return NULL;
}
-/* Per-inferior data key. */
-static const struct inferior_data *aarch64_inferior_data;
+/* Add process data for process PID. Returns newly allocated info
+ object. */
-/* Per-inferior data. */
-struct aarch64_inferior_data
+static struct aarch64_process_info *
+aarch64_add_process (pid_t pid)
{
- /* Copy of AArch64 hardware debug registers for performance reasons. */
- struct aarch64_debug_reg_state state;
-};
+ struct aarch64_process_info *proc;
-/* Per-inferior hook for register_inferior_data_with_cleanup. */
+ proc = xcalloc (1, sizeof (*proc));
+ proc->pid = pid;
-static void
-aarch64_inferior_data_cleanup (struct inferior *inf, void *arg)
+ proc->next = aarch64_process_list;
+ aarch64_process_list = proc;
+
+ return proc;
+}
+
+/* Get data specific info for process PID, creating it if necessary.
+ Never returns NULL. */
+
+static struct aarch64_process_info *
+aarch64_process_info_get (pid_t pid)
{
- struct aarch64_inferior_data *inf_data = arg;
+ struct aarch64_process_info *proc;
+
+ proc = aarch64_find_process_pid (pid);
+ if (proc == NULL)
+ proc = aarch64_add_process (pid);
- xfree (inf_data);
+ return proc;
}
-/* Get data specific for INFERIOR_PTID LWP. Return special data area
- for processes being detached. */
+/* Called whenever GDB is no longer debugging process PID. It deletes
+ data structures that keep track of debug register state. */
-static struct aarch64_inferior_data *
-aarch64_inferior_data_get (void)
+static void
+aarch64_forget_process (pid_t pid)
{
- struct inferior *inf = current_inferior ();
- struct aarch64_inferior_data *inf_data;
+ struct aarch64_process_info *proc, **proc_link;
+
+ proc = aarch64_process_list;
+ proc_link = &aarch64_process_list;
- inf_data = inferior_data (inf, aarch64_inferior_data);
- if (inf_data == NULL)
+ while (proc != NULL)
{
- inf_data = xzalloc (sizeof (*inf_data));
- set_inferior_data (inf, aarch64_inferior_data, inf_data);
- }
+ if (proc->pid == pid)
+ {
+ *proc_link = proc->next;
- return inf_data;
+ xfree (proc);
+ return;
+ }
+
+ proc_link = &proc->next;
+ proc = *proc_link;
+ }
}
-/* Get debug registers state for INFERIOR_PTID, see
- aarch64_inferior_data_get. */
+/* Get debug registers state for process PID. */
static struct aarch64_debug_reg_state *
-aarch64_get_debug_reg_state (void)
+aarch64_get_debug_reg_state (pid_t pid)
{
- return &aarch64_inferior_data_get ()->state;
+ return &aarch64_process_info_get (pid)->state;
}
/* Per-thread arch-specific data we want to keep. */
const CORE_ADDR *addr;
const unsigned int *ctrl;
+ memset (®s, 0, sizeof (regs));
iov.iov_base = ®s;
- iov.iov_len = sizeof (regs);
count = watchpoint ? aarch64_num_wp_regs : aarch64_num_bp_regs;
addr = watchpoint ? state->dr_addr_wp : state->dr_addr_bp;
ctrl = watchpoint ? state->dr_ctrl_wp : state->dr_ctrl_bp;
+ if (count == 0)
+ return;
+ iov.iov_len = (offsetof (struct user_hwdebug_state, dbg_regs[count - 1])
+ + sizeof (regs.dbg_regs [count - 1]));
for (i = 0; i < count; i++)
{
unsigned int idx;
};
-/* Callback for linux_nat_iterate_watchpoint_lwps. Records the
+/* Callback for iterate_over_lwps. Records the
information about the change of one hardware breakpoint/watchpoint
setting for the thread LWP.
The information is passed in via PTR.
if (info == NULL)
info = lwp->arch_private = XCNEW (struct arch_lwp_info);
- if (debug_hw_points)
+ if (show_debug_regs)
{
fprintf_unfiltered (gdb_stdlog,
"debug_reg_change_callback: \n\tOn entry:\n");
fprintf_unfiltered (gdb_stdlog,
- "\tpid%d, dr_changed_bp=0x%llx, "
- "dr_changed_wp=0x%llx\n",
- pid, info->dr_changed_bp, info->dr_changed_wp);
+ "\tpid%d, dr_changed_bp=0x%s, "
+ "dr_changed_wp=0x%s\n",
+ pid, phex (info->dr_changed_bp, 8),
+ phex (info->dr_changed_wp, 8));
}
dr_changed_ptr = is_watchpoint ? &info->dr_changed_wp
if (!lwp->stopped)
linux_stop_lwp (lwp);
- if (debug_hw_points)
+ if (show_debug_regs)
{
fprintf_unfiltered (gdb_stdlog,
- "\tOn exit:\n\tpid%d, dr_changed_bp=0x%llx, "
- "dr_changed_wp=0x%llx\n",
- pid, info->dr_changed_bp, info->dr_changed_wp);
+ "\tOn exit:\n\tpid%d, dr_changed_bp=0x%s, "
+ "dr_changed_wp=0x%s\n",
+ pid, phex (info->dr_changed_bp, 8),
+ phex (info->dr_changed_wp, 8));
}
/* Continue the iteration. */
int is_watchpoint, unsigned int idx)
{
struct aarch64_dr_update_callback_param param;
+ ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
param.is_watchpoint = is_watchpoint;
param.idx = idx;
- linux_nat_iterate_watchpoint_lwps (debug_reg_change_callback,
- (void *) ¶m);
+ iterate_over_lwps (pid_ptid, debug_reg_change_callback, (void *) ¶m);
}
/* Print the values of the cached breakpoint/watchpoint registers. */
fill_gregset (const struct regcache *regcache,
gdb_gregset_t *gregsetp, int regno)
{
- gdb_byte *gregs_buf = (gdb_byte *) gregsetp;
- int i;
-
- for (i = AARCH64_X0_REGNUM; i <= AARCH64_CPSR_REGNUM; i++)
- if (regno == -1 || regno == i)
- regcache_raw_collect (regcache, i,
- gregs_buf + X_REGISTER_SIZE
- * (i - AARCH64_X0_REGNUM));
+ regcache_collect_regset (&aarch64_linux_gregset, regcache,
+ regno, (gdb_byte *) gregsetp,
+ AARCH64_LINUX_SIZEOF_GREGSET);
}
/* Fill GDB's register array with the general-purpose register values
void
supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp)
{
- aarch64_linux_supply_gregset (regcache, (const gdb_byte *) gregsetp);
+ regcache_supply_regset (&aarch64_linux_gregset, regcache, -1,
+ (const gdb_byte *) gregsetp,
+ AARCH64_LINUX_SIZEOF_GREGSET);
}
/* Fill register REGNO (if it is a floating-point register) in
fill_fpregset (const struct regcache *regcache,
gdb_fpregset_t *fpregsetp, int regno)
{
- gdb_byte *fpregs_buf = (gdb_byte *) fpregsetp;
- int i;
-
- for (i = AARCH64_V0_REGNUM; i <= AARCH64_V31_REGNUM; i++)
- if (regno == -1 || regno == i)
- regcache_raw_collect (regcache, i,
- fpregs_buf + V_REGISTER_SIZE
- * (i - AARCH64_V0_REGNUM));
-
- if (regno == -1 || regno == AARCH64_FPSR_REGNUM)
- regcache_raw_collect (regcache, AARCH64_FPSR_REGNUM,
- fpregs_buf + V_REGISTER_SIZE * 32);
-
- if (regno == -1 || regno == AARCH64_FPCR_REGNUM)
- regcache_raw_collect (regcache, AARCH64_FPCR_REGNUM,
- fpregs_buf + V_REGISTER_SIZE * 32 + 4);
+ regcache_collect_regset (&aarch64_linux_fpregset, regcache,
+ regno, (gdb_byte *) fpregsetp,
+ AARCH64_LINUX_SIZEOF_FPREGSET);
}
/* Fill GDB's register array with the floating-point register values
void
supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregsetp)
{
- aarch64_linux_supply_fpregset (regcache, (const gdb_byte *) fpregsetp);
+ regcache_supply_regset (&aarch64_linux_fpregset, regcache, -1,
+ (const gdb_byte *) fpregsetp,
+ AARCH64_LINUX_SIZEOF_FPREGSET);
}
/* Called when resuming a thread.
if (DR_HAS_CHANGED (info->dr_changed_bp)
|| DR_HAS_CHANGED (info->dr_changed_wp))
{
- int tid = GET_LWP (lwp->ptid);
- struct aarch64_debug_reg_state *state = aarch64_get_debug_reg_state ();
+ int tid = ptid_get_lwp (lwp->ptid);
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (lwp->ptid));
- if (debug_hw_points)
+ if (show_debug_regs)
fprintf_unfiltered (gdb_stdlog, "prepare_to_resume thread %d\n", tid);
/* Watchpoints. */
lp->arch_private = info;
}
+
+/* linux_nat_new_fork hook. */
+
+static void
+aarch64_linux_new_fork (struct lwp_info *parent, pid_t child_pid)
+{
+ pid_t parent_pid;
+ struct aarch64_debug_reg_state *parent_state;
+ struct aarch64_debug_reg_state *child_state;
+
+ /* NULL means no watchpoint has ever been set in the parent. In
+ that case, there's nothing to do. */
+ if (parent->arch_private == NULL)
+ return;
+
+ /* 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. */
+
+ parent_pid = ptid_get_pid (parent->ptid);
+ parent_state = aarch64_get_debug_reg_state (parent_pid);
+ child_state = aarch64_get_debug_reg_state (child_pid);
+ *child_state = *parent_state;
+}
\f
/* Called by libthread_db. Returns a pointer to the thread local
aarch64_num_wp_regs = AARCH64_DEBUG_NUM_SLOTS (dreg_state.dbg_info);
if (aarch64_num_wp_regs > AARCH64_HWP_MAX_NUM)
{
- warning ("Unexpected number of hardware watchpoint registers reported"
- " by ptrace, got %d, expected %d.",
+ warning (_("Unexpected number of hardware watchpoint registers"
+ " reported by ptrace, got %d, expected %d."),
aarch64_num_wp_regs, AARCH64_HWP_MAX_NUM);
aarch64_num_wp_regs = AARCH64_HWP_MAX_NUM;
}
}
else
{
- warning ("Unable to determine the number of hardware watchpoints"
- " available.");
+ warning (_("Unable to determine the number of hardware watchpoints"
+ " available."));
aarch64_num_wp_regs = 0;
}
aarch64_num_bp_regs = AARCH64_DEBUG_NUM_SLOTS (dreg_state.dbg_info);
if (aarch64_num_bp_regs > AARCH64_HBP_MAX_NUM)
{
- warning ("Unexpected number of hardware breakpoint registers reported"
- " by ptrace, got %d, expected %d.",
+ warning (_("Unexpected number of hardware breakpoint registers"
+ " reported by ptrace, got %d, expected %d."),
aarch64_num_bp_regs, AARCH64_HBP_MAX_NUM);
aarch64_num_bp_regs = AARCH64_HBP_MAX_NUM;
}
}
else
{
- warning ("Unable to determine the number of hardware breakpoints"
- " available.");
+ warning (_("Unable to determine the number of hardware breakpoints"
+ " available."));
aarch64_num_bp_regs = 0;
}
}
-static void (*super_post_startup_inferior) (ptid_t ptid);
+static void (*super_post_startup_inferior) (struct target_ops *self,
+ ptid_t ptid);
/* Implement the "to_post_startup_inferior" target_ops method. */
static void
-aarch64_linux_child_post_startup_inferior (ptid_t ptid)
+aarch64_linux_child_post_startup_inferior (struct target_ops *self,
+ ptid_t ptid)
{
- struct aarch64_debug_reg_state *state = aarch64_get_debug_reg_state ();
-
- aarch64_init_debug_reg_state (state);
+ aarch64_forget_process (ptid_get_pid (ptid));
aarch64_linux_get_debug_reg_capacity ();
- super_post_startup_inferior (ptid);
+ super_post_startup_inferior (self, ptid);
}
/* Implement the "to_read_description" target_ops method. */
sharing implemented via reference counts. */
static int
-aarch64_linux_can_use_hw_breakpoint (int type, int cnt, int othertype)
+aarch64_linux_can_use_hw_breakpoint (struct target_ops *self,
+ int type, int cnt, int othertype)
{
return 1;
}
if (!aarch64_point_is_aligned (0 /* is_watchpoint */ , addr, len))
return -1;
- state = aarch64_get_debug_reg_state ();
+ state = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
if (is_insert)
return aarch64_dr_state_insert_one_point (state, type, addr, len);
return aarch64_dr_state_remove_one_point (state, type, addr, len);
}
-/* Insert a hardware-assisted breakpoint at BP_TGT->placed_address.
+/* Insert a hardware-assisted breakpoint at BP_TGT->reqstd_address.
Return 0 on success, -1 on failure. */
static int
-aarch64_linux_insert_hw_breakpoint (struct gdbarch *gdbarch,
+aarch64_linux_insert_hw_breakpoint (struct target_ops *self,
+ struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt)
{
int ret;
- CORE_ADDR addr = bp_tgt->placed_address;
+ CORE_ADDR addr = bp_tgt->placed_address = bp_tgt->reqstd_address;
const int len = 4;
const int type = hw_execute;
- if (debug_hw_points)
+ if (show_debug_regs)
fprintf_unfiltered
(gdb_stdlog,
"insert_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
ret = aarch64_handle_breakpoint (type, addr, len, 1 /* is_insert */);
- if (debug_hw_points > 1)
- aarch64_show_debug_reg_state (aarch64_get_debug_reg_state (),
- "insert_hw_watchpoint", addr, len, type);
+ if (show_debug_regs)
+ {
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
+
+ aarch64_show_debug_reg_state (state,
+ "insert_hw_watchpoint", addr, len, type);
+ }
return ret;
}
Return 0 on success, -1 on failure. */
static int
-aarch64_linux_remove_hw_breakpoint (struct gdbarch *gdbarch,
+aarch64_linux_remove_hw_breakpoint (struct target_ops *self,
+ struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt)
{
int ret;
const int len = 4;
const int type = hw_execute;
- if (debug_hw_points)
+ if (show_debug_regs)
fprintf_unfiltered
(gdb_stdlog, "remove_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
(unsigned long) addr, len);
ret = aarch64_handle_breakpoint (type, addr, len, 0 /* is_insert */);
- if (debug_hw_points > 1)
- aarch64_show_debug_reg_state (aarch64_get_debug_reg_state (),
- "remove_hw_watchpoint", addr, len, type);
+ if (show_debug_regs)
+ {
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
+
+ aarch64_show_debug_reg_state (state,
+ "remove_hw_watchpoint", addr, len, type);
+ }
return ret;
}
aarch64_handle_aligned_watchpoint (int type, CORE_ADDR addr, int len,
int is_insert)
{
- struct aarch64_debug_reg_state *state = aarch64_get_debug_reg_state ();
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
if (is_insert)
return aarch64_dr_state_insert_one_point (state, type, addr, len);
aarch64_handle_unaligned_watchpoint (int type, CORE_ADDR addr, int len,
int is_insert)
{
- struct aarch64_debug_reg_state *state = aarch64_get_debug_reg_state ();
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
while (len > 0)
{
ret = aarch64_dr_state_remove_one_point (state, type, aligned_addr,
aligned_len);
- if (debug_hw_points)
+ if (show_debug_regs)
fprintf_unfiltered (gdb_stdlog,
"handle_unaligned_watchpoint: is_insert: %d\n"
" aligned_addr: 0x%08lx, aligned_len: %d\n"
of the type TYPE. Return 0 on success, -1 on failure. */
static int
-aarch64_linux_insert_watchpoint (CORE_ADDR addr, int len, int type,
+aarch64_linux_insert_watchpoint (struct target_ops *self,
+ CORE_ADDR addr, int len, int type,
struct expression *cond)
{
int ret;
- if (debug_hw_points)
+ if (show_debug_regs)
fprintf_unfiltered (gdb_stdlog,
"insert_watchpoint on entry (addr=0x%08lx, len=%d)\n",
(unsigned long) addr, len);
ret = aarch64_handle_watchpoint (type, addr, len, 1 /* is_insert */);
- if (debug_hw_points > 1)
- aarch64_show_debug_reg_state (aarch64_get_debug_reg_state (),
- "insert_watchpoint", addr, len, type);
+ if (show_debug_regs)
+ {
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
+
+ aarch64_show_debug_reg_state (state,
+ "insert_watchpoint", addr, len, type);
+ }
return ret;
}
type TYPE. Return 0 on success, -1 on failure. */
static int
-aarch64_linux_remove_watchpoint (CORE_ADDR addr, int len, int type,
+aarch64_linux_remove_watchpoint (struct target_ops *self,
+ CORE_ADDR addr, int len, int type,
struct expression *cond)
{
int ret;
- if (debug_hw_points)
+ if (show_debug_regs)
fprintf_unfiltered (gdb_stdlog,
"remove_watchpoint on entry (addr=0x%08lx, len=%d)\n",
(unsigned long) addr, len);
ret = aarch64_handle_watchpoint (type, addr, len, 0 /* is_insert */);
- if (debug_hw_points > 1)
- aarch64_show_debug_reg_state (aarch64_get_debug_reg_state (),
- "remove_watchpoint", addr, len, type);
+ if (show_debug_regs)
+ {
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
+
+ aarch64_show_debug_reg_state (state,
+ "remove_watchpoint", addr, len, type);
+ }
return ret;
}
/* Implement the "to_region_ok_for_hw_watchpoint" target_ops method. */
static int
-aarch64_linux_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
+aarch64_linux_region_ok_for_hw_watchpoint (struct target_ops *self,
+ CORE_ADDR addr, int len)
{
CORE_ADDR aligned_addr;
return 0;
/* Check if the address matches any watched address. */
- state = aarch64_get_debug_reg_state ();
+ state = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
{
const unsigned int len = aarch64_watchpoint_length (state->dr_ctrl_wp[i]);
/* Implement the "to_stopped_by_watchpoint" target_ops method. */
static int
-aarch64_linux_stopped_by_watchpoint (void)
+aarch64_linux_stopped_by_watchpoint (struct target_ops *ops)
{
CORE_ADDR addr;
- return aarch64_linux_stopped_data_address (¤t_target, &addr);
+ return aarch64_linux_stopped_data_address (ops, &addr);
}
/* Implement the "to_watchpoint_addr_within_range" target_ops method. */
/* A maintenance command to enable printing the internal DRi mirror
variables. */
add_setshow_boolean_cmd ("show-debug-regs", class_maintenance,
- &debug_hw_points, _("\
+ &show_debug_regs, _("\
Set whether to show variables that mirror the AArch64 debug registers."), _("\
Show whether to show variables that mirror the AArch64 debug registers."), _("\
Use \"on\" to enable, \"off\" to disable.\n\
t->to_stopped_data_address = aarch64_linux_stopped_data_address;
t->to_watchpoint_addr_within_range =
aarch64_linux_watchpoint_addr_within_range;
- if (aarch64_inferior_data == NULL)
- aarch64_inferior_data
- = register_inferior_data_with_cleanup (NULL,
- aarch64_inferior_data_cleanup);
/* Override the GNU/Linux inferior startup hook. */
super_post_startup_inferior = t->to_post_startup_inferior;
/* Register the target. */
linux_nat_add_target (t);
linux_nat_set_new_thread (t, aarch64_linux_new_thread);
+ linux_nat_set_new_fork (t, aarch64_linux_new_fork);
+ linux_nat_set_forget_process (t, aarch64_forget_process);
linux_nat_set_prepare_to_resume (t, aarch64_linux_prepare_to_resume);
}