diff options
author | John Baldwin <jhb@FreeBSD.org> | 2022-03-22 12:05:43 -0700 |
---|---|---|
committer | John Baldwin <jhb@FreeBSD.org> | 2022-03-22 12:05:43 -0700 |
commit | 1570c37c340bb9df2db2c30b437d6c30e1d75459 (patch) | |
tree | bb521df0001a1d2e73327b8ca7f88f61f8711edf /gdb/aarch64-linux-nat.c | |
parent | 4bd817e71eefd659f51ec75bfb13109c486e8311 (diff) | |
download | gdb-1570c37c340bb9df2db2c30b437d6c30e1d75459.zip gdb-1570c37c340bb9df2db2c30b437d6c30e1d75459.tar.gz gdb-1570c37c340bb9df2db2c30b437d6c30e1d75459.tar.bz2 |
aarch64: Add an aarch64_nat_target mixin class.
This class includes platform-independent target methods for hardware
breakpoints and watchpoints using routines from
nat/aarch64-hw-point.c.
stopped_data_address is not platform-independent since the FAR
register holding the address for a breakpoint hit must be fetched in a
platform-specific manner. However, aarch64_stopped_data_address is
provided as a helper routine which performs platform-independent
validation given the value of the FAR register.
For tracking the per-process debug register mirror state, use an
unordered_map indexed by pid as recently adopted in x86-nat.c rather
than a manual linked-list.
Diffstat (limited to 'gdb/aarch64-linux-nat.c')
-rw-r--r-- | gdb/aarch64-linux-nat.c | 356 |
1 files changed, 6 insertions, 350 deletions
diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index dd072d9..7bb82d1 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -27,6 +27,7 @@ #include "target-descriptions.h" #include "auxv.h" #include "gdbcmd.h" +#include "aarch64-nat.h" #include "aarch64-tdep.h" #include "aarch64-linux-tdep.h" #include "aarch32-linux-nat.h" @@ -58,7 +59,8 @@ #define TRAP_HWBKPT 0x0004 #endif -class aarch64_linux_nat_target final : public linux_nat_target +class aarch64_linux_nat_target final + : public aarch64_nat_target<linux_nat_target> { public: /* Add our register access methods. */ @@ -68,17 +70,8 @@ public: const struct target_desc *read_description () override; /* Add our hardware breakpoint and watchpoint implementation. */ - int can_use_hw_breakpoint (enum bptype, int, int) override; - int insert_hw_breakpoint (struct gdbarch *, struct bp_target_info *) override; - int remove_hw_breakpoint (struct gdbarch *, struct bp_target_info *) override; - int region_ok_for_hw_watchpoint (CORE_ADDR, int) override; - int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type, - struct expression *) override; - int remove_watchpoint (CORE_ADDR, int, enum target_hw_bp_type, - struct expression *) override; bool stopped_by_watchpoint () override; bool stopped_data_address (CORE_ADDR *) override; - bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int) override; int can_do_single_step () override; @@ -118,103 +111,13 @@ public: static aarch64_linux_nat_target the_aarch64_linux_nat_target; -/* 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). */ - -struct aarch64_process_info -{ - /* Linked list. */ - struct aarch64_process_info *next; - - /* The process identifier. */ - pid_t pid; - - /* 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; -} - -/* Add process data for process PID. Returns newly allocated info - object. */ - -static struct aarch64_process_info * -aarch64_add_process (pid_t pid) -{ - struct aarch64_process_info *proc; - - proc = XCNEW (struct aarch64_process_info); - proc->pid = pid; - - 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_process_info *proc; - - proc = aarch64_find_process_pid (pid); - if (proc == NULL) - proc = aarch64_add_process (pid); - - return proc; -} - /* Called whenever GDB is no longer debugging process PID. It deletes data structures that keep track of debug register state. */ void aarch64_linux_nat_target::low_forget_process (pid_t pid) { - struct aarch64_process_info *proc, **proc_link; - - proc = aarch64_process_list; - proc_link = &aarch64_process_list; - - while (proc != NULL) - { - if (proc->pid == pid) - { - *proc_link = proc->next; - - xfree (proc); - return; - } - - proc_link = &proc->next; - proc = *proc_link; - } -} - -/* Get debug registers state for process PID. */ - -struct aarch64_debug_reg_state * -aarch64_get_debug_reg_state (pid_t pid) -{ - return &aarch64_process_info_get (pid)->state; + aarch64_remove_debug_reg_state (pid); } /* Fill GDB's register array with the general-purpose register values @@ -775,192 +678,12 @@ aarch64_linux_nat_target::low_siginfo_fixup (siginfo_t *native, gdb_byte *inf, return false; } -/* Returns the number of hardware watchpoints of type TYPE that we can - set. Value is positive if we can set CNT watchpoints, zero if - setting watchpoints of type TYPE is not supported, and negative if - CNT is more than the maximum number of watchpoints of type TYPE - that we can support. TYPE is one of bp_hardware_watchpoint, - bp_read_watchpoint, bp_write_watchpoint, or bp_hardware_breakpoint. - CNT is the number of such watchpoints used so far (including this - one). OTHERTYPE is non-zero if other types of watchpoints are - currently enabled. */ - -int -aarch64_linux_nat_target::can_use_hw_breakpoint (enum bptype type, - int cnt, int othertype) -{ - if (type == bp_hardware_watchpoint || type == bp_read_watchpoint - || type == bp_access_watchpoint || type == bp_watchpoint) - { - if (aarch64_num_wp_regs == 0) - return 0; - } - else if (type == bp_hardware_breakpoint) - { - if (aarch64_num_bp_regs == 0) - return 0; - } - else - gdb_assert_not_reached ("unexpected breakpoint type"); - - /* We always return 1 here because we don't have enough information - about possible overlap of addresses that they want to watch. As an - extreme example, consider the case where all the watchpoints watch - the same address and the same region length: then we can handle a - virtually unlimited number of watchpoints, due to debug register - sharing implemented via reference counts. */ - return 1; -} - -/* Insert a hardware-assisted breakpoint at BP_TGT->reqstd_address. - Return 0 on success, -1 on failure. */ - -int -aarch64_linux_nat_target::insert_hw_breakpoint (struct gdbarch *gdbarch, - struct bp_target_info *bp_tgt) -{ - int ret; - CORE_ADDR addr = bp_tgt->placed_address = bp_tgt->reqstd_address; - int len; - const enum target_hw_bp_type type = hw_execute; - struct aarch64_debug_reg_state *state - = aarch64_get_debug_reg_state (inferior_ptid.pid ()); - - gdbarch_breakpoint_from_pc (gdbarch, &addr, &len); - - if (show_debug_regs) - fprintf_unfiltered - (gdb_stdlog, - "insert_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n", - (unsigned long) addr, len); - - ret = aarch64_handle_breakpoint (type, addr, len, 1 /* is_insert */, - inferior_ptid, state); - - if (show_debug_regs) - { - aarch64_show_debug_reg_state (state, - "insert_hw_breakpoint", addr, len, type); - } - - return ret; -} - -/* Remove a hardware-assisted breakpoint at BP_TGT->placed_address. - Return 0 on success, -1 on failure. */ - -int -aarch64_linux_nat_target::remove_hw_breakpoint (struct gdbarch *gdbarch, - struct bp_target_info *bp_tgt) -{ - int ret; - CORE_ADDR addr = bp_tgt->placed_address; - int len = 4; - const enum target_hw_bp_type type = hw_execute; - struct aarch64_debug_reg_state *state - = aarch64_get_debug_reg_state (inferior_ptid.pid ()); - - gdbarch_breakpoint_from_pc (gdbarch, &addr, &len); - - 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 */, - inferior_ptid, state); - - if (show_debug_regs) - { - aarch64_show_debug_reg_state (state, - "remove_hw_watchpoint", addr, len, type); - } - - return ret; -} - -/* Implement the "insert_watchpoint" target_ops method. - - Insert a watchpoint to watch a memory region which starts at - address ADDR and whose length is LEN bytes. Watch memory accesses - of the type TYPE. Return 0 on success, -1 on failure. */ - -int -aarch64_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len, - enum target_hw_bp_type type, - struct expression *cond) -{ - int ret; - struct aarch64_debug_reg_state *state - = aarch64_get_debug_reg_state (inferior_ptid.pid ()); - - if (show_debug_regs) - fprintf_unfiltered (gdb_stdlog, - "insert_watchpoint on entry (addr=0x%08lx, len=%d)\n", - (unsigned long) addr, len); - - gdb_assert (type != hw_execute); - - ret = aarch64_handle_watchpoint (type, addr, len, 1 /* is_insert */, - inferior_ptid, state); - - if (show_debug_regs) - { - aarch64_show_debug_reg_state (state, - "insert_watchpoint", addr, len, type); - } - - return ret; -} - -/* Implement the "remove_watchpoint" target_ops method. - Remove a watchpoint that watched the memory region which starts at - address ADDR, whose length is LEN bytes, and for accesses of the - type TYPE. Return 0 on success, -1 on failure. */ - -int -aarch64_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len, - enum target_hw_bp_type type, - struct expression *cond) -{ - int ret; - struct aarch64_debug_reg_state *state - = aarch64_get_debug_reg_state (inferior_ptid.pid ()); - - if (show_debug_regs) - fprintf_unfiltered (gdb_stdlog, - "remove_watchpoint on entry (addr=0x%08lx, len=%d)\n", - (unsigned long) addr, len); - - gdb_assert (type != hw_execute); - - ret = aarch64_handle_watchpoint (type, addr, len, 0 /* is_insert */, - inferior_ptid, state); - - if (show_debug_regs) - { - aarch64_show_debug_reg_state (state, - "remove_watchpoint", addr, len, type); - } - - return ret; -} - -/* Implement the "region_ok_for_hw_watchpoint" target_ops method. */ - -int -aarch64_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) -{ - return aarch64_region_ok_for_watchpoint (addr, len); -} - /* Implement the "stopped_data_address" target_ops method. */ bool aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p) { siginfo_t siginfo; - int i; struct aarch64_debug_reg_state *state; if (!linux_nat_get_siginfo (inferior_ptid, &siginfo)) @@ -980,44 +703,7 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p) /* Check if the address matches any watched address. */ state = aarch64_get_debug_reg_state (inferior_ptid.pid ()); - for (i = aarch64_num_wp_regs - 1; i >= 0; --i) - { - const unsigned int offset - = aarch64_watchpoint_offset (state->dr_ctrl_wp[i]); - const unsigned int len = aarch64_watchpoint_length (state->dr_ctrl_wp[i]); - const CORE_ADDR addr_watch = state->dr_addr_wp[i] + offset; - const CORE_ADDR addr_watch_aligned = align_down (state->dr_addr_wp[i], 8); - const CORE_ADDR addr_orig = state->dr_addr_orig_wp[i]; - - if (state->dr_ref_count_wp[i] - && DR_CONTROL_ENABLED (state->dr_ctrl_wp[i]) - && addr_trap >= addr_watch_aligned - && addr_trap < addr_watch + len) - { - /* ADDR_TRAP reports the first address of the memory range - accessed by the CPU, regardless of what was the memory - range watched. Thus, a large CPU access that straddles - the ADDR_WATCH..ADDR_WATCH+LEN range may result in an - ADDR_TRAP that is lower than the - ADDR_WATCH..ADDR_WATCH+LEN range. E.g.: - - addr: | 4 | 5 | 6 | 7 | 8 | - |---- range watched ----| - |----------- range accessed ------------| - - In this case, ADDR_TRAP will be 4. - - To match a watchpoint known to GDB core, we must never - report *ADDR_P outside of any ADDR_WATCH..ADDR_WATCH+LEN - range. ADDR_WATCH <= ADDR_TRAP < ADDR_ORIG is a false - positive on kernels older than 4.10. See PR - external/20207. */ - *addr_p = addr_orig; - return true; - } - } - - return false; + return aarch64_stopped_data_address (state, addr_trap, addr_p); } /* Implement the "stopped_by_watchpoint" target_ops method. */ @@ -1030,15 +716,6 @@ aarch64_linux_nat_target::stopped_by_watchpoint () return stopped_data_address (&addr); } -/* Implement the "watchpoint_addr_within_range" target_ops method. */ - -bool -aarch64_linux_nat_target::watchpoint_addr_within_range (CORE_ADDR addr, - CORE_ADDR start, int length) -{ - return start <= addr && start + length - 1 >= addr; -} - /* Implement the "can_do_single_step" target_ops method. */ int @@ -1114,32 +791,11 @@ aarch64_linux_nat_target::store_memtags (CORE_ADDR address, size_t len, return false; } -/* Define AArch64 maintenance commands. */ - -static void -add_show_debug_regs_command (void) -{ - /* A maintenance command to enable printing the internal DRi mirror - variables. */ - add_setshow_boolean_cmd ("show-debug-regs", class_maintenance, - &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\ -If enabled, the debug registers values are shown when GDB inserts\n\ -or removes a hardware breakpoint or watchpoint, and when the inferior\n\ -triggers a breakpoint or watchpoint."), - NULL, - NULL, - &maintenance_set_cmdlist, - &maintenance_show_cmdlist); -} - void _initialize_aarch64_linux_nat (); void _initialize_aarch64_linux_nat () { - add_show_debug_regs_command (); + aarch64_initialize_hw_point (); /* Register the target. */ linux_target = &the_aarch64_linux_nat_target; |