]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
GDB, gdbserver: aarch64-linux: Initial Guarded Control Stack support
authorThiago Jung Bauermann <thiago.bauermann@linaro.org>
Thu, 1 May 2025 21:12:19 +0000 (18:12 -0300)
committerThiago Jung Bauermann <thiago.bauermann@linaro.org>
Mon, 9 Jun 2025 19:36:07 +0000 (16:36 -0300)
Add the org.gnu.gdb.aarch64.gcs feature with the GCSPR register, and the
org.gnu.gdb.aarch64.gcs.linux feature with "registers" to represent the
Linux kernel ptrace and prctl knobs that enable and lock specific GCS
functionality.

14 files changed:
gdb/aarch64-linux-nat.c
gdb/aarch64-linux-tdep.c
gdb/aarch64-tdep.c
gdb/aarch64-tdep.h
gdb/arch/aarch64-gcs-linux.h [new file with mode: 0644]
gdb/arch/aarch64.c
gdb/arch/aarch64.h
gdb/doc/gdb.texinfo
gdb/features/Makefile
gdb/features/aarch64-gcs-linux.c [new file with mode: 0644]
gdb/features/aarch64-gcs-linux.xml [new file with mode: 0644]
gdb/features/aarch64-gcs.c [new file with mode: 0644]
gdb/features/aarch64-gcs.xml [new file with mode: 0644]
gdbserver/linux-aarch64-low.cc

index d7869f42e82520aa5cbcc109916aeaefe0f457f4..0796b1e47955931144c4e639d2f17f2022534781 100644 (file)
@@ -51,6 +51,7 @@
 #include "gdb_proc_service.h"
 #include "arch-utils.h"
 
+#include "arch/aarch64-gcs-linux.h"
 #include "arch/aarch64-mte-linux.h"
 
 #include "nat/aarch64-mte-linux-ptrace.h"
@@ -542,6 +543,67 @@ store_tlsregs_to_thread (struct regcache *regcache)
     perror_with_name (_("unable to store TLS register"));
 }
 
+/* Fill GDB's register array with the GCS register values from
+   the current thread.  */
+
+static void
+fetch_gcsregs_from_thread (struct regcache *regcache)
+{
+  aarch64_gdbarch_tdep *tdep
+    = gdbarch_tdep<aarch64_gdbarch_tdep> (regcache->arch ());
+
+  gdb_assert (tdep->gcs_reg_base != -1);
+  gdb_assert (tdep->gcs_linux_reg_base != -1);
+
+  struct user_gcs user_gcs;
+  struct iovec iovec;
+
+  iovec.iov_base = &user_gcs;
+  iovec.iov_len = sizeof (user_gcs);
+
+  int tid = get_ptrace_pid (regcache->ptid ());
+  if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_GCS, &iovec) != 0)
+      perror_with_name (_("unable to fetch GCS registers"));
+
+  regcache->raw_supply (tdep->gcs_reg_base, &user_gcs.gcspr_el0);
+  regcache->raw_supply (tdep->gcs_linux_reg_base, &user_gcs.features_enabled);
+  regcache->raw_supply (tdep->gcs_linux_reg_base + 1,
+                       &user_gcs.features_locked);
+}
+
+/* Store to the current thread the valid GCS register set in the GDB's
+   register array.  */
+
+static void
+store_gcsregs_to_thread (struct regcache *regcache)
+{
+  aarch64_gdbarch_tdep *tdep
+    = gdbarch_tdep<aarch64_gdbarch_tdep> (regcache->arch ());
+
+  gdb_assert (tdep->gcs_reg_base != -1);
+  gdb_assert (tdep->gcs_linux_reg_base != -1);
+
+  if (REG_VALID != regcache->get_register_status (tdep->gcs_reg_base)
+      || REG_VALID != regcache->get_register_status (tdep->gcs_linux_reg_base)
+      || REG_VALID
+            != regcache->get_register_status (tdep->gcs_linux_reg_base + 1))
+    return;
+
+  struct user_gcs user_gcs;
+  regcache->raw_collect (tdep->gcs_reg_base, &user_gcs.gcspr_el0);
+  regcache->raw_collect (tdep->gcs_linux_reg_base, &user_gcs.features_enabled);
+  regcache->raw_collect (tdep->gcs_linux_reg_base + 1,
+                        &user_gcs.features_locked);
+
+  struct iovec iovec;
+  iovec.iov_base = &user_gcs;
+  iovec.iov_len = sizeof (user_gcs);
+
+  int tid = get_ptrace_pid (regcache->ptid ());
+  if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_GCS, &iovec) != 0)
+    perror_with_name (_("unable to store GCS registers"));
+}
+
 /* The AArch64 version of the "fetch_registers" target_ops method.  Fetch
    REGNO from the target and place the result into REGCACHE.  */
 
