Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Christina Schimpe <christina.schimpe@intel.com>
To: gdb-patches@sourceware.org
Subject: [PATCH 1/9] gdb: Generalize handling of the shadow stack pointer.
Date: Tue, 23 Sep 2025 11:18:34 +0000	[thread overview]
Message-ID: <20250923111842.4091694-2-christina.schimpe@intel.com> (raw)
In-Reply-To: <20250923111842.4091694-1-christina.schimpe@intel.com>

Until now, handling of the shadow stack pointer has been done in the
target dependent implementations of the gdbarch hook
'gdbarch_shadow_stack_push'.  Also amd64 and aarch64 linux specific
unwinders for the shadow stack pointer are implemented.
In a following commit a subcommmand "backtrace shadow" of the ordinary
backtrace command will be added to print the shadow stack backtrace.
This requires more target-independent logic to handle the shadow stack
pointer.  To avoid that we duplicate the logic, add new source and header
files "shadow-stack" for the implementation of shadow_stack_push and shadow
stack pointer unwinding in a target-independent way.
---
 gdb/Makefile.in           |   2 +
 gdb/aarch64-linux-tdep.c  |  51 +-----------
 gdb/aarch64-tdep.c        |  42 ++--------
 gdb/amd64-linux-tdep.c    | 129 -----------------------------
 gdb/amd64-tdep.c          |  20 +++++
 gdb/gdbarch-gen.c         | 126 ++++++++++++++++++++++------
 gdb/gdbarch-gen.h         |  65 ++++++++++++---
 gdb/gdbarch_components.py |  83 ++++++++++++++++---
 gdb/infcall.c             |   4 +-
 gdb/linux-tdep.c          |   2 +
 gdb/shadow-stack.c        | 167 ++++++++++++++++++++++++++++++++++++++
 gdb/shadow-stack.h        |  39 +++++++++
 12 files changed, 462 insertions(+), 268 deletions(-)
 create mode 100644 gdb/shadow-stack.c
 create mode 100644 gdb/shadow-stack.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index beacefcfdd5..9cb782fe88c 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1191,6 +1191,7 @@ COMMON_SFILES = \
 	sentinel-frame.c \
 	ser-event.c \
 	serial.c \
+	shadow-stack.c \
 	skip.c \
 	solib.c \
 	solib-target.c \
@@ -1653,6 +1654,7 @@ HFILES_NO_SRCDIR = \
 	serial.h \
 	ser-tcp.h \
 	ser-unix.h \
+	shadow-stack.h \
 	sh-tdep.h \
 	sim-regno.h \
 	skip.h \
diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index 048be4f3532..2928b723e0b 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -64,6 +64,7 @@
 #include "elf/common.h"
 #include "elf/aarch64.h"
 #include "arch/aarch64-insn.h"
+#include "shadow-stack.h"
 
 /* For std::pow */
 #include <cmath>
@@ -2562,54 +2563,6 @@ aarch64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache,
   return gcspr;
 }
 
-/* Implement Guarded Control Stack Pointer Register unwinding.  For each
-   previous GCS pointer check if its address is still in the GCS memory
-   range.  If it's outside the range set the returned value to unavailable,
-   otherwise return a value containing the new GCS pointer.  */
-
-static value *
-aarch64_linux_dwarf2_prev_gcspr (const frame_info_ptr &this_frame,
-				 void **this_cache, int regnum)
-{
-  value *v = frame_unwind_got_register (this_frame, regnum, regnum);
-  gdb_assert (v != nullptr);
-
-  gdbarch *gdbarch = get_frame_arch (this_frame);
-
-  if (v->entirely_available () && !v->optimized_out ())
-    {
-      int size = register_size (gdbarch, regnum);
-      bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-      CORE_ADDR gcspr = extract_unsigned_integer (v->contents_all ().data (),
-						  size, byte_order);
-
-      /* Starting with v6.13, the Linux kernel supports Guarded Control
-	 Stack.  Using /proc/PID/smaps we can only check if the current
-	 GCSPR points to GCS memory.  Only if this is the case a valid
-	 previous GCS pointer can be calculated.  */
-      std::pair<CORE_ADDR, CORE_ADDR> range;
-      if (linux_address_in_shadow_stack_mem_range (gcspr, &range))
-	{
-	  /* The GCS grows downwards.  To compute the previous GCS pointer,
-	     we need to increment the GCSPR.  */
-	  CORE_ADDR new_gcspr = gcspr + 8;
-
-	  /* If NEW_GCSPR still points within the current GCS memory range
-	     we consider it to be valid.  */
-	  if (new_gcspr < range.second)
-	    return frame_unwind_got_address (this_frame, regnum, new_gcspr);
-	}
-    }
-
-  /* Return a value which is marked as unavailable in case we could not
-     calculate a valid previous GCS pointer.  */
-  value *retval
-    = value::allocate_register (get_next_frame_sentinel_okay (this_frame),
-				regnum, register_type (gdbarch, regnum));
-  retval->mark_bytes_unavailable (0, retval->type ()->length ());
-  return retval;
-}
-
 /* AArch64 Linux implementation of the report_signal_info gdbarch
    hook.  Displays information about possible memory tag violations.  */
 
@@ -3188,7 +3141,7 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
     {
       set_gdbarch_get_shadow_stack_pointer (gdbarch,
 					aarch64_linux_get_shadow_stack_pointer);
-      tdep->fn_prev_gcspr = aarch64_linux_dwarf2_prev_gcspr;
+      tdep->fn_prev_gcspr = dwarf2_prev_ssp;
     }
 }
 
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 500ac77d75a..95af82c2632 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -58,6 +58,9 @@
 
 /* For inferior_ptid and current_inferior ().  */
 #include "inferior.h"
+
+#include "shadow-stack.h"
+
 /* For std::sqrt and std::pow.  */
 #include <cmath>
 
@@ -1893,29 +1896,6 @@ 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;
-
-  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);
-}
-
 /* Remove the newest entry from the Guarded Control Stack.  */
 
 static void
@@ -1933,15 +1913,6 @@ aarch64_pop_gcs_entry (regcache *regs)
   regcache_cooked_write_unsigned (regs, tdep->gcs_reg_base, gcs_addr + 8);
 }
 
