mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 03:51:15 +08:00
Add support for hardware breakpoints/watchpoints on FreeBSD/Aarch64.
This shares aarch64-nat.c and nat/aarch64-hw-point.c with the Linux native target. Since FreeBSD writes all of the debug registers in one ptrace op, use an unordered_set<> to track the "dirty" state for threads rather than bitmasks of modified registers.
This commit is contained in:
parent
a3627b5428
commit
065a00b3a4
2
gdb/NEWS
2
gdb/NEWS
@ -3,6 +3,8 @@
|
||||
|
||||
*** Changes since GDB 12
|
||||
|
||||
* GDB now supports hardware watchpoints on FreeBSD/Aarch64.
|
||||
|
||||
* Python API
|
||||
|
||||
** New function gdb.format_address(ADDRESS, PROGSPACE, ARCHITECTURE),
|
||||
|
@ -18,24 +18,60 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "defs.h"
|
||||
#include "arch-utils.h"
|
||||
#include "inferior.h"
|
||||
#include "regcache.h"
|
||||
#include "target.h"
|
||||
#include "nat/aarch64-hw-point.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <machine/armreg.h>
|
||||
#include <machine/reg.h>
|
||||
|
||||
#include "fbsd-nat.h"
|
||||
#include "aarch64-fbsd-tdep.h"
|
||||
#include "aarch64-nat.h"
|
||||
#include "inf-ptrace.h"
|
||||
|
||||
#if __FreeBSD_version >= 1400005
|
||||
#define HAVE_DBREG
|
||||
|
||||
#include <unordered_set>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DBREG
|
||||
struct aarch64_fbsd_nat_target final
|
||||
: public aarch64_nat_target<fbsd_nat_target>
|
||||
#else
|
||||
struct aarch64_fbsd_nat_target final : public fbsd_nat_target
|
||||
#endif
|
||||
{
|
||||
void fetch_registers (struct regcache *, int) override;
|
||||
void store_registers (struct regcache *, int) override;
|
||||
|
||||
#ifdef HAVE_DBREG
|
||||
/* Hardware breakpoints and watchpoints. */
|
||||
bool stopped_by_watchpoint () override;
|
||||
bool stopped_data_address (CORE_ADDR *) override;
|
||||
bool stopped_by_hw_breakpoint () override;
|
||||
bool supports_stopped_by_hw_breakpoint () override;
|
||||
|
||||
void post_startup_inferior (ptid_t) override;
|
||||
void post_attach (int pid) override;
|
||||
|
||||
void low_new_fork (ptid_t parent, pid_t child) override;
|
||||
void low_delete_thread (thread_info *) override;
|
||||
void low_prepare_to_resume (thread_info *) override;
|
||||
|
||||
private:
|
||||
void probe_debug_regs (int pid);
|
||||
static bool debug_regs_probed;
|
||||
#endif
|
||||
};
|
||||
|
||||
static aarch64_fbsd_nat_target the_aarch64_fbsd_nat_target;
|
||||
bool aarch64_fbsd_nat_target::debug_regs_probed;
|
||||
|
||||
/* Fetch register REGNUM from the inferior. If REGNUM is -1, do this
|
||||
for all registers. */
|
||||
@ -63,9 +99,231 @@ aarch64_fbsd_nat_target::store_registers (struct regcache *regcache,
|
||||
PT_SETFPREGS, &aarch64_fbsd_fpregset);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DBREG
|
||||
/* Set of threads which need to update debug registers on next resume. */
|
||||
|
||||
static std::unordered_set<lwpid_t> aarch64_debug_pending_threads;
|
||||
|
||||
/* Implement the "stopped_data_address" target_ops method. */
|
||||
|
||||
bool
|
||||
aarch64_fbsd_nat_target::stopped_data_address (CORE_ADDR *addr_p)
|
||||
{
|
||||
siginfo_t siginfo;
|
||||
struct aarch64_debug_reg_state *state;
|
||||
|
||||
if (!fbsd_nat_get_siginfo (inferior_ptid, &siginfo))
|
||||
return false;
|
||||
|
||||
/* This must be a hardware breakpoint. */
|
||||
if (siginfo.si_signo != SIGTRAP
|
||||
|| siginfo.si_code != TRAP_TRACE
|
||||
|| siginfo.si_trapno != EXCP_WATCHPT_EL0)
|
||||
return false;
|
||||
|
||||
const CORE_ADDR addr_trap = (CORE_ADDR) siginfo.si_addr;
|
||||
|
||||
/* Check if the address matches any watched address. */
|
||||
state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
|
||||
return aarch64_stopped_data_address (state, addr_trap, addr_p);
|
||||
}
|
||||
|
||||
/* Implement the "stopped_by_watchpoint" target_ops method. */
|
||||
|
||||
bool
|
||||
aarch64_fbsd_nat_target::stopped_by_watchpoint ()
|
||||
{
|
||||
CORE_ADDR addr;
|
||||
|
||||
return stopped_data_address (&addr);
|
||||
}
|
||||
|
||||
/* Implement the "stopped_by_hw_breakpoint" target_ops method. */
|
||||
|
||||
bool
|
||||
aarch64_fbsd_nat_target::stopped_by_hw_breakpoint ()
|
||||
{
|
||||
siginfo_t siginfo;
|
||||
struct aarch64_debug_reg_state *state;
|
||||
|
||||
if (!fbsd_nat_get_siginfo (inferior_ptid, &siginfo))
|
||||
return false;
|
||||
|
||||
/* This must be a hardware breakpoint. */
|
||||
if (siginfo.si_signo != SIGTRAP
|
||||
|| siginfo.si_code != TRAP_TRACE
|
||||
|| siginfo.si_trapno != EXCP_WATCHPT_EL0)
|
||||
return false;
|
||||
|
||||
return !stopped_by_watchpoint();
|
||||
}
|
||||
|
||||
/* Implement the "supports_stopped_by_hw_breakpoint" target_ops method. */
|
||||
|
||||
bool
|
||||
aarch64_fbsd_nat_target::supports_stopped_by_hw_breakpoint ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Fetch the hardware debug register capability information. */
|
||||
|
||||
void
|
||||
aarch64_fbsd_nat_target::probe_debug_regs (int pid)
|
||||
{
|
||||
if (!debug_regs_probed)
|
||||
{
|
||||
struct dbreg reg;
|
||||
|
||||
debug_regs_probed = true;
|
||||
aarch64_num_bp_regs = 0;
|
||||
aarch64_num_wp_regs = 0;
|
||||
|
||||
if (ptrace(PT_GETDBREGS, pid, (PTRACE_TYPE_ARG3) ®, 0) == 0)
|
||||
{
|
||||
switch (reg.db_debug_ver)
|
||||
{
|
||||
case AARCH64_DEBUG_ARCH_V8:
|
||||
case AARCH64_DEBUG_ARCH_V8_1:
|
||||
case AARCH64_DEBUG_ARCH_V8_2:
|
||||
case AARCH64_DEBUG_ARCH_V8_4:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
aarch64_num_bp_regs = reg.db_nbkpts;
|
||||
if (aarch64_num_bp_regs > AARCH64_HBP_MAX_NUM)
|
||||
{
|
||||
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;
|
||||
}
|
||||
aarch64_num_wp_regs = reg.db_nwtpts;
|
||||
if (aarch64_num_wp_regs > AARCH64_HWP_MAX_NUM)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Implement the virtual inf_ptrace_target::post_startup_inferior method. */
|
||||
|
||||
void
|
||||
aarch64_fbsd_nat_target::post_startup_inferior (ptid_t ptid)
|
||||
{
|
||||
aarch64_remove_debug_reg_state (ptid.pid ());
|
||||
probe_debug_regs (ptid.pid ());
|
||||
fbsd_nat_target::post_startup_inferior (ptid);
|
||||
}
|
||||
|
||||
/* Implement the "post_attach" target_ops method. */
|
||||
|
||||
void
|
||||
aarch64_fbsd_nat_target::post_attach (int pid)
|
||||
{
|
||||
aarch64_remove_debug_reg_state (pid);
|
||||
probe_debug_regs (pid);
|
||||
fbsd_nat_target::post_attach (pid);
|
||||
}
|
||||
|
||||
/* Implement the virtual fbsd_nat_target::low_new_fork method. */
|
||||
|
||||
void
|
||||
aarch64_fbsd_nat_target::low_new_fork (ptid_t parent, pid_t child)
|
||||
{
|
||||
struct aarch64_debug_reg_state *parent_state, *child_state;
|
||||
|
||||
/* If there is no parent state, no watchpoints nor breakpoints have
|
||||
been set, so there is nothing to do. */
|
||||
parent_state = aarch64_lookup_debug_reg_state (parent.pid ());
|
||||
if (parent_state == nullptr)
|
||||
return;
|
||||
|
||||
/* The kernel clears debug registers in the new child process after
|
||||
fork, but 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. */
|
||||
|
||||
child_state = aarch64_get_debug_reg_state (child);
|
||||
*child_state = *parent_state;
|
||||
}
|
||||
|
||||
/* Mark debug register state "dirty" for all threads belonging to the
|
||||
current inferior. */
|
||||
|
||||
void
|
||||
aarch64_notify_debug_reg_change (ptid_t ptid,
|
||||
int is_watchpoint, unsigned int idx)
|
||||
{
|
||||
for (thread_info *tp : current_inferior ()->non_exited_threads ())
|
||||
{
|
||||
if (tp->ptid.lwp_p ())
|
||||
aarch64_debug_pending_threads.emplace (tp->ptid.lwp ());
|
||||
}
|
||||
}
|
||||
|
||||
/* Implement the virtual fbsd_nat_target::low_delete_thread method. */
|
||||
|
||||
void
|
||||
aarch64_fbsd_nat_target::low_delete_thread (thread_info *tp)
|
||||
{
|
||||
gdb_assert(tp->ptid.lwp_p ());
|
||||
aarch64_debug_pending_threads.erase (tp->ptid.lwp ());
|
||||
}
|
||||
|
||||
/* Implement the virtual fbsd_nat_target::low_prepare_to_resume method. */
|
||||
|
||||
void
|
||||
aarch64_fbsd_nat_target::low_prepare_to_resume (thread_info *tp)
|
||||
{
|
||||
gdb_assert(tp->ptid.lwp_p ());
|
||||
|
||||
if (aarch64_debug_pending_threads.erase (tp->ptid.lwp ()) == 0)
|
||||
return;
|
||||
|
||||
struct aarch64_debug_reg_state *state =
|
||||
aarch64_lookup_debug_reg_state (tp->ptid.pid ());
|
||||
gdb_assert(state != nullptr);
|
||||
|
||||
struct dbreg reg;
|
||||
memset (®, 0, sizeof(reg));
|
||||
for (int i = 0; i < aarch64_num_bp_regs; i++)
|
||||
{
|
||||
reg.db_breakregs[i].dbr_addr = state->dr_addr_bp[i];
|
||||
reg.db_breakregs[i].dbr_ctrl = state->dr_ctrl_bp[i];
|
||||
}
|
||||
for (int i = 0; i < aarch64_num_wp_regs; i++)
|
||||
{
|
||||
reg.db_watchregs[i].dbw_addr = state->dr_addr_wp[i];
|
||||
reg.db_watchregs[i].dbw_ctrl = state->dr_ctrl_wp[i];
|
||||
}
|
||||
if (ptrace(PT_SETDBREGS, tp->ptid.lwp (), (PTRACE_TYPE_ARG3) ®, 0) != 0)
|
||||
error (_("Failed to set hardware debug registers"));
|
||||
}
|
||||
#else
|
||||
/* A stub that should never be called. */
|
||||
void
|
||||
aarch64_notify_debug_reg_change (ptid_t ptid,
|
||||
int is_watchpoint, unsigned int idx)
|
||||
{
|
||||
gdb_assert (true);
|
||||
}
|
||||
#endif
|
||||
|
||||
void _initialize_aarch64_fbsd_nat ();
|
||||
void
|
||||
_initialize_aarch64_fbsd_nat ()
|
||||
{
|
||||
#ifdef HAVE_DBREG
|
||||
aarch64_initialize_hw_point ();
|
||||
#endif
|
||||
add_inf_child_target (&the_aarch64_fbsd_nat_target);
|
||||
}
|
||||
|
@ -154,7 +154,8 @@ case ${gdb_host} in
|
||||
case ${gdb_host_cpu} in
|
||||
aarch64)
|
||||
# Host: FreeBSD/aarch64
|
||||
NATDEPFILES="${NATDEPFILES} aarch64-fbsd-nat.o"
|
||||
NATDEPFILES="${NATDEPFILES} aarch64-nat.o \
|
||||
nat/aarch64-hw-point.o aarch64-fbsd-nat.o"
|
||||
LOADLIBES=
|
||||
;;
|
||||
arm)
|
||||
|
Loading…
Reference in New Issue
Block a user