[gdb/tdep] Fix gdb.base/watchpoint-unaligned.exp on aarch64
commit9a03f2185347bd8f20da9bf535bc68a8d0f18ce8
authorTom de Vries <tdevries@suse.de>
Thu, 14 Mar 2024 10:25:10 +0000 (14 11:25 +0100)
committerTom de Vries <tdevries@suse.de>
Thu, 14 Mar 2024 10:25:10 +0000 (14 11:25 +0100)
tree6ee5258b5bdbef602d17686512b7be04fe96f6ae
parent3a4c6f1aa958739705ee69526fdeed7c69d7243c
[gdb/tdep] Fix gdb.base/watchpoint-unaligned.exp on aarch64

On aarch64-linux, with test-case gdb.base/watchpoint-unaligned.exp I run into:
...
(gdb) watch data.u.size8twice[1]^M
Hardware watchpoint 241: data.u.size8twice[1]^M
(gdb) PASS: gdb.base/watchpoint-unaligned.exp: watch data.u.size8twice[1]
continue^M
Continuing.^M
FAIL: gdb.base/watchpoint-unaligned.exp: continue (timeout)
FAIL: gdb.base/watchpoint-unaligned.exp: size8twice write
...

This happens as follows.

We start the exec and set an 8-byte hardware watchpoint on
data.u.size8twice[1] at address 0x440048:
...
(gdb) p sizeof (data.u.size8twice[1])
$1 = 8
(gdb) p &data.u.size8twice[1]
$2 = (uint64_t *) 0x440048 <data+16>
...

We continue execution, and a 16-byte write at address 0x440040 triggers the
hardware watchpoint:
...
  4101c8:       a9000801        stp     x1, x2, [x0]
...

When checking whether a watchpoint has triggered in
aarch64_stopped_data_address, we check against address 0x440040 (passed in
parameter addr_trap).  This behaviour is documented:
...
  /* ADDR_TRAP reports the first address of the memory range
     accessed by the CPU, regardless of what was the memory
     range watched.  ...  */
...
and consequently the matching logic compares against an addr_watch_aligned:
...
  && addr_trap >= addr_watch_aligned
  && addr_trap < addr_watch + len)
...

However, the comparison fails:
...
(gdb) p /x addr_watch_aligned
$3 = 0x440048
(gdb) p addr_trap >= addr_watch_aligned
$4 = false
...

Consequently, aarch64_stopped_data_address returns false, and
stopped_by_watchpoint returns false, and watchpoints_triggered returns 0,
which make infrun think it's looking at a delayed hardware
breakpoint/watchpoint trap:
...
  [infrun] handle_signal_stop: stop_pc=0x4101c8
  [infrun] handle_signal_stop: delayed hardware breakpoint/watchpoint trap, ignoring
...
Infrun then ignores the trap and continues, but runs into the same situation
again and again, causing a hang which then causes the test timeout.

Fix this by allowing a match 8 bytes below addr_watch_aligned.  This
introduces the possibility for false positives, so we only do this for regular
"value changed" watchpoints.

An earlier version of this patch worked by aligning addr_watch_aligned to 16
instead of 8:
...
-  const CORE_ADDR addr_watch_aligned = align_down (state->dr_addr_wp[i], 8);
+  const CORE_ADDR addr_watch_aligned = align_down (state->dr_addr_wp[i], 16);
...
but while that fixed the test-case, it didn't fix the problem completely, so
extend the test-case to check more scenarios.

Tested on aarch64-linux.

Tested-By: Luis Machado <luis.machado@arm.com>
Approved-By: Luis Machado <luis.machado@arm.com>
PR tdep/29423
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29423
gdb/aarch64-nat.c
gdb/testsuite/gdb.base/watchpoint-unaligned.c
gdb/testsuite/gdb.base/watchpoint-unaligned.exp