-/* 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
@@ -3683,7 +3654,7 @@ aarch64_displaced_step_b (const int is_bl, const int32_t offset,
       gdbarch_get_shadow_stack_pointer (dsd->regs->arch (), dsd->regs,
 					gcs_is_enabled);
       if (gcs_is_enabled)
-	aarch64_push_gcs_entry (dsd->regs, data->insn_addr + 4);
+	shadow_stack_push (dsd->regs->arch (), dsd->regs, data->insn_addr + 4);
     }
 }
 
@@ -3847,7 +3818,7 @@ aarch64_displaced_step_others (const uint32_t insn,
       gdbarch_get_shadow_stack_pointer (dsd->regs->arch (), dsd->regs,
 					gcs_is_enabled);
       if (gcs_is_enabled)
-	aarch64_push_gcs_entry (dsd->regs, data->insn_addr + 4);
+	shadow_stack_push (dsd->regs->arch (), dsd->regs, data->insn_addr + 4);
     }
   else
     aarch64_emit_insn (dsd->insn_buf, insn);
@@ -4871,9 +4842,6 @@ 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
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index a21f8a9a5ce..f0db3b7a1b4 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -48,8 +48,6 @@
 #include "arch/amd64-linux-tdesc.h"
 #include "inferior.h"
 #include "x86-tdep.h"
-#include "dwarf2/frame.h"
-#include "frame-unwind.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
@@ -1923,18 +1921,6 @@ amd64_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid,
   return dtv_addr;
 }
 
-/* Return the number of bytes required to update the shadow stack pointer
-   by one element.  For x32 the shadow stack elements are still 64-bit
-   aligned.  Thus, gdbarch_addr_bit cannot be used to compute the new
-   stack pointer.  */
-
-static inline int
-amd64_linux_shadow_stack_element_size_aligned (gdbarch *gdbarch)
-{
-  const bfd_arch_info *binfo = gdbarch_bfd_arch_info (gdbarch);
-  return (binfo->bits_per_word / binfo->bits_per_byte);
-}
-
 /* Read the shadow stack pointer register and return its value, if
    possible.  */
 
@@ -1966,119 +1952,6 @@ amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache,
   return ssp;
 }
 
-/* If shadow stack is enabled, push the address NEW_ADDR to the shadow
-   stack and increment the shadow stack pointer accordingly.  */
-
-static void
-amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr,
-			       regcache *regcache)
-{
-  bool shadow_stack_enabled;
-  std::optional<CORE_ADDR> ssp
-    = amd64_linux_get_shadow_stack_pointer (gdbarch, regcache,
-					    shadow_stack_enabled);
-
-  /* For amd64/Linux, if SSP has a value that means shadow stack is
-     enabled.  */
-  if (!ssp.has_value ())
-    return;
-  else
-    gdb_assert (shadow_stack_enabled);
-
-  /* The shadow stack grows downwards.  To push addresses to the stack,
-     we need to decrement SSP.  */
-  const int element_size
-    = amd64_linux_shadow_stack_element_size_aligned (gdbarch);
-  const CORE_ADDR new_ssp = *ssp - element_size;
-
-  /* Using /proc/PID/smaps we can only check if NEW_SSP points to shadow
-     stack memory.  If it doesn't, we assume the stack is full.  */
-  std::pair<CORE_ADDR, CORE_ADDR> memrange;
-  if (!linux_address_in_shadow_stack_mem_range (new_ssp, &memrange))
-    error (_("No space left on the shadow stack."));
-
-  /* On x86 there can be a shadow stack token at bit 63.  For x32,  the
-     address size is only 32 bit.   Always write back the full 8 bytes to
-     include the shadow stack token.  */
-  const bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  write_memory_unsigned_integer (new_ssp, element_size, byte_order,
-				 (ULONGEST) new_addr);
-
-  i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
-  gdb_assert (tdep->ssp_regnum > -1);
-
-  regcache_raw_write_unsigned (regcache, tdep->ssp_regnum, new_ssp);
-}
-
-/* Implement shadow stack pointer unwinding.  For each new shadow stack
-   pointer check if its address is still in the shadow stack memory range.
-   If it's outside the range set the returned value to unavailable,
-   otherwise return a value containing the new shadow stack pointer.  */
-
-static value *
-amd64_linux_dwarf2_prev_ssp (const frame_info_ptr &this_frame,
-			     void **this_cache, int regnum)
-{
-  value *v = frame_unwind_got_register (this_frame, regnum, regnum);
-  gdb_assert (v != nullptr);
-
-  gdbarch *gdbarch = get_frame_arch (this_frame);
-
-  if (v->entirely_available () && !v->optimized_out ())
-    {
-      int size = register_size (gdbarch, regnum);
-      bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-      CORE_ADDR ssp = extract_unsigned_integer (v->contents_all ().data (),
-						size, byte_order);
-
-      /* Using /proc/PID/smaps we can only check if the current shadow
-	 stack pointer SSP points to shadow stack memory.  Only if this is
-	 the case a valid previous shadow stack pointer can be
-	 calculated.  */
-      std::pair<CORE_ADDR, CORE_ADDR> range;
-      if (linux_address_in_shadow_stack_mem_range (ssp, &range))
-	{
-	  /* The shadow stack grows downwards.  To compute the previous
-	     shadow stack pointer, we need to increment SSP.  */
-	  CORE_ADDR new_ssp
-	    = ssp + amd64_linux_shadow_stack_element_size_aligned (gdbarch);
-
-	  /* There can be scenarios where we have a shadow stack pointer
-	     but the shadow stack is empty, as no call instruction has
-	     been executed yet.  If NEW_SSP points to the end of or before
-	     (<=) the current shadow stack memory range we consider
-	     NEW_SSP as valid (but empty).  */
-	  if (new_ssp <= range.second)
-	    return frame_unwind_got_address (this_frame, regnum, new_ssp);
-	}
-    }
-
-  /* Return a value which is marked as unavailable in case we could not
-     calculate a valid previous shadow stack pointer.  */
-  value *retval
-    = value::allocate_register (get_next_frame_sentinel_okay (this_frame),
-				regnum, register_type (gdbarch, regnum));
-  retval->mark_bytes_unavailable (0, retval->type ()->length ());
-  return retval;
-}
-
-/* Implement the "init_reg" dwarf2_frame_ops method.  */
-
-static void
-amd64_init_reg (gdbarch *gdbarch, int regnum, dwarf2_frame_state_reg *reg,
-		const frame_info_ptr &this_frame)
-{
-  if (regnum == gdbarch_pc_regnum (gdbarch))
-    reg->how = DWARF2_FRAME_REG_RA;
-  else if (regnum == gdbarch_sp_regnum (gdbarch))
-    reg->how = DWARF2_FRAME_REG_CFA;
-  else if (regnum == AMD64_PL3_SSP_REGNUM)
-    {
-      reg->how = DWARF2_FRAME_REG_FN;
-      reg->loc.fn = amd64_linux_dwarf2_prev_ssp;
-    }
-}
-
 static void
 amd64_linux_init_abi_common (struct gdbarch_info info, struct gdbarch *gdbarch,
 			     int num_disp_step_buffers)
