Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Luis Machado <luis.machado.foss@gmail.com>
To: gdb-patches@sourceware.org
Cc: thiago.bauermann@linaro.org
Subject: [PATCH 1/2] gdb, aarch64: cache pointer authentication masks per inferior
Date: Sat, 12 Sep 2026 23:20:10 +0100	[thread overview]
Message-ID: <20260912222011.2395686-2-luis.machado.foss@gmail.com> (raw)
In-Reply-To: <20260912222011.2395686-1-luis.machado.foss@gmail.com>

aarch64_remove_non_address_bits recomputed the pointer authentication
masks on every call by walking the current inferior's thread list,
looking up its regcache, and reading the dmask/cmask registers from
the target. This function is called from memory_xfer_partial for
every memory transfer GDB performs, as well as from the watchpoint
and breakpoint address-masking hooks, so the lookup happens far more
often than the masks can possibly change.

The masks are fixed for the life of a process, since they reflect the
kernel's VA-size configuration at exec time, and are shared by all of
a process' threads. Cache the computed mask per inferior (one slot
each for the low and high VA ranges) instead of recomputing it on
every call, only populating the cache when a thread is actually
stopped so a transient "thread running" state never gets cached as a
permanent answer. The cache is invalidated on inferior exit,
inferior appeared, and exec, since those are the only points where a
process' mask configuration could legitimately change.
---
 gdb/aarch64-tdep.c | 154 ++++++++++++++++++++++++++++++++-------------
 1 file changed, 112 insertions(+), 42 deletions(-)

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 950ad4f6aae..c7003687eab 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -57,6 +57,9 @@
 
 /* For inferior_ptid and current_inferior ().  */
 #include "inferior.h"
+/* For gdb::observers::inferior_exit et al, used to invalidate the pauth
+   mask cache.  */
+#include "observable.h"
 /* For std::sqrt and std::pow.  */
 #include <cmath>
 
@@ -4333,6 +4336,47 @@ aarch64_memtag_to_string (struct gdbarch *gdbarch, struct value *tag_value)
   return string_printf ("0x%s", phex_nz (tag));
 }
 
+/* Cached pointer authentication masks for an inferior.  The masks are
+   fixed for the life of a process (they reflect the kernel's VA-size
+   configuration at exec time) and are shared by all its threads.  We only
+   need to compute them once per inferior instead of on every call to
+   aarch64_remove_non_address_bits.  LOW is used for user-space (low VA
+   range) pointers, HIGH is used for kernel-space (high VA range)
+   pointers.  HIGH is only ever populated on targets that provide the
+   high-range mask registers.  */
+
+struct aarch64_pauth_mask_cache
+{
+  std::optional<CORE_ADDR> low;
+  std::optional<CORE_ADDR> high;
+};
+
+/* Per-inferior pauth mask cache.  */
+
+static const registry<inferior>::key<aarch64_pauth_mask_cache>
+  aarch64_pauth_mask_cache_data;
+
+/* Drop INF's cached pauth masks.  */
+
+static void
+aarch64_invalidate_pauth_mask_cache (inferior *inf)
+{
+  aarch64_pauth_mask_cache_data.clear (inf);
+}
+
+/* Drop the pauth mask cache of every inferior sharing PSPACE.  This is
+   attached to the all_objfiles_removed observer, which fires on exec.
+   Exec is the only point after startup where a process' VA-size
+   configuration, and hence its masks, could legitimately change.  */
+
+static void
+aarch64_pauth_mask_cache_objfiles_removed (program_space *pspace)
+{
+  for (inferior *inf : all_inferiors ())
+    if (inf->pspace == pspace)
+      aarch64_invalidate_pauth_mask_cache (inf);
+}
+
 /* See aarch64-tdep.h.  */
 
 CORE_ADDR