@@ -577,6 +639,9 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
 
       if (tdep->has_sme2 ())
        fetch_zt_from_thread (regcache);
+
+      if (tdep->has_gcs ())
+       fetch_gcsregs_from_thread (regcache);
     }
   /* General purpose register?  */
   else if (regno < AARCH64_V0_REGNUM)
@@ -609,6 +674,11 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
           && regno >= tdep->tls_regnum_base
           && regno < tdep->tls_regnum_base + tdep->tls_register_count)
     fetch_tlsregs_from_thread (regcache);
+  /* GCS register?  */
+  else if (tdep->has_gcs ()
+          && (regno == tdep->gcs_reg_base || regno == tdep->gcs_linux_reg_base
+              || regno == tdep->gcs_linux_reg_base + 1))
+    fetch_gcsregs_from_thread (regcache);
 }
 
 /* A version of the "fetch_registers" target_ops method used when running
@@ -680,6 +750,9 @@ aarch64_store_registers (struct regcache *regcache, int regno)
 
       if (tdep->has_sme2 ())
        store_zt_to_thread (regcache);
+
+      if (tdep->has_gcs ())
+       store_gcsregs_to_thread (regcache);
     }
   /* General purpose register?  */
   else if (regno < AARCH64_V0_REGNUM)
@@ -706,6 +779,11 @@ aarch64_store_registers (struct regcache *regcache, int regno)
           && regno >= tdep->tls_regnum_base
           && regno < tdep->tls_regnum_base + tdep->tls_register_count)
     store_tlsregs_to_thread (regcache);
+  /* GCS register?  */
+  else if (tdep->has_gcs ()
+          && (regno == tdep->gcs_reg_base || regno == tdep->gcs_linux_reg_base
+              || regno == tdep->gcs_linux_reg_base + 1))
+    store_gcsregs_to_thread (regcache);
 
   /* PAuth registers are read-only.  */
 }
@@ -881,6 +959,7 @@ aarch64_linux_nat_target::read_description ()
      active or not.  */
   features.vq = aarch64_sve_get_vq (tid);
   features.pauth = hwcap & AARCH64_HWCAP_PACA;
+  features.gcs = features.gcs_linux = hwcap & HWCAP_GCS;
   features.mte = hwcap2 & HWCAP2_MTE;
   features.tls = aarch64_tls_register_count (tid);
   /* SME feature check.  */
index a194ac809c23b5c9bb975d132631b55c6be99493..ce213bb482b9f2680813c24278caed1d7d66867f 100644 (file)
@@ -50,6 +50,7 @@
 #include "record-full.h"
 #include "linux-record.h"
 
+#include "arch/aarch64-gcs-linux.h"
 #include "arch/aarch64-mte.h"
 #include "arch/aarch64-mte-linux.h"
 #include "arch/aarch64-scalable-linux.h"
@@ -1604,6 +1605,27 @@ aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
       cb (".reg-aarch-tls", sizeof_tls_regset, sizeof_tls_regset,
          &aarch64_linux_tls_regset, "TLS register", cb_data);
     }
