Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Christina Joos <christina.joos@intel.com>
To: gdb-patches@sourceware.org
Cc: thiago.bauermann@linaro.org, tom@tromey.com, luis.machado.foss@gmail.com
Subject: [PATCH v5 01/13] gdb: Generalize handling of the shadow stack pointer.
Date: Thu, 24 Sep 2026 10:32:58 +0200	[thread overview]
Message-ID: <20260924083311.1961530-2-christina.joos@intel.com> (raw)
In-Reply-To: <20260924083311.1961530-1-christina.joos@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 patch a command line option "-shadow" will be added to
the backtrace command 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.  Since the
conditions for an empty shadow stack differ betweens ARM's GCS and CET
shadow stack this further requires a new gdbarch hook
gdbarch_top_addr_empty_shadow_stack to unwind the previous shadow stack
pointer.  The ARM specific implementation for
gdbarch_top_addr_empty_shadow_stack will added in the following commit:
"aarch64: Implement gdbarch function top_addr_empty_shadow_stack.".

Reviewed-By: Tom Tromey <tom@tromey.com>
Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
---
 gdb/Makefile.in           |   2 +
 gdb/aarch64-tdep.c        |  54 +++----------
 gdb/amd64-linux-tdep.c    | 134 +++----------------------------
 gdb/amd64-tdep.c          |  22 +++++
 gdb/gdbarch-gen.c         | 126 +++++++++++++++++++++++------
 gdb/gdbarch-gen.h         |  63 +++++++++++----
 gdb/gdbarch_components.py |  84 +++++++++++++++----
 gdb/infcall.c             |   4 +-
 gdb/linux-tdep.c          |   9 ++-
 gdb/shadow-stack.c        | 164 ++++++++++++++++++++++++++++++++++++++
 gdb/shadow-stack.h        |  42 ++++++++++
 11 files changed, 477 insertions(+), 227 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 d1574ec2d2c..60b33ce6838 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1183,6 +1183,7 @@ COMMON_SFILES = \
 	sentinel-frame.c \
 	ser-event.c \
 	serial.c \
+	shadow-stack.c \
 	skip.c \
 	solib.c \
 	solib-target.c \
@@ -1652,6 +1653,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-tdep.c b/gdb/aarch64-tdep.c
index 950ad4f6aae..848cee3043c 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -57,6 +57,9 @@
 
 /* For inferior_ptid and current_inferior ().  */
 #include "inferior.h"
+
+#include "shadow-stack.h"
+
 /* For std::sqrt and std::pow.  */
 #include <cmath>
 
@@ -1888,29 +1891,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
@@ -1928,19 +1908,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)
-{
-  bool gcs_is_enabled;
-
-  (void) gdbarch_get_shadow_stack_pointer (gdbarch, regcache, gcs_is_enabled);
-  if (gcs_is_enabled)
-    aarch64_push_gcs_entry (regcache, new_addr);
-}
-
 /* Implement the "push_dummy_call" gdbarch method.  */
 
 static CORE_ADDR
@@ -3678,11 +3645,7 @@ aarch64_displaced_step_b (const int is_bl, const int32_t offset,
       regcache_cooked_write_unsigned (dsd->regs, AARCH64_LR_REGNUM,
 				      data->insn_addr + 4);
       dsd->dsc->linked_branch = true;
-      bool gcs_is_enabled;
-      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, data->insn_addr + 4);
     }
 }
 
@@ -3846,7 +3809,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, data->insn_addr + 4);
     }
   else
     aarch64_emit_insn (dsd->insn_buf, insn);
@@ -4820,6 +4783,10 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   /* Register a hook for converting a memory tag to a string.  */
   set_gdbarch_memtag_to_string (gdbarch, aarch64_memtag_to_string);
 
+  /* AArch64's shadow stack pointer is the GCSPR.  */
+  if (tdep->has_gcs ())
+    set_gdbarch_ssp_regnum (gdbarch, tdep->gcs_reg_base);
+
   /* ABI */
   set_gdbarch_short_bit (gdbarch, 16);
   set_gdbarch_int_bit (gdbarch, 32);
