[aarch64] Fix removal of non-address bits for PAuth
authorLuis Machado <luis.machado@arm.com>
Tue, 24 May 2022 22:31:09 +0000 (23:31 +0100)
committerLuis Machado <luis.machado@arm.com>
Fri, 16 Dec 2022 11:18:32 +0000 (11:18 +0000)
PR gdb/28947

The address_significant gdbarch setting was introduced as a way to remove
non-address bits from pointers, and it is specified by a constant.  This
constant represents the number of address bits in a pointer.

Right now AArch64 is the only architecture that uses it, and 56 was a
correct option so far.

But if we are using Pointer Authentication (PAuth), we might use up to 2 bytes
from the address space to store the required information.  We could also have
cases where we're using both PAuth and MTE.

We could adjust the constant to 48 to cover those cases, but this doesn't
cover the case where GDB needs to sign-extend kernel addresses after removal
of the non-address bits.

This has worked so far because bit 55 is used to select between kernel-space
and user-space addresses.  But trying to clear a range of bits crossing the
bit 55 boundary requires the hook to be smarter.

The following patch renames the gdbarch hook from significant_addr_bit to
remove_non_address_bits and passes a pointer as opposed to the number of
bits.  The hook is now responsible for removing the required non-address bits
and sign-extending the address if needed.

While at it, make GDB and GDBServer share some more code for aarch64 and add a
new arch-specific testcase gdb.arch/aarch64-non-address-bits.exp.

Bug-url: https://sourceware.org/bugzilla/show_bug.cgi?id=28947

Approved-By: Simon Marchi <simon.marchi@efficios.com>
16 files changed:
gdb/aarch64-linux-nat.c
gdb/aarch64-linux-tdep.c
gdb/arch-utils.c
gdb/arch-utils.h
gdb/arch/aarch64.c
gdb/arch/aarch64.h
gdb/breakpoint.c
gdb/gdbarch-components.py
gdb/gdbarch-gen.h
gdb/gdbarch.c
gdb/target.c
gdb/testsuite/gdb.arch/aarch64-non-address-bits.c [new file with mode: 0644]
gdb/testsuite/gdb.arch/aarch64-non-address-bits.exp [new file with mode: 0644]
gdb/utils.c
gdb/utils.h
gdbserver/linux-aarch64-low.cc

index 0f4d0af8af688f7ad6a4f1f1434f468c3f5d0431..03b364a729a10307f6f02e71b0e96806b3c190aa 100644 (file)
@@ -851,7 +851,7 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
      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);
+    = gdbarch_remove_non_address_bits (gdbarch, (CORE_ADDR) siginfo.si_addr);
 
   /* Check if the address matches any watched address.  */
   state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
index 69eb1b030bff361302dba3905903c9cf3e49253d..7e04bd9251f2fcfbe3336ad9c46bf949b3a7ff75 100644 (file)
@@ -1609,7 +1609,7 @@ aarch64_linux_tagged_address_p (struct gdbarch *gdbarch, struct value *address)
   CORE_ADDR addr = value_as_address (address);
 
   /* Remove the top byte for the memory range check.  */
-  addr = address_significant (gdbarch, addr);
+  addr = gdbarch_remove_non_address_bits (gdbarch, addr);
 
   /* Check if the page that contains ADDRESS is mapped with PROT_MTE.  */
   if (!linux_address_in_memtag_page (addr))