+
+  /* Handle GCS registers.  */
+  if (tdep->has_gcs ())
+    {
+      /* Create this on the fly in order to handle the variable regnums.  */
+      const struct regcache_map_entry gcs_regmap[] =
+       {
+         { 1, tdep->gcs_linux_reg_base, 8 },      /* features_enabled */
+         { 1, tdep->gcs_linux_reg_base + 1, 8 },  /* features_locked */
+         { 1, tdep->gcs_reg_base, 8 },            /* GCSPR */
+         { 0 }
+       };
+
+      const struct regset aarch64_linux_gcs_regset =
+       {
+         gcs_regmap, regcache_supply_regset, regcache_collect_regset
+       };
+
+      cb (".reg-aarch-gcs", sizeof (struct user_gcs), sizeof (struct user_gcs),
+         &aarch64_linux_gcs_regset, "GCS registers", cb_data);
+    }
 }
 
 /* Implement the "core_read_description" gdbarch method.  */
@@ -1628,6 +1650,7 @@ aarch64_linux_core_read_description (struct gdbarch *gdbarch,
      length.  */
   features.vq = aarch64_linux_core_read_vq_from_sections (gdbarch, abfd);
   features.pauth = hwcap & AARCH64_HWCAP_PACA;
+  features.gcs = features.gcs_linux = hwcap & HWCAP_GCS;
   features.mte = hwcap2 & HWCAP2_MTE;
 
   /* Handle the TLS section.  */
index 8d54e59f332a76e1ae768ba7e12b765b24054f27..d728f60e9e15de1b2b0b1188bfa431cb6aa222ab 100644 (file)
@@ -159,6 +159,18 @@ static const char *const aarch64_mte_register_names[] =
   "tag_ctl"
 };
 
+static const char *const aarch64_gcs_register_names[] = {
+  /* Guarded Control Stack Pointer Register.  */
+  "gcspr"
+};
+
+static const char *const aarch64_gcs_linux_register_names[] = {
+  /* Field in struct user_gcs.  */
+  "gcs_features_enabled",
+  /* Field in struct user_gcs.  */
+  "gcs_features_locked",
+};
+
 static int aarch64_stack_frame_destroyed_p (struct gdbarch *, CORE_ADDR);
 
 /* AArch64 prologue cache structure.  */
@@ -1875,6 +1887,39 @@ pass_in_v_vfp_candidate (struct gdbarch *gdbarch, struct regcache *regcache,
     }
 }
 
+/* Push LR_VALUE to the Guarded Control Stack.  */
+
+static void
+aarch64_push_gcs_entry (regcache *regs, CORE_ADDR lr_value)
+{
+  gdbarch *arch = regs->arch ();
+  aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (arch);
+  CORE_ADDR gcs_addr;
+
+  enum register_status status = regs->cooked_read (tdep->gcs_reg_base,
+                                                  &gcs_addr);
+  if (status != REG_VALID)
+    error ("Can't read $gcspr.");
+
+  gcs_addr -= 8;
+  gdb_byte buf[8];
+  store_integer (buf, gdbarch_byte_order (arch), lr_value);
+  if (target_write_memory (gcs_addr, buf, sizeof (buf)) != 0)
+    error ("Can't write to Guarded Control Stack.");
+
+  /* Update GCSPR.  */
+  regcache_cooked_write_unsigned (regs, tdep->gcs_reg_base, gcs_addr);
+}
+
+/* Implement the "shadow_stack_push" gdbarch method.  */
+
+static void
+aarch64_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr,
+                          regcache *regcache)
+{
+  aarch64_push_gcs_entry (regcache, new_addr);
+}
+
 /* Implement the "push_dummy_call" gdbarch method.  */
 
 static CORE_ADDR
@@ -4046,6 +4091,14 @@ aarch64_features_from_target_desc (const struct target_desc *tdesc)
   features.sme2 = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.sme2")
                   != nullptr);
 
+  /* Check for the GCS feature.  */
+  features.gcs = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.gcs")
+                 != nullptr);
+
+  /* Check for the GCS Linux feature.  */
+  features.gcs_linux = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.gcs.linux")
+                       != nullptr);
+
   return features;
 }
 
@@ -4590,6 +4643,46 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
     int first_w_regnum = num_pseudo_regs;
     num_pseudo_regs += 31;
 
+  const struct tdesc_feature *feature_gcs
+      = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.gcs");
+  int first_gcs_regnum = -1;
+  /* Add the GCS registers.  */
+  if (feature_gcs != nullptr)
+    {
+      first_gcs_regnum = num_regs;
+      /* Validate the descriptor provides the mandatory GCS registers and
+        allocate their numbers.  */
+      for (i = 0; i < ARRAY_SIZE (aarch64_gcs_register_names); i++)
+       valid_p &= tdesc_numbered_register (feature_gcs, tdesc_data.get (),
+                                           first_gcs_regnum + i,
+                                           aarch64_gcs_register_names[i]);
+
+      num_regs += i;
+    }
+
+  if (!valid_p)
+    return nullptr;
+
+  const struct tdesc_feature *feature_gcs_linux
+      = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.gcs.linux");
+  int first_gcs_linux_regnum = -1;
+  /* Add the GCS Linux registers.  */
+  if (feature_gcs_linux != nullptr)
+    {
+      first_gcs_linux_regnum = num_regs;
+      /* Validate the descriptor provides the mandatory GCS Linux registers
+        and allocate their numbers.  */
+      for (i = 0; i < ARRAY_SIZE (aarch64_gcs_linux_register_names); i++)
+       valid_p &= tdesc_numbered_register (feature_gcs_linux, tdesc_data.get (),
+                                           first_gcs_linux_regnum + i,
+                                           aarch64_gcs_linux_register_names[i]);
+
+      /* This feature depends on the GCS feature.  */
+      valid_p &= feature_gcs != nullptr;
+
+      num_regs += i;
+    }
+
   if (!valid_p)
     return nullptr;
 
@@ -4611,6 +4704,8 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   tdep->mte_reg_base = first_mte_regnum;
   tdep->tls_regnum_base = first_tls_regnum;
   tdep->tls_register_count = tls_register_count;
+  tdep->gcs_reg_base = first_gcs_regnum;
+  tdep->gcs_linux_reg_base = first_gcs_linux_regnum;
 
   /* Set the SME register set details.  The pseudo-registers will be adjusted
      later.  */
@@ -4733,6 +4828,9 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
   set_gdbarch_get_pc_address_flags (gdbarch, aarch64_get_pc_address_flags);
 
+  if (tdep->has_gcs ())
+    set_gdbarch_shadow_stack_push (gdbarch, aarch64_shadow_stack_push);
+
   tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data));
 
   /* Fetch the updated number of registers after we're done adding all
@@ -4905,6 +5003,9 @@ aarch64_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file)
              pulongest (tdep->sme_tile_pseudo_base));
   gdb_printf (file, _("aarch64_dump_tdep: sme_svq = %s\n"),
              pulongest (tdep->sme_svq));
+
+  gdb_printf (file, _ ("aarch64_dump_tdep: gcs_reg_base = %d\n"),
+             tdep->gcs_reg_base);
 }
 
 #if GDB_SELF_TEST
index 3b8dcc26545b5365f8abdd8d58db7059c247b482..0332e80da7bc5c79cff46548896d115109bff55e 100644 (file)
@@ -182,6 +182,20 @@ struct aarch64_gdbarch_tdep : gdbarch_tdep_base
   {
     return sme2_zt0_regnum > 0;
   }
+
+  /* First GCS register.  This is -1 if no GCS registers are available.  */
+  int gcs_reg_base = -1;
+
+  /* First GCS Linux-specific register.  This is -1 if no GCS Linux feature is
+     available.  */
+  int gcs_linux_reg_base = -1;
+
+  /* Returns true if the target supports GCS.  */
+  bool
+  has_gcs () const
+  {
+    return gcs_reg_base != -1;
+  }
 };
 
 const target_desc *aarch64_read_description (const aarch64_features &features);
diff --git a/gdb/arch/aarch64-gcs-linux.h b/gdb/arch/aarch64-gcs-linux.h
new file mode 100644 (file)
index 0000000..9366caa
--- /dev/null
@@ -0,0 +1,44 @@
+/* Common Linux target-dependent definitions for AArch64 GCS
+
+   Copyright (C) 2025 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef ARCH_AARCH64_GCS_LINUX_H
+#define ARCH_AARCH64_GCS_LINUX_H
+
+#include <stdint.h>
+
+/* Feature check for Guarded Control Stack.  */
+#ifndef HWCAP_GCS
+#define HWCAP_GCS (1UL << 32)
+#endif
+
+/* Make sure we only define these if the kernel header doesn't.  */
+#ifndef GCS_MAGIC
+
+/* GCS state (NT_ARM_GCS).  */
+
+struct user_gcs
+{
+  uint64_t features_enabled;
+  uint64_t features_locked;
+  uint64_t gcspr_el0;
+};
+
+#endif /* GCS_MAGIC */
+
+#endif /* ARCH_AARCH64_GCS_LINUX_H */
index 3e1ca0547340d536971cd9ec23bc27b74f22acc0..dff2bc16003adab6d127c026d61178fbcd92cbbc 100644 (file)
@@ -26,6 +26,8 @@
 #include "../features/aarch64-sme.c"
 #include "../features/aarch64-sme2.c"
 #include "../features/aarch64-tls.c"
+#include "../features/aarch64-gcs.c"
+#include "../features/aarch64-gcs-linux.c"
 
 /* See arch/aarch64.h.  */
 
@@ -65,6 +67,12 @@ aarch64_create_target_description (const aarch64_features &features)
   if (features.sme2)
     regnum = create_feature_aarch64_sme2 (tdesc.get (), regnum);
 
+  if (features.gcs)
+    regnum = create_feature_aarch64_gcs (tdesc.get (), regnum);
+
+  if (features.gcs_linux)
+    regnum = create_feature_aarch64_gcs_linux (tdesc.get (), regnum);
+
   return tdesc.release ();
 }
 
index ee18b74b80f58161fba1eeb9a6d799ab49a315fa..679d845df74ee5a9f8256b58508e306cea036f91 100644 (file)
@@ -51,6 +51,12 @@ struct aarch64_features
 
   /* Whether SME2 is supported.  */
   bool sme2 = false;
+
+  /* Whether Guarded Control Stack is supported.  */
+  bool gcs = false;
+
+  /* Whether Guarded Control Stack Linux features are supported.  */
+  bool gcs_linux = false;
 };
 
 inline bool operator==(const aarch64_features &lhs, const aarch64_features &rhs)
@@ -60,7 +66,9 @@ inline bool operator==(const aarch64_features &lhs, const aarch64_features &rhs)
     && lhs.mte == rhs.mte
     && lhs.tls == rhs.tls
     && lhs.svq == rhs.svq
-    && lhs.sme2 == rhs.sme2;
+    && lhs.sme2 == rhs.sme2
+    && lhs.gcs == rhs.gcs
+    && lhs.gcs_linux == rhs.gcs_linux;
 }
 
 namespace std
index 9c825685cb2a21e159ace3b1a800c9f8e72768c3..d0a776a5fac65da80b9a8d5877f83f87f98cc4aa 100644 (file)
@@ -26979,6 +26979,21 @@ information automatically from the core file, and will show one of the above
 messages depending on whether the synchronous or asynchronous mode is selected.
 @xref{Memory Tagging}. @xref{Memory}.
 
+@subsubsection Guarded Control Stack
+@cindex AArch64 GCS
+@cindex AArch64 Guarded Control Stack
+
+When @value{GDBN} is debugging the AArch64 architecture, the program is
+using the feature Guarded Control Stack (GCS) and there is support in the
+kernel for GCS, @value{GDBN} will make a couple of special registers ---
+@code{gcs_features_enabled} and @code{gcs_features_locked} --- available
+through the @code{org.gnu.gdb.aarch64.gcs.linux} feature.  These registers
+expose some options that can be controlled at runtime and emulate the
+@code{prctl} option @code{PR_SET_SHADOW_STACK_STATUS}.  For further
+information, see the
+@uref{https://www.kernel.org/doc/html/latest/arch/arm64/gcs.html,ignored,
+documentation} in the Linux kernel.
+
 @node x86
 @subsection x86
 
@@ -49617,6 +49632,42 @@ of bytes.
 Extra registers are allowed in this feature, but they will not affect
 @value{GDBN}.
 
+@subsubsection GCS registers
+
+The @samp{org.gnu.gdb.aarch64.gcs} feature is optional.  If present, it
+means the target supports Guarded Control Stacks and must contain the
+following register:
+
+@itemize @minus
+
+@item
+@code{gcspr}, which points to the thread's Guarded Control Stack.  It is 64
+bits in size and has a type of @samp{data_ptr}.
+
+@end itemize
+
+The @samp{org.gnu.gdb.aarch64.gcs.linux} feature is optional.  If present,
+then the @samp{org.gnu.gdb.aarch64.gcs} feature must also be present.  The
+@samp{org.gnu.gdb.aarch64.gcs.linux} feature represents facilities provided
+by the Linux kernel for GCS support and should contain the following:
+
+@itemize @minus
+
+@item
+@code{gcs_features_enabled} shows the features currently enabled via the
+prctl or ptrace system calls. It is represented as if it were a 64-bit
+register with a custom flags type.
+
+@item
+@code{gcs_features_locked} shows the features currently locked via the
+prctl or ptrace system calls. It is represented as if it were a 64-bit
+register with a custom flags type.
+
+@end itemize
+
+Extra registers are allowed in these features, but they will not affect
+@value{GDBN}.
+
 @node ARC Features
 @subsection ARC Features
 @cindex target descriptions, ARC Features
index 7a8c7999733ac7cfa30e2ae1db138688bc825db5..92fd8085d4786da5286f42be32f5ab7043992aed 100644 (file)
@@ -203,6 +203,8 @@ FEATURE_XMLFILES = aarch64-core.xml \
        aarch64-fpu.xml \
        aarch64-pauth.xml \
        aarch64-mte.xml \
+       aarch64-gcs.xml \
+       aarch64-gcs-linux.xml \
        arc/v1-core.xml \
        arc/v1-aux.xml \
        arc/v2-core.xml \
diff --git a/gdb/features/aarch64-gcs-linux.c b/gdb/features/aarch64-gcs-linux.c
new file mode 100644 (file)
index 0000000..6b0d25b
--- /dev/null
@@ -0,0 +1,21 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: aarch64-gcs-linux.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_aarch64_gcs_linux (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.aarch64.gcs.linux");
+  tdesc_type_with_fields *type_with_fields;
+  type_with_fields = tdesc_create_flags (feature, "features_flags", 8);
+  tdesc_add_flag (type_with_fields, 0, "PR_SHADOW_STACK_ENABLE");
+  tdesc_add_flag (type_with_fields, 1, "PR_SHADOW_STACK_WRITE");
+  tdesc_add_flag (type_with_fields, 2, "PR_SHADOW_STACK_PUSH");
+
+  tdesc_create_reg (feature, "gcs_features_enabled", regnum++, 1, "system", 64, "features_flags");
+  tdesc_create_reg (feature, "gcs_features_locked", regnum++, 1, "system", 64, "features_flags");
+  return regnum;
+}
diff --git a/gdb/features/aarch64-gcs-linux.xml b/gdb/features/aarch64-gcs-linux.xml
new file mode 100644 (file)
index 0000000..8d9d2ce
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2025 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.aarch64.gcs.linux">
+  <flags id="features_flags" size="8">
+    <field name="PR_SHADOW_STACK_ENABLE" start="0" end="0"/>
+    <field name="PR_SHADOW_STACK_WRITE" start="1" end="1"/>
+    <field name="PR_SHADOW_STACK_PUSH" start="2" end="2"/>
+  </flags>
+
+  <reg name="gcs_features_enabled" bitsize="64" type="features_flags" group="system"/>
+  <reg name="gcs_features_locked" bitsize="64" type="features_flags" group="system"/>
+</feature>
diff --git a/gdb/features/aarch64-gcs.c b/gdb/features/aarch64-gcs.c
new file mode 100644 (file)
index 0000000..2b2caf2
--- /dev/null
@@ -0,0 +1,14 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: aarch64-gcs.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_aarch64_gcs (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.aarch64.gcs");
+  tdesc_create_reg (feature, "gcspr", regnum++, 1, "system", 64, "data_ptr");
+  return regnum;
+}
diff --git a/gdb/features/aarch64-gcs.xml b/gdb/features/aarch64-gcs.xml
new file mode 100644 (file)
index 0000000..bbee5e0
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2025 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.aarch64.gcs">
+  <reg name="gcspr" bitsize="64" type="data_ptr" group="system"/>
+</feature>
index 2eb3af659ad19671a7997437c57a50963c05ddca..9feb1914dbd63d014ef8d422ff24e85b063a5f27 100644 (file)
@@ -39,6 +39,7 @@
 
 #include "gdb_proc_service.h"
 #include "arch/aarch64.h"