@@ -4353,54 +4397,72 @@ aarch64_remove_non_address_bits (struct gdbarch *gdbarch, CORE_ADDR pointer)
      momentarily), we use the inferior ptid.  */
   if (inferior_ptid != null_ptid)
     {
-      /* If we do have an inferior, attempt to fetch its thread's thread_info
-	 struct.  */
-      thread_info *thread = current_inferior ()->find_thread (inferior_ptid);
+      inferior *inf = current_inferior ();
+      bool kernel_address = (pointer & VA_RANGE_SELECT_BIT_MASK) != 0;
 
-      /* If the thread is running, we will not be able to fetch the mask
-	 registers.  */
-      if (thread != nullptr && thread->state () != THREAD_RUNNING)
-	{
-	  /* Otherwise, fetch the register cache and the masks.  */
-	  struct regcache *regs
-	    = get_thread_regcache (current_inferior ()->process_target (),
-				   inferior_ptid);
-
-	  /* Use the gdbarch from the register cache to check for pointer
-	     authentication support, as it matches the features found in
-	     that particular thread.  */
-	  aarch64_gdbarch_tdep *tdep
-	    = gdbarch_tdep<aarch64_gdbarch_tdep> (regs->arch ());
+      aarch64_pauth_mask_cache *cache
+	= aarch64_pauth_mask_cache_data.get (inf);
+      if (cache == nullptr)
+	cache = &aarch64_pauth_mask_cache_data.emplace (inf);
 
-	  /* Is there pointer authentication support?  */
-	  if (tdep->has_pauth ())
+      std::optional<CORE_ADDR> &cached_mask
+	= kernel_address ? cache->high : cache->low;
+
+      if (cached_mask.has_value ())
+	mask = *cached_mask;
+      else
+	{
+	  /* If we do have an inferior, attempt to fetch its thread's
+	     thread_info struct.  */
+	  thread_info *thread = inf->find_thread (inferior_ptid);
+
+	  /* If the thread is running, we will not be able to fetch the mask
+	     registers.  Leave the cache empty for this slot, we'll get
+	     another chance to compute and cache it once some thread of
+	     this inferior is next stopped.  */
+	  if (thread != nullptr && thread->state () != THREAD_RUNNING)
 	    {
-	      CORE_ADDR cmask, dmask;
-	      int dmask_regnum
-		= AARCH64_PAUTH_DMASK_REGNUM (tdep->pauth_reg_base);
-	      int cmask_regnum
-		= AARCH64_PAUTH_CMASK_REGNUM (tdep->pauth_reg_base);
-
-	      /* If we have a kernel address and we have kernel-mode address
-		 mask registers, use those instead.  */
-	      if (tdep->pauth_reg_count > 2
-		  && pointer & VA_RANGE_SELECT_BIT_MASK)
+	      /* Otherwise, fetch the register cache and the masks.  */
+	      struct regcache *regs
+		= get_thread_regcache (inf->process_target (), inferior_ptid);
+
+	      /* Use the gdbarch from the register cache to check for pointer
+		 authentication support, as it matches the features found in
+		 that particular thread.  */
+	      aarch64_gdbarch_tdep *tdep
+		= gdbarch_tdep<aarch64_gdbarch_tdep> (regs->arch ());
+
+	      /* Is there pointer authentication support?  */
+	      if (tdep->has_pauth ())
 		{
-		  dmask_regnum
-		    = AARCH64_PAUTH_DMASK_HIGH_REGNUM (tdep->pauth_reg_base);
-		  cmask_regnum
-		    = AARCH64_PAUTH_CMASK_HIGH_REGNUM (tdep->pauth_reg_base);
+		  CORE_ADDR cmask, dmask;
+		  int dmask_regnum
+		    = AARCH64_PAUTH_DMASK_REGNUM (tdep->pauth_reg_base);
+		  int cmask_regnum
+		    = AARCH64_PAUTH_CMASK_REGNUM (tdep->pauth_reg_base);
+
+		  /* If we have a kernel address and we have kernel-mode
+		     address mask registers, use those instead.  */
+		  if (tdep->pauth_reg_count > 2 && kernel_address)
+		    {
+		      dmask_regnum
+			= AARCH64_PAUTH_DMASK_HIGH_REGNUM (tdep->pauth_reg_base);
+		      cmask_regnum
+			= AARCH64_PAUTH_CMASK_HIGH_REGNUM (tdep->pauth_reg_base);
+		    }
+
+		  /* We have both a code mask and a data mask.  For now they
+		     are the same, but this may change in the future.  */
+		  if (regs->cooked_read (dmask_regnum, &dmask) != REG_VALID)
+		    dmask = mask;
+
+		  if (regs->cooked_read (cmask_regnum, &cmask) != REG_VALID)
+		    cmask = mask;
+
+		  mask |= aarch64_mask_from_pac_registers (cmask, dmask);
 		}
 
-	      /* We have both a code mask and a data mask.  For now they are
-		 the same, but this may change in the future.  */
-	      if (regs->cooked_read (dmask_regnum, &dmask) != REG_VALID)
-		dmask = mask;
-
-	      if (regs->cooked_read (cmask_regnum, &cmask) != REG_VALID)
-		cmask = mask;
-
-	      mask |= aarch64_mask_from_pac_registers (cmask, dmask);
+	      cached_mask = mask;
 	    }
 	}
     }
@@ -5076,6 +5138,14 @@ INIT_GDB_FILE (aarch64_tdep)
   gdbarch_register (bfd_arch_aarch64, aarch64_gdbarch_init,
 		    aarch64_dump_tdep);
 
+  /* Keep the pauth mask cache in sync with the inferiors it describes.  */
+  gdb::observers::inferior_exit.attach
+    (aarch64_invalidate_pauth_mask_cache, "aarch64-tdep");
+  gdb::observers::inferior_appeared.attach
+    (aarch64_invalidate_pauth_mask_cache, "aarch64-tdep");
+  gdb::observers::all_objfiles_removed.attach
+    (aarch64_pauth_mask_cache_objfiles_removed, "aarch64-tdep");
+
   /* Debug this file's internals.  */
   add_setshow_boolean_cmd ("aarch64", class_maintenance, &aarch64_debug, _("\
 Set AArch64 debugging."), _("\
-- 
2.43.0


  reply	other threads:[~2026-09-12 22:21 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-12 22:20 [PATCH 0/2] " Luis Machado
2026-09-12 22:20 ` Luis Machado [this message]
2026-09-14 15:24   ` [PATCH 1/2] " Simon Marchi
2026-09-12 22:20 ` [PATCH 2/2] gdb, aarch64: add selftest for the pauth mask cache Luis Machado

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260912222011.2395686-2-luis.machado.foss@gmail.com \
    --to=luis.machado.foss@gmail.com \
    --cc=gdb-patches@sourceware.org \
    --cc=thiago.bauermann@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox