palves at redhat dot com
2016-03-10 19:56:50 UTC
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 (¤t_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.
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 (¤t_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.
You are receiving this mail because:
You are on the CC list for the bug.