+#include "arch/aarch64-gcs-linux.h"
 #include "arch/aarch64-mte-linux.h"
 #include "arch/aarch64-scalable-linux.h"
 #include "linux-aarch32-tdesc.h"
@@ -321,6 +322,42 @@ aarch64_store_tlsregset (struct regcache *regcache, const void *buf)
     supply_register (regcache, *regnum, tls_buf + sizeof (uint64_t));
 }
 
+/* Fill BUF with GCS register from the regcache.  */
+
+static void
+aarch64_fill_gcsregset (struct regcache *regcache, void *buf)
+{
+  struct user_gcs *regset = (struct user_gcs *) buf;
+  int gcspr_regnum  = find_regno (regcache->tdesc, "gcspr");
+  int features_enabled_regnum  = find_regno (regcache->tdesc,
+                                            "gcs_features_enabled");
+  int features_locked_regnum  = find_regno (regcache->tdesc,
+                                           "gcs_features_locked");
+
+  collect_register (regcache, gcspr_regnum, &regset->gcspr_el0);
+  collect_register (regcache, features_enabled_regnum,
+                   &regset->features_enabled);
+  collect_register (regcache, features_locked_regnum, &regset->features_locked);
+}
+
+/* Store GCS register to regcache.  */
+
+static void
+aarch64_store_gcsregset (struct regcache *regcache, const void *buf)
+{
+  const struct user_gcs *regset = (const struct user_gcs *) buf;
+  int gcspr_regnum  = find_regno (regcache->tdesc, "gcspr");
+  int features_enabled_regnum  = find_regno (regcache->tdesc,
+                                            "gcs_features_enabled");
+  int features_locked_regnum  = find_regno (regcache->tdesc,
+                                           "gcs_features_locked");
+
+  supply_register (regcache, gcspr_regnum, &regset->gcspr_el0);
+  supply_register (regcache, features_enabled_regnum,
+                  &regset->features_enabled);
+  supply_register (regcache, features_locked_regnum, &regset->features_locked);
+}
+
 bool
 aarch64_target::low_supports_breakpoints ()
 {
@@ -846,6 +883,10 @@ static struct regset_info aarch64_regsets[] =
   { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_TLS,
     0, OPTIONAL_REGS,
     aarch64_fill_tlsregset, aarch64_store_tlsregset },
+  /* Guarded Control Stack registers.  */
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_GCS,
+    0, OPTIONAL_REGS,
+    aarch64_fill_gcsregset, aarch64_store_gcsregset },
   NULL_REGSET
 };
 
@@ -909,6 +950,10 @@ aarch64_adjust_register_sets (const struct aarch64_features &features)
          if (features.sme2)
            regset->size = AARCH64_SME2_ZT0_SIZE;
          break;
+       case NT_ARM_GCS:
+         if (features.gcs_linux)
+           regset->size = sizeof (struct user_gcs);
+         break;
        default:
          gdb_assert_not_reached ("Unknown register set found.");
        }
@@ -940,6 +985,7 @@ aarch64_target::low_arch_setup ()
       /* A-profile MTE is 64-bit only.  */
       features.mte = linux_get_hwcap2 (pid, 8) & HWCAP2_MTE;
       features.tls = aarch64_tls_register_count (tid);
+      features.gcs = features.gcs_linux = linux_get_hwcap (pid, 8) & HWCAP_GCS;
 
       /* Scalable Matrix Extension feature and size check.  */
       if (linux_get_hwcap2 (pid, 8) & HWCAP2_SME)