diff --git a/gdb/aarch64-fbsd-nat.c b/gdb/aarch64-fbsd-nat.c index d7aa819023b..89ed12bb8e5 100644 --- a/gdb/aarch64-fbsd-nat.c +++ b/gdb/aarch64-fbsd-nat.c @@ -164,9 +164,7 @@ aarch64_fbsd_nat_target::stopped_data_address (CORE_ADDR *addr_p) bool aarch64_fbsd_nat_target::stopped_by_watchpoint () { - CORE_ADDR addr; - - return stopped_data_address (&addr); + return stopped_data_address (nullptr); } /* Implement the "stopped_by_hw_breakpoint" target_ops method. */ diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index 9dc45e1c1d9..cf7d5f8c6b1 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -972,9 +972,7 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p) bool aarch64_linux_nat_target::stopped_by_watchpoint () { - CORE_ADDR addr; - - return stopped_data_address (&addr); + return stopped_data_address (nullptr); } /* Implement the "can_do_single_step" target_ops method. */ diff --git a/gdb/aarch64-nat.c b/gdb/aarch64-nat.c index 2e8c0d80182..6c72a8d6d9f 100644 --- a/gdb/aarch64-nat.c +++ b/gdb/aarch64-nat.c @@ -231,46 +231,102 @@ bool aarch64_stopped_data_address (const struct aarch64_debug_reg_state *state, CORE_ADDR addr_trap, CORE_ADDR *addr_p) { - int i; + bool found = false; + for (int phase = 0; phase <= 1; ++phase) + for (int i = aarch64_num_wp_regs - 1; i >= 0; --i) + { + if (!(state->dr_ref_count_wp[i] + && DR_CONTROL_ENABLED (state->dr_ctrl_wp[i]))) + { + /* Watchpoint disabled. */ + continue; + } - 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]; + const enum target_hw_bp_type type + = aarch64_watchpoint_type (state->dr_ctrl_wp[i]); + if (type == hw_execute) + { + /* Watchpoint disabled. */ + continue; + } - 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.: + if (phase == 0) + { + /* Phase 0: No hw_write. */ + if (type == hw_write) + continue; + } + else + { + /* Phase 1: Only hw_write. */ + if (type != hw_write) + continue; + } - addr: | 4 | 5 | 6 | 7 | 8 | - |---- range watched ----| - |----------- range accessed ------------| + 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]; - In this case, ADDR_TRAP will be 4. + /* 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.: - 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: | 4 | 5 | 6 | 7 | 8 | + |---- range watched ----| + |----------- range accessed ------------| + + In this case, ADDR_TRAP will be 4. */ + if (!(addr_trap >= addr_watch_aligned + && addr_trap < addr_watch + len)) + { + /* Not a match. */ + continue; + } + + /* 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. */ + if (addr_p != nullptr) *addr_p = addr_orig; - return true; - } - } - return false; + if (phase == 0) + { + /* Phase 0: Return first match. */ + return true; + } + + /* Phase 1. */ + if (addr_p == nullptr) + { + /* First match, and we don't need to report an address. No need + to look for other matches. */ + return true; + } + + if (!found) + { + /* First match, and we need to report an address. Look for other + matches. */ + found = true; + continue; + } + + /* More than one match, and we need to return an address. No need to + look for further matches. */ + return false; + } + + return found; } /* Define AArch64 maintenance commands. */ diff --git a/gdb/nat/aarch64-hw-point.c b/gdb/nat/aarch64-hw-point.c index 04674dea2a6..08fd230b71f 100644 --- a/gdb/nat/aarch64-hw-point.c +++ b/gdb/nat/aarch64-hw-point.c @@ -73,6 +73,31 @@ aarch64_watchpoint_length (unsigned int ctrl) return retval; } +/* Utility function that returns the type of a watchpoint according to the + content of a hardware debug control register CTRL. */ + +enum target_hw_bp_type +aarch64_watchpoint_type (unsigned int ctrl) +{ + unsigned int type = DR_CONTROL_TYPE (ctrl); + + switch (type) + { + case 1: + return hw_read; + case 2: + return hw_write; + case 3: + return hw_access; + case 0: + /* Reserved for a watchpoint. It must behave as if the watchpoint is + disabled. */ + return hw_execute; + default: + gdb_assert_not_reached (""); + } +} + /* Given the hardware breakpoint or watchpoint type TYPE and its length LEN, return the expected encoding for a hardware breakpoint/watchpoint control register. */ diff --git a/gdb/nat/aarch64-hw-point.h b/gdb/nat/aarch64-hw-point.h index 70f71db7520..bdcca932e57 100644 --- a/gdb/nat/aarch64-hw-point.h +++ b/gdb/nat/aarch64-hw-point.h @@ -73,6 +73,7 @@ #define DR_CONTROL_ENABLED(ctrl) (((ctrl) & 0x1) == 1) #define DR_CONTROL_MASK(ctrl) (((ctrl) >> 5) & 0xff) +#define DR_CONTROL_TYPE(ctrl) (((ctrl) >> 3) & 0x3) /* Structure for managing the hardware breakpoint/watchpoint resources. DR_ADDR_* stores the address, DR_CTRL_* stores the control register @@ -107,6 +108,7 @@ void aarch64_notify_debug_reg_change (ptid_t ptid, int is_watchpoint, unsigned int aarch64_watchpoint_offset (unsigned int ctrl); unsigned int aarch64_watchpoint_length (unsigned int ctrl); +enum target_hw_bp_type aarch64_watchpoint_type (unsigned int ctrl); int aarch64_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr, int len, int is_insert, ptid_t ptid,