@@ -1635,7 +1635,7 @@ aarch64_linux_memtag_matches_p (struct gdbarch *gdbarch,
 
   /* Fetch the allocation tag for ADDRESS.  */
   gdb::optional<CORE_ADDR> atag
-    = aarch64_mte_get_atag (address_significant (gdbarch, addr));
+    = aarch64_mte_get_atag (gdbarch_remove_non_address_bits (gdbarch, addr));
 
   if (!atag.has_value ())
     return true;
@@ -1674,7 +1674,7 @@ aarch64_linux_set_memtags (struct gdbarch *gdbarch, struct value *address,
   else
     {
       /* Remove the top byte.  */
-      addr = address_significant (gdbarch, addr);
+      addr = gdbarch_remove_non_address_bits (gdbarch, addr);
 
       /* Make sure we are dealing with a tagged address to begin with.  */
       if (!aarch64_linux_tagged_address_p (gdbarch, address))
@@ -1731,7 +1731,7 @@ aarch64_linux_get_memtag (struct gdbarch *gdbarch, struct value *address,
        return nullptr;
 
       /* Remove the top byte.  */
-      addr = address_significant (gdbarch, addr);
+      addr = gdbarch_remove_non_address_bits (gdbarch, addr);
       gdb::optional<CORE_ADDR> atag = aarch64_mte_get_atag (addr);
 
       if (!atag.has_value ())
@@ -1805,7 +1805,8 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,
       uiout->text ("\n");
 
       gdb::optional<CORE_ADDR> atag
-       = aarch64_mte_get_atag (address_significant (gdbarch, fault_addr));
+       = aarch64_mte_get_atag (gdbarch_remove_non_address_bits (gdbarch,
+                                                                fault_addr));
       gdb_byte ltag = aarch64_mte_get_ltag (fault_addr);
 
       if (!atag.has_value ())
@@ -1979,6 +1980,40 @@ aarch64_linux_decode_memtag_section (struct gdbarch *gdbarch,
   return tags;
 }
 
+/* AArch64 implementation of the remove_non_address_bits gdbarch hook.  Remove
+   non address bits from a pointer value.  */
+
+static CORE_ADDR
+aarch64_remove_non_address_bits (struct gdbarch *gdbarch, CORE_ADDR pointer)
+{
+  aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
+
+  /* By default, we assume TBI and discard the top 8 bits plus the VA range
+     select bit (55).  */
+  CORE_ADDR mask = AARCH64_TOP_BITS_MASK;
+
+  if (tdep->has_pauth ())
+    {
+      /* Fetch the PAC masks.  These masks are per-process, so we can just
+        fetch data from whatever thread we have at the moment.
+
+        Also, we have both a code mask and a data mask.  For now they are the
+        same, but this may change in the future.  */
+      struct regcache *regs = get_current_regcache ();
+      CORE_ADDR cmask, dmask;
+
+      if (regs->cooked_read (tdep->pauth_reg_base, &dmask) != REG_VALID)
+       dmask = mask;
+
+      if (regs->cooked_read (tdep->pauth_reg_base + 1, &cmask) != REG_VALID)
+       cmask = mask;
+
+      mask |= aarch64_mask_from_pac_registers (cmask, dmask);
+    }
+
+  return aarch64_remove_top_bits (pointer, mask);
+}
+
 static void
 aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
@@ -2034,7 +2069,8 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   /* The top byte of a user space address known as the "tag",
      is ignored by the kernel and can be regarded as additional
      data associated with the address.  */
-  set_gdbarch_significant_addr_bit (gdbarch, 56);
+  set_gdbarch_remove_non_address_bits (gdbarch,
+                                      aarch64_remove_non_address_bits);
 
   /* MTE-specific settings and hooks.  */
   if (tdep->has_mte ())
index 60ffdc5e16abe30252880d14ebc569ae30e3d2f1..f84bf8781ba21019e3825bf846469e6c289ce688 100644 (file)
@@ -82,6 +82,14 @@ legacy_register_sim_regno (struct gdbarch *gdbarch, int regnum)
     return LEGACY_SIM_REGNO_IGNORE;
 }
 
+/* See arch-utils.h */
+
+CORE_ADDR
+default_remove_non_address_bits (struct gdbarch *gdbarch, CORE_ADDR pointer)
+{
+  /* By default, just return the pointer value.  */
+  return pointer;
+}
 
 /* See arch-utils.h */
 
index 46018a6fbbb610a11eb88782f86fd7d8946ba8cd..76c9dcea44743072aad8625d56c6e0d21efba997 100644 (file)
@@ -132,6 +132,10 @@ extern const struct floatformat **
   default_floatformat_for_type (struct gdbarch *gdbarch,
                                const char *name, int len);
 
+/* Default implementation of gdbarch_remove_non_address_bits.  */
+CORE_ADDR default_remove_non_address_bits (struct gdbarch *gdbarch,
+                                          CORE_ADDR pointer);
+
 /* Default implementation of gdbarch_memtag_to_string.  */
 extern std::string default_memtag_to_string (struct gdbarch *gdbarch,
                                             struct value *tag);
index 565c5e7a81cef3fe2d56a3f8d1bbb0c23cf16290..0df4140e4e5e317212b15499ed84acde2c79d341 100644 (file)
@@ -59,3 +59,34 @@ aarch64_create_target_description (const aarch64_features &features)
 
   return tdesc.release ();
 }
+
+/* See arch/aarch64.h.  */
+
+CORE_ADDR
+aarch64_remove_top_bits (CORE_ADDR pointer, CORE_ADDR mask)
+{
+  /* The VA range select bit is 55.  This bit tells us if we have a
+     kernel-space address or a user-space address.  */
+  bool kernel_address = (pointer & VA_RANGE_SELECT_BIT_MASK) != 0;
+
+  /* Remove the top non-address bits.  */
+  pointer &= ~mask;
+
+  /* Sign-extend if we have a kernel-space address.  */
+  if (kernel_address)
+    pointer |= mask;
+
+  return pointer;
+}
+
+/* See arch/aarch64.h.  */
+
+CORE_ADDR
+aarch64_mask_from_pac_registers (const CORE_ADDR cmask, const CORE_ADDR dmask)
+{
+  /* If the masks differ, default to using the one with the most coverage.  */
+  if (dmask != cmask)
+    return dmask > cmask ? dmask : cmask;
+
+  return cmask;
+}
index b1a6ce3ef0eebf878bcaa359220b509f3de3bd1d..68048198b4d2e144bd35e92fb10f49e1af1396f6 100644 (file)
@@ -71,6 +71,17 @@ namespace std
 target_desc *
   aarch64_create_target_description (const aarch64_features &features);
 
+/* Given a pointer value POINTER and a MASK of non-address bits, remove the
+   non-address bits from the pointer and sign-extend the result if required.
+   The sign-extension is required so we can handle kernel addresses
+   correctly.  */
+CORE_ADDR aarch64_remove_top_bits (CORE_ADDR pointer, CORE_ADDR mask);
+
+/* Given CMASK and DMASK the two PAC mask registers, return the correct PAC
+   mask to use for removing non-address bits from a pointer.  */
+CORE_ADDR
+aarch64_mask_from_pac_registers (const CORE_ADDR cmask, const CORE_ADDR dmask);
+
 /* Register numbers of various important registers.
    Note that on SVE, the Z registers reuse the V register numbers and the V
    registers become pseudo registers.  */
@@ -104,6 +115,13 @@ enum aarch64_regnum
 #define AARCH64_TLS_REGISTER_SIZE 8
 #define V_REGISTER_SIZE          16
 
+/* PAC-related constants.  */
+/* Bit 55 is used to select between a kernel-space and user-space address.  */
+#define VA_RANGE_SELECT_BIT_MASK  0x80000000000000ULL
+/* Mask with 1's in bits 55~63, used to remove the top byte of pointers
+   (Top Byte Ignore).  */
+#define AARCH64_TOP_BITS_MASK    0xff80000000000000ULL
+
 /* Pseudo register base numbers.  */
 #define AARCH64_Q0_REGNUM 0
 #define AARCH64_D0_REGNUM (AARCH64_Q0_REGNUM + AARCH64_D_REGISTER_COUNT)
index 7a245b6fb5f9559fc8a6a17571cc08a5a1d41b78..c4384e56a410db48424acb7507dc44401abb1875 100644 (file)
@@ -2120,7 +2120,8 @@ update_watchpoint (struct watchpoint *b, bool reparse)
                  loc->gdbarch = value_type (v)->arch ();
 
                  loc->pspace = frame_pspace;
-                 loc->address = address_significant (loc->gdbarch, addr);
+                 loc->address
+                   = gdbarch_remove_non_address_bits (loc->gdbarch, addr);
 
                  if (bitsize != 0)
                    {
@@ -7303,7 +7304,8 @@ adjust_breakpoint_address (struct gdbarch *gdbarch,
            = gdbarch_adjust_breakpoint_address (gdbarch, bpaddr);
        }
 
-      adjusted_bpaddr = address_significant (gdbarch, adjusted_bpaddr);
+      adjusted_bpaddr
+       = gdbarch_remove_non_address_bits (gdbarch, adjusted_bpaddr);
 
       /* An adjusted breakpoint address can significantly alter
         a user's expectations.  Print a warning if an adjustment
index e7230949aad574c8b2e062783b5943c0ce5cc44e..47a9413ee11e37cfda47387cbdd0a157352d2e55 100644 (file)
@@ -1154,15 +1154,23 @@ possible it should be in TARGET_READ_PC instead).
     invalid=False,
 )
 
-Value(
+Method(
     comment="""
-On some machines, not all bits of an address word are significant.
-For example, on AArch64, the top bits of an address known as the "tag"
-are ignored by the kernel, the hardware, etc. and can be regarded as
-additional data associated with the address.
+On some architectures, not all bits of a pointer are significant.
+On AArch64, for example, the top bits of a pointer may carry a "tag", which
+can be ignored by the kernel and the hardware.  The "tag" can be regarded as
+additional data associated with the pointer, but it is not part of the address.
+
+Given a pointer for the architecture, this hook removes all the
+non-significant bits and sign-extends things as needed.  It gets used to remove
+non-address bits from data pointers (for example, removing the AArch64 MTE tag
+bits from a pointer) and from code pointers (removing the AArch64 PAC signature
+from a pointer containing the return address).
 """,
-    type="int",
-    name="significant_addr_bit",
+    type="CORE_ADDR",
+    name="remove_non_address_bits",
+    params=[("CORE_ADDR", "pointer")],
+    predefault="default_remove_non_address_bits",
     invalid=False,
 )
 
index a663316df16628460a57f267824ee44670a71de3..5918de517ef29f74c2685431f2c1c2ed59614f59 100644 (file)
@@ -626,13 +626,20 @@ typedef CORE_ADDR (gdbarch_addr_bits_remove_ftype) (struct gdbarch *gdbarch, COR
 extern CORE_ADDR gdbarch_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR addr);
 extern void set_gdbarch_addr_bits_remove (struct gdbarch *gdbarch, gdbarch_addr_bits_remove_ftype *addr_bits_remove);
 
-/* On some machines, not all bits of an address word are significant.
-   For example, on AArch64, the top bits of an address known as the "tag"
-   are ignored by the kernel, the hardware, etc. and can be regarded as
-   additional data associated with the address. */
-
-extern int gdbarch_significant_addr_bit (struct gdbarch *gdbarch);
-extern void set_gdbarch_significant_addr_bit (struct gdbarch *gdbarch, int significant_addr_bit);
+/* On some architectures, not all bits of a pointer are significant.
+   On AArch64, for example, the top bits of a pointer may carry a "tag", which
+   can be ignored by the kernel and the hardware.  The "tag" can be regarded as
+   additional data associated with the pointer, but it is not part of the address.
+
+   Given a pointer for the architecture, this hook removes all the
+   non-significant bits and sign-extends things as needed.  It gets used to remove
+   non-address bits from data pointers (for example, removing the AArch64 MTE tag
+   bits from a pointer) and from code pointers (removing the AArch64 PAC signature
+   from a pointer containing the return address). */
+
+typedef CORE_ADDR (gdbarch_remove_non_address_bits_ftype) (struct gdbarch *gdbarch, CORE_ADDR pointer);
+extern CORE_ADDR gdbarch_remove_non_address_bits (struct gdbarch *gdbarch, CORE_ADDR pointer);
+extern void set_gdbarch_remove_non_address_bits (struct gdbarch *gdbarch, gdbarch_remove_non_address_bits_ftype *remove_non_address_bits);
 
 /* Return a string representation of the memory tag TAG. */
 
index 74c12c5e3ff6d446d628385aea0b40c4b681c953..2d4b1164e2051143efc455c044443f20f527883b 100644 (file)
@@ -143,7 +143,7 @@ struct gdbarch
   int frame_red_zone_size = 0;
   gdbarch_convert_from_func_ptr_addr_ftype *convert_from_func_ptr_addr = convert_from_func_ptr_addr_identity;
   gdbarch_addr_bits_remove_ftype *addr_bits_remove = core_addr_identity;
-  int significant_addr_bit = 0;
+  gdbarch_remove_non_address_bits_ftype *remove_non_address_bits = default_remove_non_address_bits;
   gdbarch_memtag_to_string_ftype *memtag_to_string = default_memtag_to_string;
   gdbarch_tagged_address_p_ftype *tagged_address_p = default_tagged_address_p;
   gdbarch_memtag_matches_p_ftype *memtag_matches_p = default_memtag_matches_p;
@@ -400,7 +400,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of frame_red_zone_size, invalid_p == 0 */
   /* Skip verify of convert_from_func_ptr_addr, invalid_p == 0 */
   /* Skip verify of addr_bits_remove, invalid_p == 0 */
-  /* Skip verify of significant_addr_bit, invalid_p == 0 */
+  /* Skip verify of remove_non_address_bits, invalid_p == 0 */
   /* Skip verify of memtag_to_string, invalid_p == 0 */
   /* Skip verify of tagged_address_p, invalid_p == 0 */
   /* Skip verify of memtag_matches_p, invalid_p == 0 */
@@ -885,8 +885,8 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
              "gdbarch_dump: addr_bits_remove = <%s>\n",
              host_address_to_string (gdbarch->addr_bits_remove));
   gdb_printf (file,
-             "gdbarch_dump: significant_addr_bit = %s\n",
-             plongest (gdbarch->significant_addr_bit));
+             "gdbarch_dump: remove_non_address_bits = <%s>\n",
+             host_address_to_string (gdbarch->remove_non_address_bits));
   gdb_printf (file,
              "gdbarch_dump: memtag_to_string = <%s>\n",
              host_address_to_string (gdbarch->memtag_to_string));
@@ -3100,21 +3100,21 @@ set_gdbarch_addr_bits_remove (struct gdbarch *gdbarch,
   gdbarch->addr_bits_remove = addr_bits_remove;
 }
 
-int
-gdbarch_significant_addr_bit (struct gdbarch *gdbarch)
+CORE_ADDR
+gdbarch_remove_non_address_bits (struct gdbarch *gdbarch, CORE_ADDR pointer)
 {
   gdb_assert (gdbarch != NULL);
-  /* Skip verify of significant_addr_bit, invalid_p == 0 */
+  gdb_assert (gdbarch->remove_non_address_bits != NULL);
   if (gdbarch_debug >= 2)
-    gdb_printf (gdb_stdlog, "gdbarch_significant_addr_bit called\n");
-  return gdbarch->significant_addr_bit;
+    gdb_printf (gdb_stdlog, "gdbarch_remove_non_address_bits called\n");
+  return gdbarch->remove_non_address_bits (gdbarch, pointer);
 }
 
 void
-set_gdbarch_significant_addr_bit (struct gdbarch *gdbarch,
-                                 int significant_addr_bit)
+set_gdbarch_remove_non_address_bits (struct gdbarch *gdbarch,
+                                    gdbarch_remove_non_address_bits_ftype remove_non_address_bits)
 {
-  gdbarch->significant_addr_bit = significant_addr_bit;
+  gdbarch->remove_non_address_bits = remove_non_address_bits;
 }
 
 std::string
index b7cd3b9b454d997ae1531b16f0268b6270d0bc7e..8a2daa000bc80c11be6c7d3da2e9b37145d194cc 100644 (file)
@@ -1617,7 +1617,7 @@ memory_xfer_partial (struct target_ops *ops, enum target_object object,
   if (len == 0)
     return TARGET_XFER_EOF;
 
-  memaddr = address_significant (target_gdbarch (), memaddr);
+  memaddr = gdbarch_remove_non_address_bits (target_gdbarch (), memaddr);
 
   /* Fill in READBUF with breakpoint shadows, or WRITEBUF with
      breakpoint insns, thus hiding out from higher layers whether
diff --git a/gdb/testsuite/gdb.arch/aarch64-non-address-bits.c b/gdb/testsuite/gdb.arch/aarch64-non-address-bits.c
new file mode 100644 (file)
index 0000000..3cf6d63
--- /dev/null
@@ -0,0 +1,25 @@
+/* This file is part of GDB, the GNU debugger.
+
+   Copyright 2022 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+static long l = 0;
+static long *l_ptr = &l;
+
+int
+main (int argc, char **argv)
+{
+  return *l_ptr;
+}
diff --git a/gdb/testsuite/gdb.arch/aarch64-non-address-bits.exp b/gdb/testsuite/gdb.arch/aarch64-non-address-bits.exp
new file mode 100644 (file)
index 0000000..9269d38
--- /dev/null
@@ -0,0 +1,118 @@
+# Copyright 2022 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# This file is part of the gdb testsuite.
+#
+# Test that GDB for AArch64/Linux can properly handle pointers with
+# the upper 16 bits (PAC) or 8 bits (Tag) set, as well as the
+# VA_RANGE_SELECT bit (55).
+
+if {![is_aarch64_target]} {
+    verbose "Skipping ${gdb_test_file_name}."
+    return
+}
+
+standard_testfile
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# We need to iterate over two distinct ranges, separated by a single bit.
+# This bit is 55 (VA_RANGE_SELECT) which tells us if we have a kernel-space
+# address or a user-space address.
+
+# The tag field has 8 bits.
+set tag_bits_count 8
+
+# The pac field has 7 bits.
+set pac_bits_count 7
+
+# A couple patterns that we reuse for the tests later.  One is for a successful
+# memory read and the other is for a memory read failure.
+set memory_read_ok_pattern "$::hex\( <l>\)?:\[ \t\]+$::hex"
+set memory_read_fail_pattern "$::hex:\[ \t\]+Cannot access memory at address $::hex"
+
+set pac_enabled 0
+
+# Check if PAC is enabled.
+gdb_test_multiple "ptype \$pauth_cmask" "fetch PAC cmask" {
+    -re -wrap "type = long" {
+       set pac_enabled 1
+    }
+    -re -wrap "type = void" {
+    }
+    -re ".*$gdb_prompt $" {
+       fail $gdb_test_name
+       return 1
+    }
+}
+
+# Value of the cmask register.
+set cmask 0
+
+# If there are PAC registers, GDB uses those to unmask the PAC bits.
+if {$pac_enabled} {
+    set cmask [get_valueof "" "\$pauth_cmask >> 48" "0" "fetch PAC cmask"]
+}
+
+# Cycle through the tag and pac bit ranges and check how GDB
+# behaves when trying to access these addresses.
+foreach_with_prefix upper_bits {"0x0" "0x1" "0x2" "0x4" "0x8" "0x10" "0x20" "0x40" "0x80"} {
+    foreach_with_prefix lower_bits {"0x0" "0x1" "0x2" "0x4" "0x8" "0x10" "0x20" "0x40"} {
+
+       # A successful memory read pattern
+       set pattern $memory_read_ok_pattern
+
+       if {!$pac_enabled} {
+           # If PAC is not supported, memory reads will fail if
+           # lower_bits != 0x0
+           if {$lower_bits != "0x0"} {
+               set pattern $memory_read_fail_pattern
+           }
+       } else {
+           # Otherwise, figure out if the memory read will succeed or not by
+           # checking cmask.
+           gdb_test_multiple "p/x (~${cmask}ULL & (${lower_bits}ULL))" "" {
+               -re -wrap "= 0x0" {
+                   # Either cmask is 0x7F or lower_bits is 0x0.
+                   # Either way, the memory read should succeed.
+               }
+               -re -wrap "= $::hex" {
+                   if {$lower_bits != "0x0"} {
+                       # cmask doesn't mask off all the PAC bits, which
+                       # results in a memory read failure, with the actual
+                       # address being accessed differing from the one we
+                       # passed.
+                       set pattern $memory_read_fail_pattern
+                   }
+               }
+           }
+       }
+
+       # Test without the VA_RANGE_SELECT bit set.
+       gdb_test "x/gx ((unsigned long) l_ptr | ((${upper_bits}ULL << 56) | (${lower_bits}ULL << 48)))" \
+           $pattern \
+           "user-space memory access"
+
+       # Now test with the VA_RANGE_SELECT bit set.
+       gdb_test "x/gx ((unsigned long) l_ptr | ((${upper_bits}ULL << 56) | (${lower_bits}ULL << 48) | (1ULL << 55))) " \
+           $memory_read_fail_pattern \
+           "kernel-space memory access"
+    }
+}
index 74917f25ab903b82a9a9d38afe161e9e85a06f25..4a715b945f6b2a5c98d5a21cee7e82e0dc72a806 100644 (file)
@@ -3110,28 +3110,6 @@ show_debug_timestamp (struct ui_file *file, int from_tty,
 }
 \f
 
-/* See utils.h.  */
-
-CORE_ADDR
-address_significant (gdbarch *gdbarch, 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 = gdbarch_significant_addr_bit (gdbarch);
-
-  if (addr_bit && (addr_bit < (sizeof (CORE_ADDR) * HOST_CHAR_BIT)))
-    {
-      CORE_ADDR sign = (CORE_ADDR) 1 << (addr_bit - 1);
-      addr &= ((CORE_ADDR) 1 << addr_bit) - 1;
-      addr = (addr ^ sign) - sign;
-    }
-
-  return addr;
-}
-
 const char *
 paddress (struct gdbarch *gdbarch, CORE_ADDR addr)
 {
index 509361dc42955435bed16330f7df549a57f9fe49..59340f368cd2d02023f3da96f1473b49addd6815 100644 (file)
@@ -264,9 +264,6 @@ extern void fputs_styled (const char *linebuffer,
 extern void fputs_highlighted (const char *str, const compiled_regex &highlight,
                               struct ui_file *stream);
 
-/* Return the address only having significant bits.  */
-extern CORE_ADDR address_significant (gdbarch *gdbarch, CORE_ADDR addr);
-
 /* Convert CORE_ADDR to string in platform-specific manner.
    This is usually formatted similar to 0x%lx.  */
 extern const char *paddress (struct gdbarch *gdbarch, CORE_ADDR addr);
index b657a265ee7c6c529ce2af0fc976f315fc0e97cc..6f44bc6b3504d5e2c2aedf5828528dd66b800a25 100644 (file)
@@ -522,21 +522,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)
+aarch64_remove_non_address_bits (CORE_ADDR pointer)
 {
-  /* Clear insignificant bits of a target address and sign extend resulting
-     address.  */
-  int addr_bit = 56;
+  /* By default, we assume TBI and discard the top 8 bits plus the
+     VA range select bit (55).  */
+  CORE_ADDR mask = AARCH64_TOP_BITS_MASK;
+
+  /* Check if PAC is available for this target.  */
+  if (tdesc_contains_feature (current_process ()->tdesc,
+                             "org.gnu.gdb.aarch64.pauth"))
+    {
+      /* Fetch the PAC masks.  These masks are per-process, so we can just
+        fetch data from whatever thread we have at the moment.
 
-  CORE_ADDR sign = (CORE_ADDR) 1 << (addr_bit - 1);
-  addr &= ((CORE_ADDR) 1 << addr_bit) - 1;
-  addr = (addr ^ sign) - sign;
+        Also, we have both a code mask and a data mask.  For now they are the
+        same, but this may change in the future.  */
+
+      struct regcache *regs = get_thread_regcache (current_thread, 1);
+      CORE_ADDR dmask = regcache_raw_get_unsigned_by_name (regs, "pauth_dmask");
+      CORE_ADDR cmask = regcache_raw_get_unsigned_by_name (regs, "pauth_cmask");
+      mask |= aarch64_mask_from_pac_registers (cmask, dmask);
+    }
 
-  return addr;
+  return aarch64_remove_top_bits (pointer, mask);
 }
 
 /* Implementation of linux target ops method "low_stopped_data_address".  */
@@ -563,7 +572,7 @@ aarch64_target::low_stopped_data_address ()
      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);
+    = aarch64_remove_non_address_bits ((CORE_ADDR) siginfo.si_addr);
 
   /* Check if the address matches any watched address.  */
   state = aarch64_get_debug_reg_state (pid_of (current_thread));