@@ -2137,10 +2010,8 @@ amd64_linux_init_abi_common (struct gdbarch_info info, struct gdbarch *gdbarch,
   set_gdbarch_remove_non_address_bits_watchpoint
     (gdbarch, amd64_linux_remove_non_address_bits_watchpoint);
 
-  set_gdbarch_shadow_stack_push (gdbarch, amd64_linux_shadow_stack_push);
   set_gdbarch_get_shadow_stack_pointer (gdbarch,
 					amd64_linux_get_shadow_stack_pointer);
-  dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg);
 }
 
 static void
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 9245889210b..5406275e199 100755
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -51,6 +51,8 @@
 #include "x86-tdep.h"
 #include "amd64-ravenscar-thread.h"
 #include "gdbsupport/selftest.h"
+#include "shadow-stack.h"
+#include "dwarf2/frame.h"
 
 /* Note that the AMD64 architecture was previously known as x86-64.
    The latter is (forever) engraved into the canonical system name as
@@ -3493,6 +3495,21 @@ amd64_in_indirect_branch_thunk (struct gdbarch *gdbarch, CORE_ADDR pc)
 				       AMD64_RIP_REGNUM);
 }
 
+static void
+amd64_init_reg (gdbarch *gdbarch, int regnum, dwarf2_frame_state_reg *reg,
+		const frame_info_ptr &this_frame)
+{
+  if (regnum == gdbarch_pc_regnum (gdbarch))
+    reg->how = DWARF2_FRAME_REG_RA;
+  else if (regnum == gdbarch_sp_regnum (gdbarch))
+    reg->how = DWARF2_FRAME_REG_CFA;
+  else if (regnum == AMD64_PL3_SSP_REGNUM)
+    {
+      reg->how = DWARF2_FRAME_REG_FN;
+      reg->loc.fn = dwarf2_prev_ssp;
+    }
+}
+
 void
 amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
 		const target_desc *default_tdesc)
@@ -3660,6 +3677,9 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
   set_gdbarch_in_indirect_branch_thunk (gdbarch,
 					amd64_in_indirect_branch_thunk);
 
+  set_gdbarch_ssp_regnum (gdbarch, tdep->ssp_regnum);
+  dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg);
+
   register_amd64_ravenscar_ops (gdbarch);
 }
 
diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c
index 7887b1386c6..640576f4b85 100644
--- a/gdb/gdbarch-gen.c
+++ b/gdb/gdbarch-gen.c
@@ -85,6 +85,7 @@ struct gdbarch
   int pc_regnum = -1;
   int ps_regnum = -1;
   int fp0_regnum = -1;
+  int ssp_regnum = -1;
   gdbarch_stab_reg_to_regnum_ftype *stab_reg_to_regnum = no_op_reg_to_regnum;
   gdbarch_ecoff_reg_to_regnum_ftype *ecoff_reg_to_regnum = no_op_reg_to_regnum;
   gdbarch_sdb_reg_to_regnum_ftype *sdb_reg_to_regnum = no_op_reg_to_regnum;
@@ -261,8 +262,10 @@ struct gdbarch
   gdbarch_read_core_file_mappings_ftype *read_core_file_mappings = default_read_core_file_mappings;
   gdbarch_use_target_description_from_corefile_notes_ftype *use_target_description_from_corefile_notes = default_use_target_description_from_corefile_notes;
   gdbarch_core_parse_exec_context_ftype *core_parse_exec_context = default_core_parse_exec_context;
-  gdbarch_shadow_stack_push_ftype *shadow_stack_push = nullptr;
   gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer = default_get_shadow_stack_pointer;
+  gdbarch_top_addr_empty_shadow_stack_ftype *top_addr_empty_shadow_stack = nullptr;
+  gdbarch_address_in_shadow_stack_memory_range_ftype *address_in_shadow_stack_memory_range = nullptr;
+  int shadow_stack_element_size_aligned = 8;
 };
 
 /* Create a new ``struct gdbarch'' based on information provided by
@@ -348,6 +351,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of pc_regnum, invalid_p == 0.  */
   /* Skip verify of ps_regnum, invalid_p == 0.  */
   /* Skip verify of fp0_regnum, invalid_p == 0.  */
+  /* Skip verify of ssp_regnum, invalid_p == 0.  */
   /* Skip verify of stab_reg_to_regnum, invalid_p == 0.  */
   /* Skip verify of ecoff_reg_to_regnum, invalid_p == 0.  */
   /* Skip verify of sdb_reg_to_regnum, invalid_p == 0.  */
@@ -535,8 +539,10 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of read_core_file_mappings, invalid_p == 0.  */
   /* Skip verify of use_target_description_from_corefile_notes, invalid_p == 0.  */
   /* Skip verify of core_parse_exec_context, invalid_p == 0.  */
-  /* Skip verify of shadow_stack_push, has predicate.  */
   /* Skip verify of get_shadow_stack_pointer, invalid_p == 0.  */