@@ -4882,9 +4849,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 9b23db72bbe..42a62eaa975 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"
 #include "cli/cli-style.h"
 
 /* The syscall's XML filename for i386.  */
@@ -1921,18 +1919,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.  */
 
@@ -1964,117 +1950,14 @@ 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.  */
+/* Return true if ADDR points to the top of an empty shadow stack, defined by
+   RANGE [start_address, end_address).  */
 
-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)
+static bool
+amd64_linux_top_addr_empty_shadow_stack
+  (const CORE_ADDR addr, const std::pair<CORE_ADDR, CORE_ADDR> range)
 {
-  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;
-    }
+  return addr == range.second;
 }
 
 static void
@@ -2135,10 +2018,11 @@ 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);
+
+  set_gdbarch_top_addr_empty_shadow_stack
+    (gdbarch, amd64_linux_top_addr_empty_shadow_stack);
 }
 
 static void
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index a982e610642..0972aaf93d5 100644
--- 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
@@ -3488,6 +3490,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 == gdbarch_ssp_regnum (gdbarch))
+    {
+      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)
@@ -3647,6 +3664,11 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
   set_gdbarch_in_indirect_branch_thunk (gdbarch,
 					amd64_in_indirect_branch_thunk);
 
+  if (tdep->ssp_regnum != -1)
+    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 6008003466c..f5f85ba1f63 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_dwarf2_reg_to_regnum_ftype *dwarf2_reg_to_regnum = no_op_reg_to_regnum;
   gdbarch_register_name_ftype *register_name = nullptr;
   gdbarch_register_type_ftype *register_type = nullptr;
@@ -251,8 +252,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_address_in_shadow_stack_memory_range_ftype *address_in_shadow_stack_memory_range = nullptr;
+  gdbarch_top_addr_empty_shadow_stack_ftype *top_addr_empty_shadow_stack = nullptr;
+  int shadow_stack_element_size_aligned = 8;
 };
 
 /* Create a new ``struct gdbarch'' based on information provided by
@@ -334,6 +337,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 dwarf2_reg_to_regnum, invalid_p == 0.  */
   if (gdbarch->register_name == nullptr)
     log.puts ("\n\tregister_name");
@@ -511,8 +515,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 address_in_shadow_stack_memory_range, has predicate.  */
+  /* Skip verify of top_addr_empty_shadow_stack, 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 ());
@@ -682,6 +688,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: dwarf2_reg_to_regnum = <%s>\n",
 	      host_address_to_string (gdbarch->dwarf2_reg_to_regnum));
@@ -1330,15 +1339,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_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: 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: shadow_stack_element_size_aligned = %s\n",
+	      plongest (gdbarch->shadow_stack_element_size_aligned));
   if (gdbarch->dump_tdep != nullptr)
     gdbarch->dump_tdep (gdbarch, file);
 }
@@ -2077,6 +2095,23 @@ set_gdbarch_fp0_regnum (struct gdbarch *gdbarch,
   gdbarch->fp0_regnum = fp0_regnum;
 }
 
+int
+gdbarch_ssp_regnum (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != nullptr);
+  /* 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_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int dwarf2_regnr)
 {
@@ -5246,43 +5281,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 != nullptr);
+  gdb_assert (gdbarch->get_shadow_stack_pointer != nullptr);
+  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_address_in_shadow_stack_memory_range_p (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != nullptr);
-  return gdbarch->shadow_stack_push != nullptr;
+  return gdbarch->address_in_shadow_stack_memory_range != nullptr;
+}
+
+bool
+gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gdbarch, CORE_ADDR addr, std::pair<CORE_ADDR, CORE_ADDR> *range)
+{
+  gdb_assert (gdbarch != nullptr);
+  gdb_assert (gdbarch->address_in_shadow_stack_memory_range != nullptr);
+  if (gdbarch_debug >= 2)
+    gdb_printf (gdb_stdlog, "gdbarch_address_in_shadow_stack_memory_range called\n");
+  return gdbarch->address_in_shadow_stack_memory_range (addr, range);
 }
 
 void
-gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache)
+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->address_in_shadow_stack_memory_range = address_in_shadow_stack_memory_range;
+}
+
+bool
+gdbarch_top_addr_empty_shadow_stack_p (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != nullptr);
-  gdb_assert (gdbarch->shadow_stack_push != nullptr);
+  return gdbarch->top_addr_empty_shadow_stack != nullptr;
+}
+
+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 != nullptr);
+  gdb_assert (gdbarch->top_addr_empty_shadow_stack != nullptr);
   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_top_addr_empty_shadow_stack called\n");
+  return gdbarch->top_addr_empty_shadow_stack (addr, range);
 }
 
 void
-set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch,
-			       gdbarch_shadow_stack_push_ftype shadow_stack_push)
+set_gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch,
+					 gdbarch_top_addr_empty_shadow_stack_ftype top_addr_empty_shadow_stack)
 {
-  gdbarch->shadow_stack_push = shadow_stack_push;
+  gdbarch->top_addr_empty_shadow_stack = top_addr_empty_shadow_stack;
 }
 
-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 != nullptr);
-  gdb_assert (gdbarch->get_shadow_stack_pointer != nullptr);
+  /* 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 6eda8693d58..6611a3c3bc5 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -286,6 +286,12 @@ void set_gdbarch_ps_regnum (struct gdbarch *gdbarch, int ps_regnum);
 int gdbarch_fp0_regnum (struct gdbarch *gdbarch);
 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. */
