This is the mail archive of the gdb-prs@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug breakpoints/19806] New: Aarch64: watchpoints set on non-8-byte-aligned addresses are always missed


https://sourceware.org/bugzilla/show_bug.cgi?id=19806

            Bug ID: 19806
           Summary: Aarch64: watchpoints set on non-8-byte-aligned
                    addresses are always missed
           Product: gdb
           Version: HEAD
            Status: NEW
          Severity: normal
          Priority: P2
         Component: breakpoints
          Assignee: unassigned at sourceware dot org
          Reporter: palves at redhat dot com
  Target Milestone: ---

For example, with:

union
{
  char buf[4];
  unsigned int ul;
} u;

int
main ()
{
  u.ul = 0xffffffff;
  return 0;
}

on x86-64, we get:

(gdb) watch u.buf[1]
Hardware watchpoint 1: u.buf[1]
(gdb) c
Continuing.
Hardware watchpoint 1: u.buf[1]

Old value = 0 '\000'
New value = -1 '\377'
main () at watch.c:11
11              return 0;
(gdb) 

While on Aarch64, gdb just miss the watchpoint hit.

Actually, the kernel reports the hit to gdb, and linux-nat.c forwards the event
to infrun.c.

However, it doesn't work as expected because this:


int
watchpoints_triggered (struct target_waitstatus *ws)
{
...
  ALL_BREAKPOINTS (b)
    if (is_hardware_watchpoint (b))
      {
...
        for (loc = b->loc; loc; loc = loc->next)
          {
...
            /* Exact match not required.  Within range is sufficient.  */
            else if (target_watchpoint_addr_within_range (&current_target,
                                                         addr, loc->address,
                                                         loc->length))
              {
                w->watchpoint_triggered = watch_triggered_yes;
                break;
              }
          }
      }

  return 1;
}


never reaches the:

  w->watchpoint_triggered = watch_triggered_yes;

line, because this:

/* Implement the "to_watchpoint_addr_within_range" target_ops method.  */

static int
aarch64_linux_watchpoint_addr_within_range (struct target_ops *target,
                                            CORE_ADDR addr,
                                            CORE_ADDR start, int length)
{
  return start <= addr && start + length - 1 >= addr;
}

doesn't consider the aarch64 watchpoint alignment restrictions, and returns
false if the kernel-reported stop data address is 8-byte aligned, outside the
original watched range.  

So bpstat_check_watchpoint will never check the watched expression either, and
thus the watchpoint trigger ends up NOT reported to the user.

Some PowerPC machines have similar restrictions, and the ppc version has this
instead:

static int
ppc_linux_watchpoint_addr_within_range (...)
{
  int mask;

  if (have_ptrace_hwdebug_interface ()
      && ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE)
    return start <= addr && start + length >= addr;
  else if (ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE)
    mask = 3;
  else
    mask = 7;

  addr &= ~mask;

  /* Check whether [start, start+length-1] intersects [addr, addr+mask].  */
  return start <= addr + mask && start + length - 1 >= addr;
}

Instead, we'll reach the moribund watchpoint locations handling, and
and automatically re-resume the program:

    fprintf_unfiltered (gdb_stdlog,
                        "infrun: no user watchpoint explains "
                        "watchpoint SIGTRAP, ignoring\n");


The Aarch64 watchpoint alignment restrictions are discussed here:

nat/aarch64-linux-hw-point.c:aarch64_align_watchpoint:

/* Given the (potentially unaligned) watchpoint address in ADDR and
   length in LEN, return the aligned address and aligned length in
   *ALIGNED_ADDR_P and *ALIGNED_LEN_P, respectively.  The returned
   aligned address and length will be valid values to write to the
   hardware watchpoint value and control registers.

...

   Essentially, unaligned watchpoint is achieved by minimally
   enlarging the watched area to meet the alignment requirement, and
   if necessary, splitting the watchpoint over several hardware
   watchpoint registers.  The trade-off is that there will be
   false-positive hits for the read-type or the access-type hardware
   watchpoints; for the write type, which is more commonly used, there
   will be no such issues, as the higher-level breakpoint management
   in gdb always examines the exact watched region for any content
   change, and transparently resumes a thread from a watchpoint trap
   if there is no change to the watched region.

   Another limitation is that because the watched region is enlarged,
   the watchpoint fault address returned by
   aarch64_stopped_data_address may be outside of the original watched
   region, especially when the triggering instruction is accessing a
   larger region.  When the fault address is not within any known
   range, watchpoints_triggered in gdb will get confused, as the
   higher-level watchpoint management is only aware of original
   watched regions, and will think that some unknown watchpoint has
   been triggered.  In such a case, gdb may stop without displaying
   any detailed information.

   Once the kernel provides the full support for Byte Address Select
   (BAS) in the hardware watchpoint control register, these
   limitations can be largely relaxed with some further work.  */

(...)

nat/aarch64-linux-hw-point.h:

/* Alignment requirement in bytes for addresses written to
   hardware breakpoint and watchpoint value registers.

   A ptrace call attempting to set an address that does not meet the
   alignment criteria will fail.  Limited support has been provided in
   this port for unaligned watchpoints, such that from a GDB user
   perspective, an unaligned watchpoint may be requested.

   This is achieved by minimally enlarging the watched area to meet the
   alignment requirement, and if necessary, splitting the watchpoint
   over several hardware watchpoint registers.  */

(...)
#define AARCH64_HWP_ALIGNMENT 8

For the same reason, read watchpoints on non-8-byte-aligned addresses are
always missed too, instead of the occasional false positive suggested by the
comments above.  I think that when that was written, GDB would stop with a
spurious SIGTRAP.  Fixing the aarch64 range detection would make us report a
spurious watchpoint hit, which is IMO obviously much better.

In any case, I think the occasional read watchpoint false positive is much
better than ever missing (read or regular) watchpoints.

-- 
You are receiving this mail because:
You are on the CC list for the bug.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]