+  /* Skip verify of top_addr_empty_shadow_stack, has predicate.  */
+  /* Skip verify of address_in_shadow_stack_memory_range, has predicate.  */
+  /* Skip verify of shadow_stack_element_size_aligned, invalid_p == 0.  */
   if (!log.empty ())
     internal_error (_("verify_gdbarch: the following are invalid ...%s"),
 		    log.c_str ());
@@ -709,6 +715,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   gdb_printf (file,
 	      "gdbarch_dump: fp0_regnum = %s\n",
 	      plongest (gdbarch->fp0_regnum));
+  gdb_printf (file,
+	      "gdbarch_dump: ssp_regnum = %s\n",
+	      plongest (gdbarch->ssp_regnum));
   gdb_printf (file,
 	      "gdbarch_dump: stab_reg_to_regnum = <%s>\n",
 	      host_address_to_string (gdbarch->stab_reg_to_regnum));
@@ -1405,15 +1414,24 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   gdb_printf (file,
 	      "gdbarch_dump: core_parse_exec_context = <%s>\n",
 	      host_address_to_string (gdbarch->core_parse_exec_context));
-  gdb_printf (file,
-	      "gdbarch_dump: gdbarch_shadow_stack_push_p() = %d\n",
-	      gdbarch_shadow_stack_push_p (gdbarch));
-  gdb_printf (file,
-	      "gdbarch_dump: shadow_stack_push = <%s>\n",
-	      host_address_to_string (gdbarch->shadow_stack_push));
   gdb_printf (file,
 	      "gdbarch_dump: get_shadow_stack_pointer = <%s>\n",
 	      host_address_to_string (gdbarch->get_shadow_stack_pointer));
+  gdb_printf (file,
+	      "gdbarch_dump: gdbarch_top_addr_empty_shadow_stack_p() = %d\n",
+	      gdbarch_top_addr_empty_shadow_stack_p (gdbarch));
+  gdb_printf (file,
+	      "gdbarch_dump: top_addr_empty_shadow_stack = <%s>\n",
+	      host_address_to_string (gdbarch->top_addr_empty_shadow_stack));
+  gdb_printf (file,
+	      "gdbarch_dump: gdbarch_address_in_shadow_stack_memory_range_p() = %d\n",
+	      gdbarch_address_in_shadow_stack_memory_range_p (gdbarch));
+  gdb_printf (file,
+	      "gdbarch_dump: address_in_shadow_stack_memory_range = <%s>\n",
+	      host_address_to_string (gdbarch->address_in_shadow_stack_memory_range));
+  gdb_printf (file,
+	      "gdbarch_dump: shadow_stack_element_size_aligned = %s\n",
+	      plongest (gdbarch->shadow_stack_element_size_aligned));
   if (gdbarch->dump_tdep != NULL)
     gdbarch->dump_tdep (gdbarch, file);
 }
@@ -2161,6 +2179,23 @@ set_gdbarch_fp0_regnum (struct gdbarch *gdbarch,
   gdbarch->fp0_regnum = fp0_regnum;
 }
 
+int
+gdbarch_ssp_regnum (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Skip verify of ssp_regnum, invalid_p == 0.  */
+  if (gdbarch_debug >= 2)
+    gdb_printf (gdb_stdlog, "gdbarch_ssp_regnum called\n");
+  return gdbarch->ssp_regnum;
+}
+
+void
+set_gdbarch_ssp_regnum (struct gdbarch *gdbarch,
+			int ssp_regnum)
+{
+  gdbarch->ssp_regnum = ssp_regnum;
+}
+
 int
 gdbarch_stab_reg_to_regnum (struct gdbarch *gdbarch, int stab_regnr)
 {
@@ -5543,43 +5578,84 @@ set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch,
   gdbarch->core_parse_exec_context = core_parse_exec_context;
 }
 
+std::optional<CORE_ADDR>
+gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->get_shadow_stack_pointer != NULL);
+  if (gdbarch_debug >= 2)
+    gdb_printf (gdb_stdlog, "gdbarch_get_shadow_stack_pointer called\n");
+  return gdbarch->get_shadow_stack_pointer (gdbarch, regcache, shadow_stack_enabled);
+}
+
+void
+set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch,
+				      gdbarch_get_shadow_stack_pointer_ftype get_shadow_stack_pointer)
+{
+  gdbarch->get_shadow_stack_pointer = get_shadow_stack_pointer;
+}
+
 bool
-gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch)
+gdbarch_top_addr_empty_shadow_stack_p (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
-  return gdbarch->shadow_stack_push != NULL;
+  return gdbarch->top_addr_empty_shadow_stack != NULL;
+}
+
+bool
+gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch, const CORE_ADDR ADDR, const std::pair<CORE_ADDR, CORE_ADDR> range)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->top_addr_empty_shadow_stack != NULL);
+  if (gdbarch_debug >= 2)
+    gdb_printf (gdb_stdlog, "gdbarch_top_addr_empty_shadow_stack called\n");
+  return gdbarch->top_addr_empty_shadow_stack (gdbarch, ADDR, range);
 }
 
 void
-gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache)
+set_gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch,
+					 gdbarch_top_addr_empty_shadow_stack_ftype top_addr_empty_shadow_stack)
+{
+  gdbarch->top_addr_empty_shadow_stack = top_addr_empty_shadow_stack;
+}
+
+bool
+gdbarch_address_in_shadow_stack_memory_range_p (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
-  gdb_assert (gdbarch->shadow_stack_push != NULL);
+  return gdbarch->address_in_shadow_stack_memory_range != NULL;
+}
+
+bool
+gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gdbarch, CORE_ADDR ssp, std::pair<CORE_ADDR, CORE_ADDR> *range)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->address_in_shadow_stack_memory_range != NULL);
   if (gdbarch_debug >= 2)
-    gdb_printf (gdb_stdlog, "gdbarch_shadow_stack_push called\n");
-  gdbarch->shadow_stack_push (gdbarch, new_addr, regcache);
+    gdb_printf (gdb_stdlog, "gdbarch_address_in_shadow_stack_memory_range called\n");
+  return gdbarch->address_in_shadow_stack_memory_range (ssp, range);
 }
 
 void
