public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] [AArch64] Fix TBI handling for watchpoints
@ 2020-12-11 16:07 Luis Machado
  2020-12-14 12:52 ` Alan Hayward
  0 siblings, 1 reply; 3+ messages in thread
From: Luis Machado @ 2020-12-11 16:07 UTC (permalink / raw)
  To: gdb-patches

When inserting hw watchpoints, we take care of masking off the top byte
of the address (and sign-extending it if needed).  This guarantees we won't
pass tagged addresses to the kernel via ptrace.

However, from the kernel documentation on tagged pointers...

"Non-zero tags are not preserved when delivering signals. This means that
signal handlers in applications making use of tags cannot rely on the tag
information for user virtual addresses being maintained for fields inside
siginfo_t.

One exception to this rule is for signals raised in response to watchpoint
debug exceptions, where the tag information will be preserved."

So the stopped data address after a hw watchpoint hit can be potentially
tagged, and we don't handle this in GDB at the moment.  This results in
GDB missing a hw watchpoint hit and attempting to step over an unsteppable
hw watchpoint, causing it to spin endlessly.

The following patch fixes this by adjusting the stopped data address and adds
some tests to expose the problem.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>

	* aarch64-linux-nat.c
	(aarch64_linux_nat_target::stopped_data_address): Handle the TBI.

gdbserver/ChangeLog:

YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>

	* linux-aarch64-low.cc (address_significant): New function.
	(aarch64_target::low_stopped_data_address): Handle the TBI.

gdb/testsuite/ChangeLog:

YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>

	* gdb.arch/aarch64-tagged-pointer.c (main): Add a few more
	pointer-based memory accesses.
	* gdb.arch/aarch64-tagged-pointer.exp: Exercise additional
	hw watchpoint cases.
---
 gdb/aarch64-linux-nat.c                       |  8 ++++-
 .../gdb.arch/aarch64-tagged-pointer.c         |  8 ++++-
 .../gdb.arch/aarch64-tagged-pointer.exp       | 27 ++++++++++------
 gdbserver/linux-aarch64-low.cc                | 31 ++++++++++++++++++-
 4 files changed, 61 insertions(+), 13 deletions(-)

diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index 77d5863a56..b3bbde4b92 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -877,6 +877,13 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
       || (siginfo.si_code & 0xffff) != TRAP_HWBKPT)
     return false;
 
+  /* Make sure to ignore the top byte, otherwise we may not recognize a
+     hardware watchpoint hit.  The stopped data addresses coming from the
+     kernel can potentially be tagged addresses.  */
+  struct gdbarch *gdbarch = thread_architecture (inferior_ptid);
+  const CORE_ADDR addr_trap
+    = address_significant (gdbarch, (CORE_ADDR) siginfo.si_addr);
+
   /* Check if the address matches any watched address.  */
   state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
   for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
@@ -884,7 +891,6 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
       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_trap = (CORE_ADDR) siginfo.si_addr;
       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];
diff --git a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c
index 609d4f220e..658c3093e8 100644
--- a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c
+++ b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c
@@ -53,5 +53,11 @@ main (void)
     }
 
   sp1->i = 8765;
-  i = 1;
+  sp2->i = 4321;
+  sp1->i = 8765;
+  sp2->i = 4321;
+  *p1 = 1;
+  *p2 = 2;
+  *p1 = 1;
+  *p2 = 2;
 }
diff --git a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp
index 957571fdf9..01c2b577d5 100644
--- a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp
+++ b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp
@@ -92,14 +92,21 @@ foreach_with_prefix bptype {"hbreak" "break"} {
 
 gdb_test "down"
 gdb_test "finish"
-# Watch on tagged pointer.
-gdb_test "watch *sp2"
-gdb_test "continue" \
-    "Continuing\\..*Hardware watchpoint \[0-9\]+.*" \
-    "run until watchpoint on s1"
-delete_breakpoints
 
-gdb_test "watch *p2"
-gdb_test "continue" \
-    "Continuing\\..*Hardware watchpoint \[0-9\]+.*" \
-    "run until watchpoint on i"
+# sp1 and p1 are untagged pointers, but sp2 and p2 are tagged pointers.
+# Cycle through all of them to make sure the following combinations work:
+#
+# hw watch on untagged address, hit on untagged address.
+# hw watch on tagged address, hit on untagged address.
+# hw watch on untagged address, hit on tagged address.
+# hw watch on tagged address, hit on tagged address.
+foreach symbol {"sp1" "sp2" "p1" "p2"} {
+    gdb_test "watch *${symbol}"
+    gdb_test "continue" \
+	"Continuing\\..*Hardware watchpoint \[0-9\]+.*" \
+	"run until watchpoint on ${symbol}"
+    gdb_test "continue" \
+	"Continuing\\..*Hardware watchpoint \[0-9\]+.*" \
+	"run until watchpoint on ${symbol}, 2nd hit"
+    delete_breakpoints
+}
diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc
index 08208ae4f4..da599ef4bb 100644
--- a/gdbserver/linux-aarch64-low.cc
+++ b/gdbserver/linux-aarch64-low.cc
@@ -458,6 +458,30 @@ aarch64_target::low_remove_point (raw_bkpt_type type, CORE_ADDR addr,
   return ret;
 }
 
+/* Return the address only having significant bits.  This is used to ignore
+   the top byte (TBI).  */
+
+static CORE_ADDR
+address_significant (CORE_ADDR addr)
+{
+  /* Clear insignificant bits of a target address and sign extend resulting
+     address, avoiding shifts larger or equal than the width of a CORE_ADDR.
+     The local variable ADDR_BIT stops the compiler reporting a shift overflow
+     when it won't occur.  Skip updating of target address if current target
+     has not set gdbarch significant_addr_bit.  */
+  int addr_bit = 56;
+  int char_bit = CHAR_BIT;
+
+  if (addr_bit && (addr_bit < (sizeof (CORE_ADDR) * char_bit)))
+    {
+      CORE_ADDR sign = (CORE_ADDR) 1 << (addr_bit - 1);
+      addr &= ((CORE_ADDR) 1 << addr_bit) - 1;
+      addr = (addr ^ sign) - sign;
+    }
+
+  return addr;
+}
+
 /* Implementation of linux target ops method "low_stopped_data_address".  */
 
 CORE_ADDR
@@ -478,6 +502,12 @@ aarch64_target::low_stopped_data_address ()
       || (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
     return (CORE_ADDR) 0;
 
+  /* Make sure to ignore the top byte, otherwise we may not recognize a
+     hardware watchpoint hit.  The stopped data addresses coming from the
+     kernel can potentially be tagged addresses.  */
+  const CORE_ADDR addr_trap
+    = address_significant ((CORE_ADDR) siginfo.si_addr);
+
   /* Check if the address matches any watched address.  */
   state = aarch64_get_debug_reg_state (pid_of (current_thread));
   for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
@@ -485,7 +515,6 @@ aarch64_target::low_stopped_data_address ()
       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_trap = (CORE_ADDR) siginfo.si_addr;
       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];
-- 
2.25.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] [AArch64] Fix TBI handling for watchpoints
  2020-12-11 16:07 [PATCH] [AArch64] Fix TBI handling for watchpoints Luis Machado
@ 2020-12-14 12:52 ` Alan Hayward
  2020-12-14 13:15   ` Luis Machado
  0 siblings, 1 reply; 3+ messages in thread
From: Alan Hayward @ 2020-12-14 12:52 UTC (permalink / raw)
  To: Luis Machado; +Cc: gdb-patches\@sourceware.org, nd



> On 11 Dec 2020, at 16:07, Luis Machado <luis.machado@linaro.org> wrote:
> 
> When inserting hw watchpoints, we take care of masking off the top byte
> of the address (and sign-extending it if needed).  This guarantees we won't
> pass tagged addresses to the kernel via ptrace.
> 
> However, from the kernel documentation on tagged pointers...
> 
> "Non-zero tags are not preserved when delivering signals. This means that
> signal handlers in applications making use of tags cannot rely on the tag
> information for user virtual addresses being maintained for fields inside
> siginfo_t.
> 
> One exception to this rule is for signals raised in response to watchpoint
> debug exceptions, where the tag information will be preserved."
> 
> So the stopped data address after a hw watchpoint hit can be potentially
> tagged, and we don't handle this in GDB at the moment.  This results in
> GDB missing a hw watchpoint hit and attempting to step over an unsteppable
> hw watchpoint, causing it to spin endlessly.
> 
> The following patch fixes this by adjusting the stopped data address and adds
> some tests to expose the problem.
> 
> gdb/ChangeLog:
> 
> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>
> 
> 	* aarch64-linux-nat.c
> 	(aarch64_linux_nat_target::stopped_data_address): Handle the TBI.
> 
> gdbserver/ChangeLog:
> 
> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>
> 
> 	* linux-aarch64-low.cc (address_significant): New function.
> 	(aarch64_target::low_stopped_data_address): Handle the TBI.
> 
> gdb/testsuite/ChangeLog:
> 
> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>
> 
> 	* gdb.arch/aarch64-tagged-pointer.c (main): Add a few more
> 	pointer-based memory accesses.
> 	* gdb.arch/aarch64-tagged-pointer.exp: Exercise additional
> 	hw watchpoint cases.
> ---
> gdb/aarch64-linux-nat.c                       |  8 ++++-
> .../gdb.arch/aarch64-tagged-pointer.c         |  8 ++++-
> .../gdb.arch/aarch64-tagged-pointer.exp       | 27 ++++++++++------
> gdbserver/linux-aarch64-low.cc                | 31 ++++++++++++++++++-
> 4 files changed, 61 insertions(+), 13 deletions(-)
> 
> diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
> index 77d5863a56..b3bbde4b92 100644
> --- a/gdb/aarch64-linux-nat.c
> +++ b/gdb/aarch64-linux-nat.c
> @@ -877,6 +877,13 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
>       || (siginfo.si_code & 0xffff) != TRAP_HWBKPT)
>     return false;
> 
> +  /* Make sure to ignore the top byte, otherwise we may not recognize a
> +     hardware watchpoint hit.  The stopped data addresses coming from the
> +     kernel can potentially be tagged addresses.  */
> +  struct gdbarch *gdbarch = thread_architecture (inferior_ptid);
> +  const CORE_ADDR addr_trap
> +    = address_significant (gdbarch, (CORE_ADDR) siginfo.si_addr);
> +
>   /* Check if the address matches any watched address.  */
>   state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
>   for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
> @@ -884,7 +891,6 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
>       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_trap = (CORE_ADDR) siginfo.si_addr;
>       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];
> diff --git a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c
> index 609d4f220e..658c3093e8 100644
> --- a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c
> +++ b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c
> @@ -53,5 +53,11 @@ main (void)
>     }
> 
>   sp1->i = 8765;
> -  i = 1;
> +  sp2->i = 4321;
> +  sp1->i = 8765;
> +  sp2->i = 4321;
> +  *p1 = 1;
> +  *p2 = 2;
> +  *p1 = 1;
> +  *p2 = 2;
> }
> diff --git a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp
> index 957571fdf9..01c2b577d5 100644
> --- a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp
> +++ b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp
> @@ -92,14 +92,21 @@ foreach_with_prefix bptype {"hbreak" "break"} {
> 
> gdb_test "down"
> gdb_test "finish"
> -# Watch on tagged pointer.
> -gdb_test "watch *sp2"
> -gdb_test "continue" \
> -    "Continuing\\..*Hardware watchpoint \[0-9\]+.*" \
> -    "run until watchpoint on s1"
> -delete_breakpoints
> 
> -gdb_test "watch *p2"
> -gdb_test "continue" \
> -    "Continuing\\..*Hardware watchpoint \[0-9\]+.*" \
> -    "run until watchpoint on i"
> +# sp1 and p1 are untagged pointers, but sp2 and p2 are tagged pointers.
> +# Cycle through all of them to make sure the following combinations work:
> +#
> +# hw watch on untagged address, hit on untagged address.
> +# hw watch on tagged address, hit on untagged address.
> +# hw watch on untagged address, hit on tagged address.
> +# hw watch on tagged address, hit on tagged address.
> +foreach symbol {"sp1" "sp2" "p1" "p2"} {
> +    gdb_test "watch *${symbol}"
> +    gdb_test "continue" \
> +	"Continuing\\..*Hardware watchpoint \[0-9\]+.*" \
> +	"run until watchpoint on ${symbol}"
> +    gdb_test "continue" \
> +	"Continuing\\..*Hardware watchpoint \[0-9\]+.*" \
> +	"run until watchpoint on ${symbol}, 2nd hit"
> +    delete_breakpoints
> +}
> diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc
> index 08208ae4f4..da599ef4bb 100644
> --- a/gdbserver/linux-aarch64-low.cc
> +++ b/gdbserver/linux-aarch64-low.cc
> @@ -458,6 +458,30 @@ aarch64_target::low_remove_point (raw_bkpt_type type, CORE_ADDR addr,
>   return ret;
> }
> 
> +/* Return the address only having significant bits.  This is used to ignore
> +   the top byte (TBI).  */
> +
> +static CORE_ADDR
> +address_significant (CORE_ADDR addr)

Ideally this would be in a common file. But it’s only used the once, so it’s
not worth the hassle of making it arch independent (plus then I’d be wondering
if it could get combined with the gdb version of the function).


> +{
> +  /* Clear insignificant bits of a target address and sign extend resulting
> +     address, avoiding shifts larger or equal than the width of a CORE_ADDR.
> +     The local variable ADDR_BIT stops the compiler reporting a shift overflow
> +     when it won't occur.  Skip updating of target address if current target
> +     has not set gdbarch significant_addr_bit.  */

This comment shouldn’t mention gdbarch or significant_addr_bit.

> +  int addr_bit = 56;
> +  int char_bit = CHAR_BIT;
> +
> +  if (addr_bit && (addr_bit < (sizeof (CORE_ADDR) * char_bit)))

This whole if statement is redundant - all values are fixed.


Everything else looks good.

> +    {
> +      CORE_ADDR sign = (CORE_ADDR) 1 << (addr_bit - 1);
> +      addr &= ((CORE_ADDR) 1 << addr_bit) - 1;
> +      addr = (addr ^ sign) - sign;
> +    }
> +
> +  return addr;
> +}
> +
> /* Implementation of linux target ops method "low_stopped_data_address".  */
> 
> CORE_ADDR
> @@ -478,6 +502,12 @@ aarch64_target::low_stopped_data_address ()
>       || (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
>     return (CORE_ADDR) 0;
> 
> +  /* Make sure to ignore the top byte, otherwise we may not recognize a
> +     hardware watchpoint hit.  The stopped data addresses coming from the
> +     kernel can potentially be tagged addresses.  */
> +  const CORE_ADDR addr_trap
> +    = address_significant ((CORE_ADDR) siginfo.si_addr);
> +
>   /* Check if the address matches any watched address.  */
>   state = aarch64_get_debug_reg_state (pid_of (current_thread));
>   for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
> @@ -485,7 +515,6 @@ aarch64_target::low_stopped_data_address ()
>       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_trap = (CORE_ADDR) siginfo.si_addr;
>       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];
> -- 
> 2.25.1
> 


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] [AArch64] Fix TBI handling for watchpoints
  2020-12-14 12:52 ` Alan Hayward