+
+int gdbarch_ssp_regnum (struct gdbarch *gdbarch);
+void set_gdbarch_ssp_regnum (struct gdbarch *gdbarch, int ssp_regnum);
+
 /* Provide a default mapping from a DWARF2 register number to a gdb REGNUM.
    Return -1 for bad REGNUM.  Note: Several targets get this wrong. */
 
@@ -1734,22 +1740,23 @@ void set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarch_core_
 /* 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. */
-
-bool gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch);
-
-using gdbarch_shadow_stack_push_ftype = void (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
-void gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
-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 and inferior
+     function calls
+   - address_in_shadow_stack_memory_range: required for shadow stack pointer
+     unwinding and inferior function calls
+   - top_addr_empty_shadow_stack: required for shadow stack pointer unwinding
+   - 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.
+
+   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.
+   displaced stepping and inferior function calls for shadow stack enabled
+   programs.
    On some architectures, the shadow stack pointer is available even if the
    feature is disabled.  So dependent on the target, an implementation of
    this function may return a valid shadow stack pointer, but set
@@ -1758,3 +1765,31 @@ void set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, gdbarch_shadow_stac
 using gdbarch_get_shadow_stack_pointer_ftype = std::optional<CORE_ADDR> (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
 std::optional<CORE_ADDR> gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
 void set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer);
+
+/* Returns true if ADDR belongs to a shadow stack memory range.  If this is
+   the case and RANGE is non-null, assign the shadow stack memory range to
+   RANGE [start_address, end_address).  This hook has to be provided for
+   shadow stack pointer unwinding and inferior function calls. */
+
+bool gdbarch_address_in_shadow_stack_memory_range_p (struct gdbarch *gdbarch);
+
+using gdbarch_address_in_shadow_stack_memory_range_ftype = bool (CORE_ADDR addr, std::pair<CORE_ADDR, CORE_ADDR> *range);
+bool gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gdbarch, CORE_ADDR addr, std::pair<CORE_ADDR, CORE_ADDR> *range);
+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);
+
+/* 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
+   unwinding of the shadow stack pointer. */
+
+bool gdbarch_top_addr_empty_shadow_stack_p (struct gdbarch *gdbarch);
+
+using gdbarch_top_addr_empty_shadow_stack_ftype = bool (const CORE_ADDR addr, const std::pair<CORE_ADDR, CORE_ADDR> range);
+bool gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch, const CORE_ADDR addr, const std::pair<CORE_ADDR, CORE_ADDR> range);
+void set_gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch, gdbarch_top_addr_empty_shadow_stack_ftype *top_addr_empty_shadow_stack);
+
+/* 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. */
+
+int gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch);
+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 d8b2d114909..dc7080ae556 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -553,6 +553,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="""
 Provide a default mapping from a DWARF2 register number to a gdb REGNUM.
@@ -2760,24 +2771,23 @@ 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.
+For GDB shadow stack support the following methods or values must be
+provided:
+- get_shadow_stack_pointer: required for displaced stepping and inferior
+  function calls
+- address_in_shadow_stack_memory_range: required for shadow stack pointer
+  unwinding and inferior function calls
+- top_addr_empty_shadow_stack: required for shadow stack pointer unwinding
+- 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.
 
-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,
-)
-
-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
-displaced stepping for shadow stack enabled programs.
+displaced stepping and inferior function calls for shadow stack enabled
+programs.
 On some architectures, the shadow stack pointer is available even if the
 feature is disabled.  So dependent on the target, an implementation of
 this function may return a valid shadow stack pointer, but set
@@ -2789,3 +2799,49 @@ SHADOW_STACK_ENABLED to false.
     predefault="default_get_shadow_stack_pointer",
     invalid=False,
 )
+
+Function(
+    comment="""
+Returns true if ADDR belongs to a shadow stack memory range.  If this is
+the case and RANGE is non-null, assign the shadow stack memory range to
+RANGE [start_address, end_address).  This hook has to be provided for
+shadow stack pointer unwinding and inferior function calls.
+""",
+    type="bool",
+    name="address_in_shadow_stack_memory_range",
+    params=[("CORE_ADDR", "addr"), ("std::pair<CORE_ADDR, CORE_ADDR> *", "range")],
+    predicate=True,
+)
+
+Function(
+    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
+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,
+)
+
+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,
+    # Currently unused but we wanted to keep this hook around, since
+    # x86 32-bit shadow stacks would require a 4-byte offset, for
+    # instance.  But this is not supported yet by the Linux kernel
+    # as x86 shadow stack for userspace is only supported for amd64
+    # linux starting with Linux kernel v6.6.
+    unused=True,
+)
diff --git a/gdb/infcall.c b/gdb/infcall.c
index 4c40f6cb06e..14b76557253 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -43,6 +43,7 @@
 #include "thread-fsm.h"
 #include <algorithm>
 #include "gdbsupport/scope-exit.h"
+#include "shadow-stack.h"
 #include <list>
 #include "cli/cli-style.h"
 
@@ -1493,8 +1494,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 (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 1d525fd94c5..275581c5d05 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -3232,8 +3232,11 @@ linux_address_in_shadow_stack_mem_range
 
   if (it != smaps.end ())
     {
-      range->first = it->start_address;
-      range->second = it->end_address;
+      if (range != nullptr)
+	{
+	  range->first = it->start_address;
+	  range->second = it->end_address;
+	}
       return true;
     }
 
@@ -3283,6 +3286,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..160d30e0bfb
--- /dev/null
+++ b/gdb/shadow-stack.c
@@ -0,0 +1,164 @@
+/* Manage a shadow stack pointer for GDB, the GNU debugger.
+
+   Copyright (C) 2024-2026 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 "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 oldest (outermost) element of the shadow
+     stack.  */
+  outer = 0,
+
+  /* Update ssp towards the most recent (innermost) element of the
+     shadow stack.  */
+  inner,
+};
+
+/* Return a new shadow stack pointer which is incremented or decremented
+   by one element 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::outer
+		     : direction == ssp_update_direction::inner;
+
+  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 (regcache *regcache, const CORE_ADDR new_addr)
+{
+  gdbarch *gdbarch = regcache->arch ();
+  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::inner);
+
+  /* If NEW_SSP does not point to shadow stack memory, we assume the
+     stack is full.  */
+  if (!gdbarch_address_in_shadow_stack_memory_range (gdbarch,
+						     new_ssp,
+						     nullptr))
+    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 element
+     size 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,
+				 (unsigned long) 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::outer);
+
+	  /* On x86, if NEW_SSP points to the end outside of RANGE
+	     (NEW_SSP == RANGE.SECOND), it indicates that NEW_SSP is
+	     valid, but the shadow stack is empty.  In contrast, for
+	     ARM's Guarded Control Stack, if NEW_SSP points to the end
+	     of RANGE, it means that the shadow stack feature is
+	     disabled.  */
+	  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);
+
+	  /* Validate NEW_SSP.  This may depend on both
+	     IS_TOP_ADDR_EMPTY_SHADOW_STACK and the gdbarch hook (e.g., x86),
+	     or on the hook only (e.g., ARM).  */
+	  if (is_top_addr_empty_shadow_stack
+	      || gdbarch_address_in_shadow_stack_memory_range (gdbarch,
+							       new_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..5f540ff7d62
--- /dev/null
+++ b/gdb/shadow-stack.h
@@ -0,0 +1,42 @@
+/* Definitions to manage a shadow stack pointer for GDB, the GNU debugger.
+
+   Copyright (C) 2024-2026 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
+
+class regcache;
+class frame_info_ptr;
+struct value;
+
+/* 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 (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.53.0

________________________________________
Intel Deutschland GmbH 

Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany 

Tel: +49 (89) 99143-0 

www.intel.de 

Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman

Chairperson of the Supervisory Board: Sonja Pierer

Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.


  reply	other threads:[~2026-09-24  8:35 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-24  8:32 [PATCH v5 00/13] Add new command to print the shadow stack backtrace Christina Joos
2026-09-24  8:32 ` Christina Joos [this message]
2026-09-24 21:03   ` [PATCH v5 01/13] gdb: Generalize handling of the shadow stack pointer Thiago Jung Bauermann
2026-09-24  8:32 ` [PATCH v5 02/13] aarch64: Implement gdbarch function top_addr_empty_shadow_stack Christina Joos
2026-09-24 21:04   ` Thiago Jung Bauermann
2026-09-24  8:33 ` [PATCH v5 03/13] gdb: Add get_main_func_start_pc to refactor frame.c:inside_main_func Christina Joos
2026-09-24  8:33 ` [PATCH v5 04/13] gdb: Refactor 'stack.c:print_frame' Christina Joos
2026-09-24  8:33 ` [PATCH v5 05/13] gdb: Introduce 'stack.c:print_pc' function without frame argument Christina Joos
2026-09-24  8:33 ` [PATCH v5 06/13] gdb: Refactor 'find_symbol_funname' and 'info_frame_command_core' in stack.c Christina Joos
2026-09-24  8:33 ` [PATCH v5 07/13] gdb: Refactor 'stack.c:print_frame_info' Christina Joos
2026-09-24  8:33 ` [PATCH v5 08/13] gdb: Add command option 'bt -shadow' to print the shadow stack backtrace Christina Joos
2026-09-24  9:37   ` Eli Zaretskii
2026-09-24  8:33 ` [PATCH v5 09/13] gdb: Provide gdbarch hook to distinguish shadow stack backtrace elements Christina Joos
2026-09-24  9:29   ` Eli Zaretskii
2026-09-24 13:10     ` Joos, Christina
2026-09-24  8:33 ` [PATCH v5 10/13] gdb: Implement the hook 'is_no_return_shadow_stack_address' for amd64 linux Christina Joos
2026-09-24  8:33 ` [PATCH v5 11/13] gdb: Enable inferior calls in the shadow stack backtrace Christina Joos
2026-09-24  8:33 ` [PATCH v5 12/13] gdb: Enable signal trampolines " Christina Joos
2026-09-24  8:33 ` [PATCH v5 13/13] gdb, mi: Add -shadow-stack-list-frames command Christina Joos
2026-09-24  9:39   ` Eli Zaretskii

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=20260924083311.1961530-2-christina.joos@intel.com \
    --to=christina.joos@intel.com \
    --cc=gdb-patches@sourceware.org \
    --cc=luis.machado.foss@gmail.com \
    --cc=thiago.bauermann@linaro.org \
    --cc=tom@tromey.com \
    /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