-set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch,
-			       gdbarch_shadow_stack_push_ftype shadow_stack_push)
+set_gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gdbarch,
+						  gdbarch_address_in_shadow_stack_memory_range_ftype address_in_shadow_stack_memory_range)
 {
-  gdbarch->shadow_stack_push = shadow_stack_push;
+  gdbarch->address_in_shadow_stack_memory_range = address_in_shadow_stack_memory_range;
 }
 
-std::optional<CORE_ADDR>
-gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled)
+int
+gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
-  gdb_assert (gdbarch->get_shadow_stack_pointer != NULL);
+  /* Skip verify of shadow_stack_element_size_aligned, invalid_p == 0.  */
   if (gdbarch_debug >= 2)
-    gdb_printf (gdb_stdlog, "gdbarch_get_shadow_stack_pointer called\n");
-  return gdbarch->get_shadow_stack_pointer (gdbarch, regcache, shadow_stack_enabled);
+    gdb_printf (gdb_stdlog, "gdbarch_shadow_stack_element_size_aligned called\n");
+  return gdbarch->shadow_stack_element_size_aligned;
 }
 
 void
-set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch,
-				      gdbarch_get_shadow_stack_pointer_ftype get_shadow_stack_pointer)
+set_gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch,
+					       int shadow_stack_element_size_aligned)
 {
-  gdbarch->get_shadow_stack_pointer = get_shadow_stack_pointer;
+  gdbarch->shadow_stack_element_size_aligned = shadow_stack_element_size_aligned;
 }
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index 24fa8bab2db..34a32c346e8 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -283,6 +283,12 @@ extern void set_gdbarch_ps_regnum (struct gdbarch *gdbarch, int ps_regnum);
 extern int gdbarch_fp0_regnum (struct gdbarch *gdbarch);
 extern void set_gdbarch_fp0_regnum (struct gdbarch *gdbarch, int fp0_regnum);
 
+/* Register number for the shadow stack pointer.  For inferior calls, the
+   gdbarch value ssp_regnum has to be provided. */
+
+extern int gdbarch_ssp_regnum (struct gdbarch *gdbarch);
+extern void set_gdbarch_ssp_regnum (struct gdbarch *gdbarch, int ssp_regnum);
+
 /* Convert stab register number (from `r' declaration) to a gdb REGNUM. */
 
 typedef int (gdbarch_stab_reg_to_regnum_ftype) (struct gdbarch *gdbarch, int stab_regnr);
@@ -1793,19 +1799,21 @@ extern void set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarc
 /* Some targets support special hardware-assisted control-flow protection
    technologies.  For example, the Intel Control-Flow Enforcement Technology
    (Intel CET) on x86 provides a shadow stack and indirect branch tracking.
-   To enable shadow stack support for inferior calls the shadow_stack_push
-   gdbarch hook has to be provided.  The get_shadow_stack_pointer gdbarch
-   hook has to be provided to enable displaced stepping.
-
-   Push NEW_ADDR to the shadow stack and update the shadow stack pointer. */
-
-extern bool gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch);
-
-typedef void (gdbarch_shadow_stack_push_ftype) (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
-extern void gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
-extern void set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, gdbarch_shadow_stack_push_ftype *shadow_stack_push);
-
-/* If possible, return the shadow stack pointer.  If the shadow stack
+   For GDB shadow stack support the following methods or values must be
+   provided:
+   - get_shadow_stack_pointer: required for displaced stepping
+   - address_in_shadow_stack_memory_range and top_addr_empty_shadow_stack:
+     required for shadow stack pointer unwinding
+   - address_in_shadow_stack_memory_range and the value ssp_regnum: required
+     for inferior function calls.
+
+   If the shadow stack alignment is not the predefault of 8 bytes, configure
+   the gdbarch value shadow_stack_element_size_aligned.
+   In case the shadow stack can be empty even though the shadow stack pointer
+   points to shadow stack memory range, implement the gdbarch hook
+   top_addr_empty_shadow_stack.
+
+   If possible, return the shadow stack pointer.  If the shadow stack
    feature is enabled then set SHADOW_STACK_ENABLED to true, otherwise
    set SHADOW_STACK_ENABLED to false.  This hook has to be provided to enable
    displaced stepping for shadow stack enabled programs.
@@ -1817,3 +1825,34 @@ extern void set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, gdbarch_shad
 typedef std::optional<CORE_ADDR> (gdbarch_get_shadow_stack_pointer_ftype) (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
 extern std::optional<CORE_ADDR> gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
 extern void set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer);
+
+/* Return true if ADDR points to the top of an empty shadow stack, defined by
+   RANGE [start_address, end_address).  This hook has to be provided to enable
+   inferior calls for shadow stack enabled programs and to enable unwinding
+   of the shadow stack pointer. */
+
+extern bool gdbarch_top_addr_empty_shadow_stack_p (struct gdbarch *gdbarch);
+
+typedef bool (gdbarch_top_addr_empty_shadow_stack_ftype) (struct gdbarch *gdbarch, const CORE_ADDR ADDR, const std::pair<CORE_ADDR, CORE_ADDR> range);
+extern bool gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch, const CORE_ADDR ADDR, const std::pair<CORE_ADDR, CORE_ADDR> range);
+extern void set_gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch, gdbarch_top_addr_empty_shadow_stack_ftype *top_addr_empty_shadow_stack);
+
+/* Returns true if ADDR belongs to a shadow stack memory range.  If this is
+   the case, assign the shadow stack memory range to RANGE
+   [start_address, end_address).  This hook has to be provided in case the
+   shadow stack can be empty even though SSP points to shadow stack memory
+   defined by RANGE [start_address, end_address).  This is possible for the
+   Guarded Control Stack on ARM. */
+
+extern bool gdbarch_address_in_shadow_stack_memory_range_p (struct gdbarch *gdbarch);
+
+typedef bool (gdbarch_address_in_shadow_stack_memory_range_ftype) (CORE_ADDR ssp, std::pair<CORE_ADDR, CORE_ADDR> *range);
+extern bool gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gdbarch, CORE_ADDR ssp, std::pair<CORE_ADDR, CORE_ADDR> *range);
+extern void set_gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gdbarch, gdbarch_address_in_shadow_stack_memory_range_ftype *address_in_shadow_stack_memory_range);
+
+/* The number of bytes required to update the shadow stack pointer by one
+   element.  In case the alignment is not the predefault (8 bytes), configure
+   this value. */
+
+extern int gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch);
+extern void set_gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch, int shadow_stack_element_size_aligned);
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 3820ae3c94c..5223553941f 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -544,6 +544,17 @@ Value(
     invalid=False,
 )
 