@ 2020-12-14 13:15   ` Luis Machado
  0 siblings, 0 replies; 3+ messages in thread
From: Luis Machado @ 2020-12-14 13:15 UTC (permalink / raw)
  To: Alan Hayward; +Cc: gdb-patches\@sourceware.org, nd

On 12/14/20 9:52 AM, Alan Hayward wrote:
> 
> 
>> On 11 Dec 2020, at 16:07, Luis Machado <luis.machado@linaro.org> wrote:
>>
>> When inserting hw watchpoints, we take care of masking off the top byte
>> of the address (and sign-extending it if needed).  This guarantees we won't
>> pass tagged addresses to the kernel via ptrace.
>>
>> However, from the kernel documentation on tagged pointers...
>>
>> "Non-zero tags are not preserved when delivering signals. This means that
>> signal handlers in applications making use of tags cannot rely on the tag
>> information for user virtual addresses being maintained for fields inside
>> siginfo_t.
>>
>> One exception to this rule is for signals raised in response to watchpoint
>> debug exceptions, where the tag information will be preserved."
>>
>> So the stopped data address after a hw watchpoint hit can be potentially
>> tagged, and we don't handle this in GDB at the moment.  This results in
>> GDB missing a hw watchpoint hit and attempting to step over an unsteppable
>> hw watchpoint, causing it to spin endlessly.
>>
>> The following patch fixes this by adjusting the stopped data address and adds
>> some tests to expose the problem.
>>
>> gdb/ChangeLog:
>>
>> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>
>>
>> 	* aarch64-linux-nat.c
>> 	(aarch64_linux_nat_target::stopped_data_address): Handle the TBI.
>>
>> gdbserver/ChangeLog:
>>
>> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>
>>
>> 	* linux-aarch64-low.cc (address_significant): New function.
>> 	(aarch64_target::low_stopped_data_address): Handle the TBI.
>>
>> gdb/testsuite/ChangeLog:
>>
>> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>
>>
>> 	* gdb.arch/aarch64-tagged-pointer.c (main): Add a few more
>> 	pointer-based memory accesses.
>> 	* gdb.arch/aarch64-tagged-pointer.exp: Exercise additional
>> 	hw watchpoint cases.
>> ---
>> gdb/aarch64-linux-nat.c                       |  8 ++++-
>> .../gdb.arch/aarch64-tagged-pointer.c         |  8 ++++-
>> .../gdb.arch/aarch64-tagged-pointer.exp       | 27 ++++++++++------
>> gdbserver/linux-aarch64-low.cc                | 31 ++++++++++++++++++-
>> 4 files changed, 61 insertions(+), 13 deletions(-)
>>
>> diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
>> index 77d5863a56..b3bbde4b92 100644
>> --- a/gdb/aarch64-linux-nat.c
>> +++ b/gdb/aarch64-linux-nat.c
>> @@ -877,6 +877,13 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
>>        || (siginfo.si_code & 0xffff) != TRAP_HWBKPT)
>>      return false;
>>
>> +  /* Make sure to ignore the top byte, otherwise we may not recognize a
>> +     hardware watchpoint hit.  The stopped data addresses coming from the
>> +     kernel can potentially be tagged addresses.  */
>> +  struct gdbarch *gdbarch = thread_architecture (inferior_ptid);
>> +  const CORE_ADDR addr_trap
>> +    = address_significant (gdbarch, (CORE_ADDR) siginfo.si_addr);
>> +
>>    /* Check if the address matches any watched address.  */
>>    state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
>>    for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
>> @@ -884,7 +891,6 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
>>        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_trap = (CORE_ADDR) siginfo.si_addr;
>>        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];
>> diff --git a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c
>> index 609d4f220e..658c3093e8 100644
>> --- a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c
>> +++ b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c
>> @@ -53,5 +53,11 @@ main (void)
>>      }
>>
>>    sp1->i = 8765;
>> -  i = 1;
>> +  sp2->i = 4321;
>> +  sp1->i = 8765;
>> +  sp2->i = 4321;
>> +  *p1 = 1;
>> +  *p2 = 2;
>> +  *p1 = 1;
>> +  *p2 = 2;
>> }
>> diff --git a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp
>> index 957571fdf9..01c2b577d5 100644
>> --- a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp
>> +++ b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp
>> @@ -92,14 +92,21 @@ foreach_with_prefix bptype {"hbreak" "break"} {
>>
>> gdb_test "down"
>> gdb_test "finish"
>> -# Watch on tagged pointer.
>> -gdb_test "watch *sp2"
>> -gdb_test "continue" \
>> -    "Continuing\\..*Hardware watchpoint \[0-9\]+.*" \
>> -    "run until watchpoint on s1"
>> -delete_breakpoints
>>
>> -gdb_test "watch *p2"
>> -gdb_test "continue" \
>> -    "Continuing\\..*Hardware watchpoint \[0-9\]+.*" \
>> -    "run until watchpoint on i"
>> +# sp1 and p1 are untagged pointers, but sp2 and p2 are tagged pointers.
>> +# Cycle through all of them to make sure the following combinations work:
>> +#
>> +# hw watch on untagged address, hit on untagged address.
>> +# hw watch on tagged address, hit on untagged address.
>> +# hw watch on untagged address, hit on tagged address.
>> +# hw watch on tagged address, hit on tagged address.
>> +foreach symbol {"sp1" "sp2" "p1" "p2"} {
>> +    gdb_test "watch *${symbol}"
>> +    gdb_test "continue" \
>> +	"Continuing\\..*Hardware watchpoint \[0-9\]+.*" \
>> +	"run until watchpoint on ${symbol}"
>> +    gdb_test "continue" \
>> +	"Continuing\\..*Hardware watchpoint \[0-9\]+.*" \
>> +	"run until watchpoint on ${symbol}, 2nd hit"
>> +    delete_breakpoints
>> +}
>> diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc
>> index 08208ae4f4..da599ef4bb 100644
>> --- a/gdbserver/linux-aarch64-low.cc
>> +++ b/gdbserver/linux-aarch64-low.cc
>> @@ -458,6 +458,30 @@ aarch64_target::low_remove_point (raw_bkpt_type type, CORE_ADDR addr,
>>    return ret;
>> }
>>
>> +/* Return the address only having significant bits.  This is used to ignore
>> +   the top byte (TBI).  */
>> +
>> +static CORE_ADDR
>> +address_significant (CORE_ADDR addr)
> 
> Ideally this would be in a common file. But it’s only used the once, so it’s
> not worth the hassle of making it arch independent (plus then I’d be wondering
> if it could get combined with the gdb version of the function).

I considered unifying the implementation, but then it would've required 
a change to how this is implemented in gdbarch. Currently it is just the 
number of significant bits plus a generic implementation. We'd need to 
turn it into an arch-specific implementation instead of just relying on 
the number of bits, plus a generic default implementation.

But that means we'd need to replicate the code for each architecture 
potentially. Hence the gdbarch-independent implementation in gdbserver.

Not too pretty on both strategies. I picked this one.

> 
> 
>> +{
>> +  /* Clear insignificant bits of a target address and sign extend resulting
>> +     address, avoiding shifts larger or equal than the width of a CORE_ADDR.
>> +     The local variable ADDR_BIT stops the compiler reporting a shift overflow
>> +     when it won't occur.  Skip updating of target address if current target
>> +     has not set gdbarch significant_addr_bit.  */
> 
> This comment shouldn’t mention gdbarch or significant_addr_bit.
> 

That's true. In fact, I trimmed most of it, as we have a fixed number of 
bits. It now reads:

   /* Clear insignificant bits of a target address and sign extend 
resulting address.  */

>> +  int addr_bit = 56;
>> +  int char_bit = CHAR_BIT;
>> +
>> +  if (addr_bit && (addr_bit < (sizeof (CORE_ADDR) * char_bit)))
> 
> This whole if statement is redundant - all values are fixed.

Removed now.

> 
> 
> Everything else looks good.

Thanks. I'll push the updated version.

> 
>> +    {
>> +      CORE_ADDR sign = (CORE_ADDR) 1 << (addr_bit - 1);
>> +      addr &= ((CORE_ADDR) 1 << addr_bit) - 1;
>> +      addr = (addr ^ sign) - sign;
>> +    }
>> +
>> +  return addr;
>> +}
>> +
>> /* Implementation of linux target ops method "low_stopped_data_address".  */
>>
>> CORE_ADDR
>> @@ -478,6 +502,12 @@ aarch64_target::low_stopped_data_address ()
>>        || (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
>>      return (CORE_ADDR) 0;
>>
>> +  /* Make sure to ignore the top byte, otherwise we may not recognize a
>> +     hardware watchpoint hit.  The stopped data addresses coming from the
>> +     kernel can potentially be tagged addresses.  */
>> +  const CORE_ADDR addr_trap
>> +    = address_significant ((CORE_ADDR) siginfo.si_addr);
>> +
>>    /* Check if the address matches any watched address.  */
>>    state = aarch64_get_debug_reg_state (pid_of (current_thread));
>>    for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
>> @@ -485,7 +515,6 @@ aarch64_target::low_stopped_data_address ()
>>        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_trap = (CORE_ADDR) siginfo.si_addr;
>>        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];
>> -- 
>> 2.25.1
>>
> 

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2020-12-14 13:15 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-11 16:07 [PATCH] [AArch64] Fix TBI handling for watchpoints Luis Machado
2020-12-14 12:52 ` Alan Hayward
2020-12-14 13:15   ` Luis Machado

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).