From 6f9909c4179983330a29b6fd246b39ab1b003934 Mon Sep 17 00:00:00 2001 From: Tom de Vries Date: Tue, 12 Aug 2025 16:35:05 +0200 Subject: [PATCH] [gdb/tdep] Handle M1 ldp in aarch64_stopped_data_address In test-case gdb.base/watchpoint-unaligned.exp, in function write_size8twice, two adjacent 8-byte vars are written. For aarch64, we use a single stp instruction for that. If we do the same in function read_size8twice for two adjacent 8-byte var reads using aarch64 insn ldp, on an aarch64-linux M1 system we get a hang: ... (gdb) continue^M Continuing.^M FAIL: $exp: fun=read_size8twice: offset=0: index=1: continue (timeout) FAIL: $exp: fun=read_size8twice: offset=0: index=1: $got_hit ... The same problem was observed for stp in PR tdep/29423, fixed by commit 9a03f218534 ("[gdb/tdep] Fix gdb.base/watchpoint-unaligned.exp on aarch64"). See that commit for an explanation of the hang. That commit introduced max_access_size in aarch64_stopped_data_address: ... The access size also can be larger than that of the watchpoint itself. For instance, the access size of an stp instruction is 16. So, if we use stp to store to address p, and set a watchpoint on address p + 8, the reported ADDR_TRAP can be p + 8 (observed on RK3399 SOC). But it also can be p (observed on M1 SOC). Checking for this situation introduces the possibility of false positives, so we only do this for hw_write watchpoints. */ const CORE_ADDR max_access_size = type == hw_write ? 16 : 8; ... If we say that hangs are worse than false positives, then we should also fix this case. Fix this by setting max_access_size to 16 for all watchpoint types. Tested on aarch64-linux, both on an M1 SOC and an RK3399 SOC. Approved-By: Luis Machado --- gdb/nat/aarch64-hw-point.c | 6 ++---- gdb/testsuite/gdb.base/watchpoint-unaligned.c | 8 ++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/gdb/nat/aarch64-hw-point.c b/gdb/nat/aarch64-hw-point.c index 6d8dce86dfe..8c0854bcc88 100644 --- a/gdb/nat/aarch64-hw-point.c +++ b/gdb/nat/aarch64-hw-point.c @@ -710,10 +710,8 @@ aarch64_stopped_data_address (const struct aarch64_debug_reg_state *state, itself. For instance, the access size of an stp instruction is 16. So, if we use stp to store to address p, and set a watchpoint on address p + 8, the reported ADDR_TRAP can be p + 8 (observed on - RK3399 SOC). But it also can be p (observed on M1 SOC). Checking - for this situation introduces the possibility of false positives, - so we only do this for hw_write watchpoints. */ - const CORE_ADDR max_access_size = type == hw_write ? 16 : 8; + RK3399 SOC). But it also can be p (observed on M1 SOC). */ + const CORE_ADDR max_access_size = 16; const CORE_ADDR addr_watch_base = addr_watch_aligned - (max_access_size - AARCH64_HWP_MAX_LEN_PER_REG); if (!(addr_trap >= addr_watch_base diff --git a/gdb/testsuite/gdb.base/watchpoint-unaligned.c b/gdb/testsuite/gdb.base/watchpoint-unaligned.c index baa7ae021a1..ca2fa457c14 100644 --- a/gdb/testsuite/gdb.base/watchpoint-unaligned.c +++ b/gdb/testsuite/gdb.base/watchpoint-unaligned.c @@ -71,8 +71,16 @@ read_size8twice (void) static uint64_t volatile first; static uint64_t volatile second; +#ifdef __aarch64__ + volatile void *p = &data.u.size8twice[offset]; + asm volatile ("ldp %0, %1, [%2]" + : "=r" (first), "=r" (second) /* output */ + : "r" (p) /* input */ + : /* clobber */); +#else first = data.u.size8twice[offset]; second = data.u.size8twice[offset + 1]; +#endif /* Setting a breakpoint on an instruction after an instruction triggering a watchpoint makes it ambiguous which one will be reported. -- 2.47.2