+Value(
+    comment="""
+Register number for the shadow stack pointer.  For inferior calls, the
+gdbarch value ssp_regnum has to be provided.
+""",
+    type="int",
+    name="ssp_regnum",
+    predefault="-1",
+    invalid=False,
+)
+
 Method(
     comment="""
 Convert stab register number (from `r' declaration) to a gdb REGNUM.
@@ -2852,20 +2863,20 @@ Method(
 Some targets support special hardware-assisted control-flow protection
 technologies.  For example, the Intel Control-Flow Enforcement Technology
 (Intel CET) on x86 provides a shadow stack and indirect branch tracking.
-To enable shadow stack support for inferior calls the shadow_stack_push
-gdbarch hook has to be provided.  The get_shadow_stack_pointer gdbarch
-hook has to be provided to enable displaced stepping.
-
-Push NEW_ADDR to the shadow stack and update the shadow stack pointer.
-""",
-    type="void",
-    name="shadow_stack_push",
-    params=[("CORE_ADDR", "new_addr"), ("regcache *", "regcache")],
-    predicate=True,
-)
+For GDB shadow stack support the following methods or values must be
+provided:
+- get_shadow_stack_pointer: required for displaced stepping
+- address_in_shadow_stack_memory_range and top_addr_empty_shadow_stack:
+  required for shadow stack pointer unwinding
+- address_in_shadow_stack_memory_range and the value ssp_regnum: required
+  for inferior function calls.
+
+If the shadow stack alignment is not the predefault of 8 bytes, configure
+the gdbarch value shadow_stack_element_size_aligned.
+In case the shadow stack can be empty even though the shadow stack pointer
+points to shadow stack memory range, implement the gdbarch hook
+top_addr_empty_shadow_stack.
 
-Method(
-    comment="""
 If possible, return the shadow stack pointer.  If the shadow stack
 feature is enabled then set SHADOW_STACK_ENABLED to true, otherwise
 set SHADOW_STACK_ENABLED to false.  This hook has to be provided to enable
@@ -2881,3 +2892,49 @@ SHADOW_STACK_ENABLED to false.
     predefault="default_get_shadow_stack_pointer",
     invalid=False,
 )
+
+Method(
+    comment="""
+Return true if ADDR points to the top of an empty shadow stack, defined by
+RANGE [start_address, end_address).  This hook has to be provided to enable
+inferior calls for shadow stack enabled programs and to enable unwinding
+of the shadow stack pointer.
+""",
+    type="bool",
+    name="top_addr_empty_shadow_stack",
+    params=[
+        ("const CORE_ADDR", "ADDR"),
+        ("const std::pair<CORE_ADDR, CORE_ADDR>", "range")
+    ],
+    predicate=True,
+)
+
+Function(
+    comment="""
+Returns true if ADDR belongs to a shadow stack memory range.  If this is
+the case, assign the shadow stack memory range to RANGE
+[start_address, end_address).  This hook has to be provided in case the
+shadow stack can be empty even though SSP points to shadow stack memory
+defined by RANGE [start_address, end_address).  This is possible for the
+Guarded Control Stack on ARM.
+""",
+    type="bool",
+    name="address_in_shadow_stack_memory_range",
+    params=[
+        ("CORE_ADDR", "ssp"),
+        ("std::pair<CORE_ADDR, CORE_ADDR> *", "range")
+    ],
+    predicate=True,
+)
+
+Value(
+    comment="""
+The number of bytes required to update the shadow stack pointer by one
+element.  In case the alignment is not the predefault (8 bytes), configure
+this value.
+""",
+    type="int",
+    name="shadow_stack_element_size_aligned",
+    predefault="8",
+    invalid=False,
+)
diff --git a/gdb/infcall.c b/gdb/infcall.c
index c4b4c8f0bea..46f9fd9342e 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -42,6 +42,7 @@
 #include "thread-fsm.h"
 #include <algorithm>
 #include "gdbsupport/scope-exit.h"
+#include "shadow-stack.h"
 #include <list>
 
 /* True if we are debugging inferior calls.  */
@@ -1462,8 +1463,7 @@ call_function_by_hand_dummy (struct value *function,
   /* Push the return address of the inferior (bp_addr) to the shadow stack
      and update the shadow stack pointer.  As we don't execute a call
      instruction to call the function we need to handle this manually.  */
-  if (gdbarch_shadow_stack_push_p (gdbarch))
-    gdbarch_shadow_stack_push (gdbarch, bp_addr, regcache);
+  shadow_stack_push (gdbarch, regcache, bp_addr);
 
   /* Set up a frame ID for the dummy frame so we can pass it to
      set_momentary_breakpoint.  We need to give the breakpoint a frame
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 4b679c8759e..1c1ebd5a99f 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -3118,6 +3118,8 @@ linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
   set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
   set_gdbarch_core_parse_exec_context (gdbarch,
 				       linux_corefile_parse_exec_context);
+  set_gdbarch_address_in_shadow_stack_memory_range
+    (gdbarch, linux_address_in_shadow_stack_mem_range);
 }
 
 INIT_GDB_FILE (linux_tdep)
diff --git a/gdb/shadow-stack.c b/gdb/shadow-stack.c
new file mode 100644
index 00000000000..d153d5fc846
--- /dev/null
+++ b/gdb/shadow-stack.c
@@ -0,0 +1,167 @@
+/* Manage a shadow stack pointer for GDB, the GNU debugger.
+
+   Copyright (C) 2024-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/>.  */
+
+#include "defs.h"
+#include "arch-utils.h"
+#include "gdbcore.h"
+#include "extract-store-integer.h"
+#include "frame.h"
+#include "frame-unwind.h"
+#include "shadow-stack.h"
+
+enum class ssp_update_direction
+{
+  /* Update ssp towards the bottom of the shadow stack.  */
+  bottom = 0,
+
+  /* Update ssp towards the top of the shadow stack.  */
+  top
+};
+
+/* Return a new shadow stack pointer which is incremented or decremented
+   by COUNT elements dependent on DIRECTION.  */
+
+static CORE_ADDR
+update_shadow_stack_pointer (gdbarch *gdbarch, CORE_ADDR ssp,
+			     const ssp_update_direction direction)
+{
+  bool increment = (gdbarch_stack_grows_down (gdbarch))
+		    ? (direction == ssp_update_direction::bottom)
+		      : (direction == ssp_update_direction::top);
+
+  CORE_ADDR new_ssp;
+  if (increment)
+    return ssp + gdbarch_shadow_stack_element_size_aligned (gdbarch);
+  else
+    return ssp - gdbarch_shadow_stack_element_size_aligned (gdbarch);
+}
+
+/* See shadow-stack.h.  */
+
+void shadow_stack_push (gdbarch *gdbarch, regcache *regcache,
+			const CORE_ADDR new_addr)
+{
+  if (!gdbarch_address_in_shadow_stack_memory_range_p (gdbarch)
+      || gdbarch_ssp_regnum (gdbarch) == -1)
+    return;
+
+  bool shadow_stack_enabled;
+  std::optional<CORE_ADDR> ssp
+    = gdbarch_get_shadow_stack_pointer (gdbarch, regcache,
+					shadow_stack_enabled);
+  if (!ssp.has_value () || !shadow_stack_enabled)
+    return;
+
+  const CORE_ADDR new_ssp
+    = update_shadow_stack_pointer (gdbarch, *ssp,
+				   ssp_update_direction::top);
+
+  /* If NEW_SSP does not point to shadow stack memory, we assume the stack
+     is full.  */
+  std::pair<CORE_ADDR, CORE_ADDR> range;
+  if (!gdbarch_address_in_shadow_stack_memory_range (gdbarch,
+						     new_ssp,
+						     &range))
+    error (_("No space left on the shadow stack."));
+
+  /* On x86 there can be a shadow stack token at bit 63.  For x32,  the
+     address size is only 32 bit.  Always write back the full 8 bytes to
+     include the shadow stack token.  */
+  const int element_size
+    = gdbarch_shadow_stack_element_size_aligned (gdbarch);
+
+  const bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+  write_memory_unsigned_integer (new_ssp, element_size, byte_order,
+				 (ULONGEST) new_addr);
+
+  regcache_raw_write_unsigned (regcache,
+			       gdbarch_ssp_regnum (gdbarch),
+			       new_ssp);
+}
+
+/* See shadow-stack.h.  */
+
+value *
+dwarf2_prev_ssp (const frame_info_ptr &this_frame, void **this_cache,
+		 int regnum)
+{
+  value *v = frame_unwind_got_register (this_frame, regnum, regnum);
+  gdb_assert (v != nullptr);
+
+  gdbarch *gdbarch = get_frame_arch (this_frame);
+
+  if (gdbarch_address_in_shadow_stack_memory_range_p (gdbarch)
+      && v->entirely_available () && !v->optimized_out ())
+    {
+      const int size = register_size (gdbarch, regnum);
+      bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+      CORE_ADDR ssp = extract_unsigned_integer
+	(v->contents_all ().data (), size, byte_order);
+
+      /* Only if the current shadow stack pointer SSP points to shadow
+	 stack memory a valid previous shadow stack pointer can be
+	 calculated.  */
+      std::pair<CORE_ADDR, CORE_ADDR> range;
+      if (gdbarch_address_in_shadow_stack_memory_range (gdbarch, ssp,
+							&range))
+	{
+	  /* Note that a shadow stack memory range can change, due to
+	     shadow stack switches for instance on x86 for an inter-
+	     privilege far call or when calling an interrupt/exception
+	     handler at a higher privilege level.  Shadow stack for
+	     userspace is supported for amd64 linux starting with
+	     Linux kernel v6.6.  However, shadow stack switches are not
+	     supported due to missing kernel space support.  We therefore
+	     implement this unwinder without support for shadow stack
+	     switches for now.  */
+	  const CORE_ADDR new_ssp
+	    = update_shadow_stack_pointer (gdbarch, ssp,
+					   ssp_update_direction::bottom);
+
+	  /* On x86, if NEW_SSP points to the end of RANGE, it indicates
+	     that NEW_SSP is valid, but the shadow stack is empty.  In
+	     contrast, on ARM's Guarded Control Stack, if NEW_SSP points
+	     to the end of RANGE, it means that the shadow stack pointer
+	     is invalid.  */
+	  bool is_top_addr_empty_shadow_stack
+	    = gdbarch_top_addr_empty_shadow_stack_p (gdbarch)
+	      && gdbarch_top_addr_empty_shadow_stack (gdbarch, new_ssp, range);
+
+	  /* Check whether the new SSP is valid.  Depending on the
+	     architecture, this may rely on both
+	     IS_TOP_ADDR_EMPTY_SHADOW_STACK and the return value of
+	     gdbarch_address_in_shadow_stack_memory_range, or on the
+	     latter only.  */
+	  if (is_top_addr_empty_shadow_stack
+	      || gdbarch_address_in_shadow_stack_memory_range (gdbarch,
+							       ssp,
+							       &range))
+	    return frame_unwind_got_address (this_frame, regnum, new_ssp);
+	}
+    }
+
+  /* Return a value which is marked as unavailable, in case we could not
+     calculate a valid previous shadow stack pointer.  */
+  value *retval
+    = value::allocate_register (get_next_frame_sentinel_okay (this_frame),
+				regnum, register_type (gdbarch, regnum));
+  retval->mark_bytes_unavailable (0, retval->type ()->length ());
+  return retval;
+
+}
diff --git a/gdb/shadow-stack.h b/gdb/shadow-stack.h
new file mode 100644
index 00000000000..5c3ba80974e
--- /dev/null
+++ b/gdb/shadow-stack.h
@@ -0,0 +1,39 @@
+/* Definitions to manage a shadow stack pointer for GDB, the GNU debugger.
+
+   Copyright (C) 2024-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 GDB_SHADOW_STACK_H
+#define GDB_SHADOW_STACK_H
+
+/* If shadow stack is enabled, push the address NEW_ADDR on the shadow
+   stack and update the shadow stack pointer accordingly.  */
+
+void shadow_stack_push (gdbarch *gdbarch, regcache *regcache,
+			const CORE_ADDR new_addr);
+
+/* Unwind the previous shadow stack pointer of THIS_FRAME's shadow stack
+   pointer.  REGNUM is the register number of the shadow stack pointer.
+   Return a value that is unavailable in case we cannot unwind the
+   previous shadow stack pointer.  Otherwise, return a value containing
+   the previous shadow stack pointer.  */
+
+value * dwarf2_prev_ssp (const frame_info_ptr &this_frame,
+			 void **this_cache, int regnum);
+
+#endif /* GDB_SHADOW_STACK_H */
-- 
2.34.1

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


  reply	other threads:[~2025-09-23 11:20 UTC|newest]

Thread overview: 67+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-23 11:18 [PATCH 0/9] Add new command to print the shadow stack backtrace Christina Schimpe
2025-09-23 11:18 ` Christina Schimpe [this message]
2025-10-31  1:31   ` [PATCH 1/9] gdb: Generalize handling of the shadow stack pointer Thiago Jung Bauermann
2025-11-17 11:18     ` Schimpe, Christina
2025-11-26  4:19       ` Thiago Jung Bauermann
2025-12-30 10:39         ` Schimpe, Christina
2025-09-23 11:18 ` [PATCH 2/9] gdb: Refactor 'stack.c:print_frame' Christina Schimpe
2025-10-03 20:05   ` Tom Tromey
2025-09-23 11:18 ` [PATCH 3/9] gdb: Introduce 'stack.c:print_pc' function without frame argument Christina Schimpe
2025-10-03 19:56   ` Tom Tromey
2025-09-23 11:18 ` [PATCH 4/9] gdb: Refactor 'find_symbol_funname' and 'info_frame_command_core' in stack.c Christina Schimpe
2025-10-03 19:55   ` Tom Tromey
2025-09-23 11:18 ` [PATCH 5/9] gdb: Refactor 'stack.c:print_frame_info' Christina Schimpe
2025-10-03 20:03   ` Tom Tromey
2025-09-23 11:18 ` [PATCH 6/9] gdb: Implement 'bt shadow' to print the shadow stack backtrace Christina Schimpe
2025-09-23 11:47   ` Eli Zaretskii
2025-09-25 11:06     ` Schimpe, Christina
2025-09-25 13:19       ` Eli Zaretskii
2025-09-25 14:58         ` Simon Marchi
2025-09-26  7:45           ` Schimpe, Christina
2025-10-29 15:05             ` Schimpe, Christina
2025-10-29 15:28               ` Guinevere Larsen
2025-11-03 19:47                 ` Schimpe, Christina
2025-11-04 11:53                   ` Guinevere Larsen
2025-11-05 16:33                     ` Schimpe, Christina
2025-10-13  1:17       ` Thiago Jung Bauermann
2025-10-13  7:19         ` Schimpe, Christina
2025-10-31  4:39           ` Thiago Jung Bauermann
2025-11-06 14:23             ` Schimpe, Christina
2025-10-03 20:15   ` Tom Tromey
2025-10-12 19:45     ` Schimpe, Christina
2026-02-19 17:24       ` Tom Tromey
2026-03-02 12:24         ` Schimpe, Christina
2025-10-31  4:02   ` Thiago Jung Bauermann
2025-11-17 20:14     ` Schimpe, Christina
2025-11-26  4:07       ` Thiago Jung Bauermann
2025-11-26 16:29         ` Thiago Jung Bauermann
2026-01-22 17:04           ` Schimpe, Christina
2026-03-06  2:35             ` Thiago Jung Bauermann
2026-01-15 14:05         ` Schimpe, Christina
2025-09-23 11:18 ` [PATCH 7/9] gdb: Provide gdbarch hook to distinguish shadow stack backtrace elements Christina Schimpe
2025-09-23 11:49   ` Eli Zaretskii
2025-09-25 11:10     ` Schimpe, Christina
2025-11-02 21:20       ` Thiago Jung Bauermann
2025-11-12 17:28         ` Schimpe, Christina
2025-11-16 18:39           ` Thiago Jung Bauermann
2025-11-17 11:51             ` Schimpe, Christina
2025-09-23 11:18 ` [PATCH 8/9] gdb: Implement the hook 'is_no_return_shadow_stack_address' for amd64 linux Christina Schimpe
2025-11-26  4:22   ` Thiago Jung Bauermann
2025-09-23 11:18 ` [PATCH 9/9] gdb, mi: Add -shadow-stack-list-frames command Christina Schimpe
2025-09-23 11:53   ` Eli Zaretskii
2025-09-25 11:32     ` Schimpe, Christina
2025-10-03 20:17   ` Tom Tromey
2025-10-12 19:54     ` Schimpe, Christina
2025-10-13  0:06       ` Thiago Jung Bauermann
2025-11-26  4:26   ` Thiago Jung Bauermann
2026-01-22 17:01     ` Schimpe, Christina
2026-03-06  2:44       ` Thiago Jung Bauermann
2025-09-25 11:46 ` [PATCH 0/9] Add new command to print the shadow stack backtrace Schimpe, Christina
2025-10-08  1:46   ` Thiago Jung Bauermann
2025-10-13  1:18     ` Thiago Jung Bauermann
2025-10-13  6:34       ` Schimpe, Christina
2025-10-29 14:52         ` Schimpe, Christina
2025-10-31  0:47           ` Thiago Jung Bauermann
2025-12-30 10:16             ` Schimpe, Christina
2026-03-06  2:30               ` Thiago Jung Bauermann
2026-03-12  9:53                 ` Schimpe, Christina

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=20250923111842.4091694-2-christina.schimpe@intel.com \
    --to=christina.schimpe@intel.com \
    --cc=gdb-patches@sourceware.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