Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: "Schimpe, Christina" <christina.schimpe@intel.com>
To: "gdb-patches@sourceware.org" <gdb-patches@sourceware.org>
Cc: "tom@tromey.com" <tom@tromey.com>,
	"thiago.bauermann@linaro.org" <thiago.bauermann@linaro.org>
Subject: RE: [PATCH v3 08/12] gdb: Add command option 'bt -shadow' to print the shadow stack backtrace.
Date: Wed, 8 Jul 2026 09:13:48 +0000	[thread overview]
Message-ID: <SN7PR11MB7638B841D7927ECC93B2A547F9FF2@SN7PR11MB7638.namprd11.prod.outlook.com> (raw)
In-Reply-To: <20260706123133.1990441-9-christina.schimpe@intel.com>

I accidentally merged the patch 
"gdb: Provide gdbarch hook to distinguish shadow stack backtrace elements. " into this one.

Also there is an arm build failure:

/tmp/gdb/shadow-stack.c:712:35: error: call of overloaded ‘get_shadow_stack_frame_info(gdbarch*&, const CORE_ADDR&, int)’ is ambiguous
  | # 00:02:09 make[1]: *** [Makefile:2102: shadow-stack.o] Error 1
  | # 00:02:09 make: *** [Makefile:11400: all-gdb] Error 2

I will post a v4 to fix this soon.

Christina

> -----Original Message-----
> From: Christina Schimpe <christina.schimpe@intel.com>
> Sent: Montag, 6. Juli 2026 14:31
> To: gdb-patches@sourceware.org
> Cc: tom@tromey.com; thiago.bauermann@linaro.org
> Subject: [PATCH v3 08/12] gdb: Add command option 'bt -shadow' to print
> the shadow stack backtrace.
> 
> Add command option '-shadow" to the backtrace command to print the
> shadow stack backtrace instead of the normal backtrace.
> 
> This option may be combined with '-frame-info' and implies '-no-filters'
> and '-frame-arguments none'.  Also the options @samp{-past-entry} or
> @samp{-hide} are not supported.
> 
> This is an example for the output of 'bt -shadow' on amd64 linux:
> ~~
> (gdb) bt -shadow
> /#0  0x000055555555514a in call1 at amd64-shadow-stack.c:27
> /#1  0x000055555555515f in main at amd64-shadow-stack.c:38 ~~
> 
> Note that the normal backtrace includes one additional frame, since the
> shadow stack backtrace relies on return addresses of the shadow stack only.
> But except the missing frame arguments and frame #0 of the normal
> backtrace, the backtrace is the same.
> ~~
> (gdb) bt
> /#0  call2 () at amd64-shadow-stack.c:21
> /#1  0x000055555555514a in call1 () at amd64-shadow-stack.c:27
> /#2  0x000055555555515f in main () at amd64-shadow-stack.c:38 ~~
> 
> This commit also adds a test for 'bt -shadow' on amd64.
> Although the test is OS independent we can only test this on linux, as GDB
> does not support shadow stack on other OS for now.
> Also we do not add a test for 32 bit, as support for shadow stack is limited to
> 64 bit by the linux kernel.
> ---
>  gdb/NEWS                                      |   3 +
>  gdb/doc/gdb.texinfo                           |  43 ++
>  gdb/gdbarch-gen.c                             |  32 ++
>  gdb/gdbarch-gen.h                             |  21 +
>  gdb/gdbarch.h                                 |   1 +
>  gdb/gdbarch_components.py                     |  27 +
>  gdb/shadow-stack.c                            | 509 ++++++++++++++++++
>  gdb/shadow-stack.h                            |  91 ++++
>  gdb/stack.c                                   |  49 +-
>  gdb/stack.h                                   |  43 ++
>  .../gdb.arch/amd64-shadow-stack-cmds.exp      |  47 ++
>  gdb/testsuite/gdb.base/options.exp            |   2 +-
>  12 files changed, 843 insertions(+), 25 deletions(-)
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 343828a85dd..2605a5ab78a 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,9 @@
> 
>  *** Changes since GDB 17
> 
> +* New "-shadow" command line option for the backtrace command to print
> +  the shadow stack backtrace instead of the normal backtrace.
> +
>  * Support for the Common Trace Format (CTF) has been removed.  GDB now
>    saves trace information exclusively in its own "tfile" format.
> 
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index
> a698b2b8451..24b11d2657c 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -8788,6 +8788,47 @@ Related setting: @ref{set print raw-frame-
> arguments}.
>  @item -frame-info @code{auto}|@code{source-
> line}|@code{location}|@code{source-and-location}|@code{location-and-
> address}|@code{short-location}
>  Set printing of frame information.
>  Related setting: @ref{set print frame-info}.
> +
> +@item -shadow
> +Print the shadow stack backtrace instead of the normal backtrace.
> +Similar to the normal backtrace @value{GDBN} prints one line per
> +element (so-called shadow stack frames) on the shadow stack.
> +
> +A shadow stack is supported, for instance, with the Intel Control-Flow
> +Enforcement Technology (@pxref{CET}) on x86 and the Guarded Control
> +Stack feature (@pxref{GCS}) on AArch64.
> +
> +This option may be combined with @code{-frame-info} and implies
> +@code{-no-filters} and @code{-frame-arguments none}.  Also the options
> +@samp{-past-entry} or @samp{-hide} are not supported.
> +
> +This is how a shadow stack backtrace looks like on amd64:
> +@smallexample
> +@group
> +#0  0x000055555555514a in call1 at amd64-shadow-stack.c:27
> +#1  0x000055555555515f in main at amd64-shadow-stack.c:38 @end
> group
> +@end smallexample
> +
> +There can be elements on the shadow stack which are not return
> +addresses, for example on x86 with the Intel Control-Flow Enforcement
> +Technology (@xref{CET}).  In case of signals, the old shadow stack
> +pointer is pushed in a special format with bit 63 set.  See
> +@url{https://docs.kernel.org/arch/x86/shstk.html}
> +for more details.  For such shadow stack elements, the shadow stack
> +frame just contains the level and a string describing the shadow stack
> +element:
> +
> +@smallexample
> +@group
> +(gdb) bt -shadow 4
> +#0  0x00007ffff7c45330 in __restore_rt from
> +/lib/x86_64-linux-gnu/libc.so.6
> +#1  <sigframe token>
> +#2  0x00007ffff7c4527e in __GI_raise at ../sysdeps/posix/raise.c:26
> +#3  0x000055555555519d in main at tmp/amd64-shadow-stack-signal.c:29
> +(More shadow stack frames follow...)
> +@end group
> +@end smallexample
> +
>  @end table
> 
>  The optional @var{qualifier} is maintained for backward compatibility.
> @@ -27219,6 +27260,7 @@ information automatically from the core file,
> and will show one of the above  messages depending on whether the
> synchronous or asynchronous mode is selected.
>  @xref{Memory Tagging}. @xref{Memory}.
> 
> +@anchor{GCS}
>  @subsubsection AArch64 Guarded Control Stack  @cindex Guarded Control
> Stack, AArch64  @cindex GCS, AArch64 @@ -27307,6 +27349,7 @@
> registers
> 
>  @end itemize
> 
> +@anchor{CET}
>  @subsubsection Intel Control-Flow Enforcement Technology.
>  @cindex Intel Control-Flow Enforcement Technology.
> 
> diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c index
> 022ad496026..e93afff34b1 100644
> --- a/gdb/gdbarch-gen.c
> +++ b/gdb/gdbarch-gen.c
> @@ -256,6 +256,7 @@ struct gdbarch
>    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;
> +  gdbarch_is_no_return_shadow_stack_address_ftype
> + *is_no_return_shadow_stack_address = nullptr;
>  };
> 
>  /* Create a new ``struct gdbarch'' based on information provided by @@ -
> 519,6 +520,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
>    /* 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.  */
> +  /* Skip verify of is_no_return_shadow_stack_address, has predicate.
> + */
>    if (!log.empty ())
>      internal_error (_("verify_gdbarch: the following are invalid ...%s"),
>  		    log.c_str ());
> @@ -1357,6 +1359,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct
> ui_file *file)
>    gdb_printf (file,
>  	      "gdbarch_dump: shadow_stack_element_size_aligned = %s\n",
>  	      plongest (gdbarch->shadow_stack_element_size_aligned));
> +  gdb_printf (file,
> +	      "gdbarch_dump:
> gdbarch_is_no_return_shadow_stack_address_p() = %d\n",
> +	      gdbarch_is_no_return_shadow_stack_address_p (gdbarch));
> +  gdb_printf (file,
> +	      "gdbarch_dump: is_no_return_shadow_stack_address = <%s>\n",
> +	      host_address_to_string
> +(gdbarch->is_no_return_shadow_stack_address));
>    if (gdbarch->dump_tdep != nullptr)
>      gdbarch->dump_tdep (gdbarch, file);  } @@ -5362,3 +5370,27 @@
> set_gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch,
> {
>    gdbarch->shadow_stack_element_size_aligned =
> shadow_stack_element_size_aligned;
>  }
> +
> +bool
> +gdbarch_is_no_return_shadow_stack_address_p (struct gdbarch *gdbarch) {
> +  gdb_assert (gdbarch != nullptr);
> +  return gdbarch->is_no_return_shadow_stack_address != nullptr; }
> +
> +std::optional<shadow_stack_frame_info>
> +gdbarch_is_no_return_shadow_stack_address (struct gdbarch *gdbarch,
> +const CORE_ADDR ssp, const CORE_ADDR value, const unsigned long level)
> +{
> +  gdb_assert (gdbarch != nullptr);
> +  gdb_assert (gdbarch->is_no_return_shadow_stack_address != nullptr);
> +  if (gdbarch_debug >= 2)
> +    gdb_printf (gdb_stdlog, "gdbarch_is_no_return_shadow_stack_address
> +called\n");
> +  return gdbarch->is_no_return_shadow_stack_address (gdbarch, ssp,
> +value, level); }
> +
> +void
> +set_gdbarch_is_no_return_shadow_stack_address (struct gdbarch *gdbarch,
> +
> gdbarch_is_no_return_shadow_stack_address_ftype
> +is_no_return_shadow_stack_address)
> +{
> +  gdbarch->is_no_return_shadow_stack_address =
> +is_no_return_shadow_stack_address;
> +}
> diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index
> c05564fe05f..791324777dd 100644
> --- a/gdb/gdbarch-gen.h
> +++ b/gdb/gdbarch-gen.h
> @@ -1745,6 +1745,8 @@ void set_gdbarch_core_parse_exec_context
> (struct gdbarch *gdbarch, gdbarch_core_
> 
>     If the shadow stack alignment is not the predefault of 8 bytes, configure
>     the gdbarch value shadow_stack_element_size_aligned.
> +   To support the command line option 'backtrace -shadow' each value and
> hook
> +   listed above has to be provided.
> 
>     If possible, return the shadow stack pointer.  If the shadow stack
>     feature is enabled then set SHADOW_STACK_ENABLED to true, otherwise
> @@ -1787,3 +1789,22 @@ void
> set_gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch,
> gdbarch_t
> 
>  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);
> +
> +/* There can be elements on the shadow stack which are not return
> addresses.
> +   This happens for example on x86 with CET in case of signals.
> +   If an architecture implements the command option 'backtrace -shadow'
> and
> +   the shadow stack can contain elements which are not return addresses,
> this
> +   function has to be provided.
> +   Return a shadow stack frame info with frame type
> +   ssp_frame_type::non_return_frame, if the shadow stack pointer SSP
> belongs
> +   to a valid shadow stack frame while the element on the shadow stack
> +   VALUE does not refer to a return address.  In that case, also the frame's
> +   attribute non_return_description has to be set to a string which is
> +   displayed instead of the element on the shadow stack in the shadow stack
> +   backtrace.  Otherwise, return an empty optional. */
> +
> +bool gdbarch_is_no_return_shadow_stack_address_p (struct gdbarch
> +*gdbarch);
> +
> +using gdbarch_is_no_return_shadow_stack_address_ftype =
> +std::optional<shadow_stack_frame_info> (struct gdbarch *gdbarch, const
> +CORE_ADDR ssp, const CORE_ADDR value, const unsigned long level);
> +std::optional<shadow_stack_frame_info>
> +gdbarch_is_no_return_shadow_stack_address (struct gdbarch *gdbarch,
> +const CORE_ADDR ssp, const CORE_ADDR value, const unsigned long level);
> +void set_gdbarch_is_no_return_shadow_stack_address (struct gdbarch
> +*gdbarch, gdbarch_is_no_return_shadow_stack_address_ftype
> +*is_no_return_shadow_stack_address);
> diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index
> 7077f9262e9..b587a862e90 100644
> --- a/gdb/gdbarch.h
> +++ b/gdb/gdbarch.h
> @@ -32,6 +32,7 @@
>  #include "registry.h"
>  #include "solib.h"
>  #include "find-memory-region.h"
> +#include "shadow-stack.h"
> 
>  struct floatformat;
>  struct ui_file;
> diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
> index 531d7e212d8..0435b1b5eca 100644
> --- a/gdb/gdbarch_components.py
> +++ b/gdb/gdbarch_components.py
> @@ -2774,6 +2774,8 @@ provided:
> 
>  If the shadow stack alignment is not the predefault of 8 bytes, configure  the
> gdbarch value shadow_stack_element_size_aligned.
> +To support the command line option 'backtrace -shadow' each value and
> +hook listed above has to be provided.
> 
>  If possible, return the shadow stack pointer.  If the shadow stack  feature is
> enabled then set SHADOW_STACK_ENABLED to true, otherwise @@ -2837,3
> +2839,28 @@ this value.
>      # linux starting with Linux kernel v6.6.
>      unused=True,
>  )
> +
> +Method(
> +    comment="""
> +There can be elements on the shadow stack which are not return addresses.
> +This happens for example on x86 with CET in case of signals.
> +If an architecture implements the command option 'backtrace -shadow'
> +and the shadow stack can contain elements which are not return
> +addresses, this function has to be provided.
> +Return a shadow stack frame info with frame type
> +ssp_frame_type::non_return_frame, if the shadow stack pointer SSP
> +belongs to a valid shadow stack frame while the element on the shadow
> +stack VALUE does not refer to a return address.  In that case, also the
> +frame's attribute non_return_description has to be set to a string
> +which is displayed instead of the element on the shadow stack in the
> +shadow stack backtrace.  Otherwise, return an empty optional.
> +""",
> +    type="std::optional<shadow_stack_frame_info>",
> +    name="is_no_return_shadow_stack_address",
> +    params=[
> +        ("const CORE_ADDR", "ssp"),
> +        ("const CORE_ADDR", "value"),
> +        ("const unsigned long", "level"),
> +    ],
> +    predicate=True,
> +)
> diff --git a/gdb/shadow-stack.c b/gdb/shadow-stack.c index
> 160d30e0bfb..dbd8959753d 100644
> --- a/gdb/shadow-stack.c
> +++ b/gdb/shadow-stack.c
> @@ -22,6 +22,12 @@
>  #include "frame.h"
>  #include "frame-unwind.h"
>  #include "shadow-stack.h"
> +#include "stack.h"
> +#include "solib.h"
> +#include "event-top.h"
> +#include "cli/cli-style.h"
> +#include "buffered-streams.h"
> +#include "inferior.h"
> 
>  enum class ssp_update_direction
>  {
> @@ -162,3 +168,506 @@ dwarf2_prev_ssp (const frame_info_ptr
> &this_frame, void **this_cache,
>    retval->mark_bytes_unavailable (0, retval->type ()->length ());
>    return retval;
>  }
> +
> +/* Return true, if PC is in the middle of a statement.  Note that in the
> +   middle of a statement PC range includes sal.end (SAL.PC, SAL.END].
> +   Return false, if
> +   - SAL.IS_STMT is false
> +   - there is no location information associated with this SAL, which
> +   could happen in case of inlined functions
> +   - PC is not in the range (SAL.PC, SAL.END].
> +   This function is similar to stack.c:frame_show_address but is used
> +   to determine if we are in the middle of a statement only, not to decide
> +   if we should print a frame's address.  */
> +
> +static bool
> +pc_in_middle_of_statement (CORE_ADDR pc, symtab_and_line sal) {
> +  if (sal.is_stmt == false)
> +    return false;
> +
> +  /* If there is a line number, but no PC, then there is no location
> +     information associated with this sal.  The only way that should
> +     happen is for the call sites of inlined functions (SAL comes from
> +     find_sal_for_pc).  Otherwise, we would have some PC range if the SAL
> +     came from a line table.  However, as we don't have a frame for this
> +     function we cannot assert (in contrast to frame_show_address).  */
> + if (sal.line != 0 && sal.pc == 0 && sal.end == 0)
> +    return false;
> +
> +  return pc > sal.pc && pc <= sal.end;
> +}
> +
> +/* Attempt to obtain the function name based on the symbol of PC.  */
> +
> +static gdb::unique_xmalloc_ptr<char>
> +find_pc_funname (CORE_ADDR pc)
> +{
> +  symbol *func = find_symbol_for_pc (pc);
> +  if (func != nullptr)
> +    return find_symbol_funname (func);
> +
> +  gdb::unique_xmalloc_ptr<char> funname;  bound_minimal_symbol
> msymbol
> + = lookup_minimal_symbol_by_pc (pc);  if (msymbol.minsym != nullptr)
> +    funname = make_unique_xstrdup (msymbol.minsym->print_name ());
> +
> +  return funname;
> +}
> +
> +/* Print information of shadow stack frame info FRAME.  The output is
> +   formatted according to PRINT_WHAT.  For the meaning of PRINT_WHAT,
> see
> +   enum print_what comments in frame.h.  Note that PRINT_WHAT is
> overridden,
> +   if PRINT_OPTIONS.print_frame_info != print_frame_info_auto.  */
> +
> +static void
> +do_print_shadow_stack_frame_info
> +  (ui_out *uiout, const frame_print_options &fp_opts,
> +   const shadow_stack_frame_info &frame, print_what print_what) {
> +  /* On x86 there can be a shadow stack token at bit 63.  For x32,
> +     the address size is only 32 bit.  Thus, we still must use
> +     gdbarch_shadow_stack_element_size_aligned (and not
> +     gdbarch_addr_bit) to determine the width of the address to be
> +     printed.  */
> +  const int element_size
> +    = gdbarch_shadow_stack_element_size_aligned (frame.arch);
> +
> +  if (frame.type != ssp_frame_type::normal_frame)
> +    {
> +      std::string str;
> +      if (frame.type == ssp_frame_type::non_return_frame)
> +	{
> +	  /* For non-return frames, the string must have a value.  */
> +	   gdb_assert (frame.non_return_description.has_value ());
> +	   str = frame.non_return_description.value ();
> +	}
> +      else
> +	gdb_assert_not_reached ("Invalid shadow stack frame type.");
> +
> +      ui_out_emit_tuple tuple_emitter (uiout, "shadow-stack-frame");
> +      uiout->text ("#");
> +      uiout->field_fmt_signed (2, ui_left, "level", frame.level);
> +
> +      uiout->field_string ("func", str, metadata_style.style ());
> +
> +      uiout->text ("\n");
> +      gdb_flush (gdb_stdout);
> +      return;
> +    }
> +
> +  if (fp_opts.print_frame_info != print_frame_info_auto)
> +    {
> +      /* Use the specific frame information desired by the user.  */
> +      print_what
> +	= *print_frame_info_to_print_what (fp_opts.print_frame_info);
> +    }
> +
> +  /* At this point, since FRAME is a shadow stack frame which belongs to a
> +     return address, we should always have a valid symtab_and_line
> + object.  */  gdb_assert (frame.sal.has_value ());
> +
> +  const symtab_and_line &sal = frame.sal.value ();  if
> + (should_print_location (print_what) || sal.symtab == nullptr)
> +    {
> +      gdb::unique_xmalloc_ptr<char> funname = find_pc_funname
> + (frame.value);
> +
> +      ui_out_emit_tuple tuple_emitter (uiout, "shadow-stack-frame");
> +      uiout->text ("#");
> +      uiout->field_fmt_signed (2, ui_left, "level", frame.level);
> +
> +      uiout->field_string
> +	("addr", hex_string_custom (frame.value, element_size * 2),
> +	 address_style.style ());
> +
> +      uiout->text (" in ");
> +      print_funname (uiout, funname);
> +
> +      if (print_what != SHORT_LOCATION && sal.symtab != nullptr)
> +	print_filename (uiout, sal);
> +
> +      if (print_what != SHORT_LOCATION
> +	  && (funname == nullptr || sal.symtab == nullptr)
> +	  && sal.pspace != nullptr)
> +	{
> +	  const char *lib = solib_name_from_address (sal.pspace,
> +						     frame.value);
> +	  if (lib != nullptr)
> +	    print_lib (uiout, lib);
> +	}
> +
> +      uiout->text ("\n");
> +    }
> +
> +  if (print_what == SRC_LINE || print_what == SRC_AND_LOC)
> +    {
> +      bool mid_statement = pc_in_middle_of_statement (frame.value,
> + sal);
> +
> +      /* While for the ordinary backtrace printing of pc is based on
> +	 MID_STATEMENT determined by stack.c:frame_show_address and
> the
> +	 print configuration, for shadow stack backtrace we always print
> +	 the pc/address on the shadow stack.  */
> +      bool print_address = true;
> +      print_source (uiout, frame.arch, frame.value, sal, print_address,
> +		    mid_statement, "");
> +    }
> +
> +  gdb_flush (gdb_stdout);
> +}
> +
> +/* Redirect output to a temporary buffer for the duration of
> +   do_print_shadow_stack_frame_info.  */
> +
> +static void
> +print_shadow_stack_frame_info
> +  (const frame_print_options &fp_opts,
> +   const shadow_stack_frame_info &frame,
> +   print_what print_what)
> +{
> +  do_with_buffered_output
> +    (do_print_shadow_stack_frame_info, current_uiout,
> +     fp_opts, frame, print_what);
> +}
> +
> +/* Extract a char array which can be used for printing a reasonable
> +   error message for REASON.  REASON must not be NO_ERROR if passed
> +   to this function.  */
> +
> +static const char *
> +ssp_unwind_stop_reason_to_err_string (ssp_unwind_stop_reason reason) {
> +  switch (reason)
> +    {
> +    case ssp_unwind_stop_reason::memory_read_error:
> +      return _("shadow stack memory read failure");
> +    }
> +
> +  gdb_assert_not_reached ("invalid unwind stop reason."); }
> +
> +/* Read the memory at shadow stack pointer SSP and assign it to
> +   RETURN_VALUE.  In case of success, return true.  Otherwise,
> +   return false.  */
> +
> +static bool
> +read_shadow_stack_memory (gdbarch *gdbarch, CORE_ADDR ssp,
> +			  CORE_ADDR &return_value)
> +{
> +  /* On x86 there can be a shadow stack token at bit 63.  For x32, the
> +     address size is only 32 bit.  Thus, we still must use
> +     gdbarch_shadow_stack_element_size_aligned (and not
> gdbarch_addr_bit)
> +     to read the full element for x32 as well.  */
> +  const int element_size
> +    = gdbarch_shadow_stack_element_size_aligned (gdbarch);
> +
> +  const bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> +  return safe_read_memory_unsigned_integer (ssp,
> +					    element_size,
> +					    byte_order,
> +					    &return_value);
> +}
> +
> +/*  If possible, return the starting shadow stack frame info needed to handle
> +    COUNT outermost frames.  This is the equivalent function to
> +    stack.c:trailing_outermost_frame which is used to print the normal
> backtrace.
> +    FRAME should point to the innermost (newest) element of the shadow
> stack.
> +    RANGE is the shadow stack memory range [start_address, end_address)
> +    corresponding to FRAME's shadow stack pointer.  If COUNT is bigger than
> the
> +    number of elements on the shadow stack, return FRAME.  In case of
> failure,
> +    assign an appropriate ssp_unwind_stop_reason in
> +FRAME->UNWIND_STOP_REASON.  */
> +
> +static std::optional<shadow_stack_frame_info>
> +get_trailing_outermost_shadow_stack_frame_info
> +  (const std::pair<CORE_ADDR, CORE_ADDR> range,
> +   LONGEST count, shadow_stack_frame_info &frame) {
> +  gdb_assert (count > 0);
> +
> +  std::optional<shadow_stack_frame_info> trailing
> +    = std::optional<shadow_stack_frame_info> (frame);
> +
> +  std::optional<shadow_stack_frame_info> current = trailing;  while
> + (current.has_value () && count--)
> +    {
> +      QUIT;
> +      current = current->unwind_prev_shadow_stack_frame_info (range);
> +    }
> +
> +  /* Will stop when CURRENT reaches the outermost (oldest) element of the
> +     shadow stack.  TRAILING will be COUNT below it.  */
> +  while (current.has_value ())
> +    {
> +      QUIT;
> +      trailing = trailing->unwind_prev_shadow_stack_frame_info (range);
> +      /* Since current has a value until this point we can assume that
> +	 trailing also has a value.  */
> +      gdb_assert (trailing.has_value ());
> +      current = current->unwind_prev_shadow_stack_frame_info (range);
> +    }
> +
> +  return trailing;
> +}
> +
> +/* If possible, get shadow stack frame info for the shadow stack pointer
> +   SSP and its current frame LEVEL.  Pass FALLBACK_ARCH which can be used
> +   as fallback gdbarch in case the gdbarch cannot be extracted from the
> +   SAL.  Usually this is the gdbarch of the previous frame.  */
> +
> +static std::optional<shadow_stack_frame_info>
> +get_shadow_stack_frame_info
> +  (gdbarch *fallback_arch, const CORE_ADDR ssp, unsigned long level) {
> +  CORE_ADDR value;
> +  if (!read_shadow_stack_memory (fallback_arch, ssp, value))
> +    return {};
> +
> +  if (gdbarch_is_no_return_shadow_stack_address_p (fallback_arch))
> +    {
> +      std::optional<shadow_stack_frame_info> no_return_frame
> +	= gdbarch_is_no_return_shadow_stack_address (fallback_arch, ssp,
> +						     value, level);
> +      if (no_return_frame.has_value ())
> +	return *no_return_frame;
> +    }
> +
> +  /* At this point, we know that SSP points to VALUE which is a return
> +     address.  In contrast to find_frame_sal which is used for the normal
> +     backtrace command, VALUE always points at the return instruction
> +     (which is *after* the call instruction).  Since we want to get the
> +     line containing the call (because the call is where the user thinks
> +     the program is), we pass 1 here as second argument.  */
> + symtab_and_line sal = find_sal_for_pc (value, 1);  struct gdbarch
> + *sal_arch = get_sal_arch (sal);
> +
> +  /* Fall back to the previous gdbarch in case we cannot extract it from
> +     SAL.  */
> +  if (sal_arch == nullptr)
> +    sal_arch = fallback_arch;
> +
> +  return std::optional<shadow_stack_frame_info>
> +    ({ssp, value, level, sal_arch, sal,
> +      ssp_frame_type::normal_frame, {},
> +      ssp_unwind_stop_reason::no_error});
> +}
> +
> +std::optional<shadow_stack_frame_info>
> +shadow_stack_frame_info::unwind_prev_shadow_stack_frame_info
> +  (std::pair<CORE_ADDR, CORE_ADDR> range) {
> +  if (this->type == ssp_frame_type::normal_frame
> +      && !user_set_backtrace_options.backtrace_past_main
> +      && this->inside_main_func ())
> +    {
> +      /* Don't unwind past main().  */
> +      return {};
> +    }
> +
> +  /* If the user's backtrace limit has been exceeded, stop.  We must
> +     add two to the current level; one of those accounts for
> +     backtrace_limit being 1-based and the level being 0-based, and the
> +     other accounts for the level of the new frame instead of the level
> +     of the current frame.  */
> +  if (this->level + 2 > user_set_backtrace_options.backtrace_limit)
> +    return {};
> +
> +  CORE_ADDR new_ssp
> +    = update_shadow_stack_pointer (this->arch, this->ssp,
> +				   ssp_update_direction::outer);
> +
> +  if (gdbarch_top_addr_empty_shadow_stack_p (this->arch)
> +      && gdbarch_top_addr_empty_shadow_stack (this->arch, new_ssp,
> range))
> +    return {};
> +  else if (gdbarch_stack_grows_down (this->arch))
> +    {
> +      /* The shadow stack grows downwards.  */
> +      if (new_ssp >= range.second)
> +	{
> +	  /* We reached the outermost element of the shadow stack.  */
> +	  return {};
> +	}
> +      /* We updated new_ssp towards the outermost element of the shadow
> +	 stack before, and new_ssp must be pointing to shadow stack
> +	 memory.  */
> +      gdb_assert (new_ssp > range.first);
> +    }
> +  else
> +    {
> +      /* The shadow stack grows upwards.  */
> +      if (new_ssp < range.first)
> +	{
> +	  /* We reached the outermost element of the shadow stack.  */
> +	  return {};
> +	}
> +      /* We updated new_ssp towards the outermost element of the shadow
> +	 stack before, and new_ssp must be pointing to shadow stack
> +	 memory.  */
> +      gdb_assert (new_ssp <= range.second);
> +    }
> +
> +  /* Extract the previous shadow stack frame info (level + 1).  */
> + std::optional<shadow_stack_frame_info> new_frame
> +    = get_shadow_stack_frame_info (this->arch, new_ssp, this->level +
> + 1);
> +
> +  if (!new_frame.has_value ())
> +    this->unwind_stop_reason =
> + ssp_unwind_stop_reason::memory_read_error;
> +
> +  return new_frame;
> +}
> +
> +bool
> +shadow_stack_frame_info::inside_main_func () const {
> +  language lang;
> +  symbol *sym = find_symbol_for_pc (this->value);
> +  if (sym != nullptr)
> +    lang = sym->language ();
> +  else
> +    {
> +      bound_minimal_symbol msymbol
> +	= lookup_minimal_symbol_by_pc (this->value);
> +
> +      if (msymbol.minsym != nullptr)
> +	lang = msymbol.minsym->language ();
> +      else
> +	{
> +	  /* If we cannot extract the symbol, we cannot use the address to
> +	     find out if the function is inside main and we better print
> +	     this frame, too.  */
> +	  return false;
> +	}
> +    }
> +
> +  std::optional<CORE_ADDR> sym_addr
> +    = get_main_func_start_pc (this->arch, lang);  if
> + (!sym_addr.has_value () || *sym_addr == 0)
> +    return false;
> +
> +  CORE_ADDR pc_func_start = get_pc_function_start (this->value);  if
> + (*sym_addr == pc_func_start)
> +    return true;
> +
> +  return false;
> +}
> +
> +/* Print all elements on the shadow stack or just the innermost COUNT_EXP
> +   frames.  */
> +
> +void
> +backtrace_shadow_command (const frame_print_options &fp_opts,
> +			  const char *count_exp, int from_tty) {
> +  if (!target_has_stack ())
> +    error (_("No shadow stack."));
> +
> +  gdbarch *gdbarch = get_current_arch ();
> +  if (!gdbarch_address_in_shadow_stack_memory_range_p (gdbarch)
> +      || !gdbarch_top_addr_empty_shadow_stack_p (gdbarch)
> +      || gdbarch_ssp_regnum (gdbarch) == -1)
> +    error (_("Printing of the shadow stack backtrace is not supported for"
> +	     " the current target."));
> +
> +  regcache *regcache = get_thread_regcache (inferior_thread ());  bool
> + shadow_stack_enabled = false;
> +
> +  std::optional<CORE_ADDR> start_ssp
> +    = gdbarch_get_shadow_stack_pointer (gdbarch, regcache,
> +					shadow_stack_enabled);
> +
> +  if (!start_ssp.has_value () || !shadow_stack_enabled)
> +    error (_("Shadow stack is not enabled for the current thread."));
> +
> +  /* Check if START_SSP points to a shadow stack memory range and use
> +     the returned range to determine when to stop unwinding.
> +     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
> +     command without support for shadow stack switches for now.  */
> +  bool is_top_addr_empty_shadow_stack = false;
> +  std::pair<CORE_ADDR, CORE_ADDR> range;
> +  if (!gdbarch_address_in_shadow_stack_memory_range (gdbarch,
> *start_ssp,
> +						     &range))
> +    {
> +      /* For x86, if the current shadow stack pointer does not point to
> +	 shadow stack memory but is valid, the shadow stack is empty.  */
> +      is_top_addr_empty_shadow_stack = true;
> +    }
> +
> +  if (!is_top_addr_empty_shadow_stack)
> +    {
> +      /* For ARM's Guarded Control Stack, the shadow stack can be empty
> +	 even though START_SSP points to shadow stack memory range.  */
> +      is_top_addr_empty_shadow_stack
> +	= gdbarch_top_addr_empty_shadow_stack_p (gdbarch)
> +	  && gdbarch_top_addr_empty_shadow_stack (gdbarch, *start_ssp,
> range);
> +    }
> +
> +  if (is_top_addr_empty_shadow_stack)
> +    {
> +      gdb_printf (_("The shadow stack is empty.\n"));
> +      return;
> +    }
> +
> +  /* Extract the first shadow stack frame info (level 0).  */
> + ssp_unwind_stop_reason reason = ssp_unwind_stop_reason::no_error;
> + std::optional<shadow_stack_frame_info> current
> +    = get_shadow_stack_frame_info (gdbarch, *start_ssp, 0);
> +
> +  if (!current.has_value ())
> +    reason = ssp_unwind_stop_reason::memory_read_error;
> +
> +  std::optional<shadow_stack_frame_info> trailing = current;
> +
> +  LONGEST count = -1;
> +  if (current.has_value () && count_exp != nullptr)
> +    {
> +      count = parse_and_eval_long (count_exp);
> +      /* If count is negative, update trailing with the shadow stack frame
> +	 info from which we should start printing.  */
> +      if (count < 0)
> +	{
> +	  trailing = get_trailing_outermost_shadow_stack_frame_info
> +		       (range, std::abs (count), *current);
> +
> +	  if (!trailing.has_value ())
> +	    reason = current->unwind_stop_reason;
> +	}
> +    }
> +
> +  if (!trailing.has_value ())
> +    {
> +      gdb_assert (reason != ssp_unwind_stop_reason::no_error);
> +
> +      error (_("Cannot print shadow stack backtrace: %s.\n"),
> +	     ssp_unwind_stop_reason_to_err_string (reason));
> +    }
> +
> +  for (current = trailing; current.has_value () && count != 0; count--)
> +    {
> +      QUIT;
> +
> +      print_shadow_stack_frame_info (fp_opts, *current, LOCATION);
> +
> +      trailing = current;
> +      current = current->unwind_prev_shadow_stack_frame_info (range);
> +    }
> +
> +  /* If we've stopped before the end, mention that.  */  if
> + (current.has_value () && from_tty)
> +    gdb_printf (_("(More shadow stack frames follow...)\n"));
> +
> +  /* Due to the loop above, trailing always has a value at this point.
> + */  gdb_assert (trailing.has_value ());
> +
> +  /* If we've run out of shadow stack frames, and the reason appears to
> +     be an error condition, print it.  */
> +  if (!current.has_value ()
> +      && trailing->unwind_stop_reason > ssp_unwind_stop_reason::no_error)
> +    gdb_printf (_("Shadow stack backtrace stopped at shadow stack " \
> +		  "pointer %s due to: %s.\n"),
> +		paddress (trailing->arch, trailing->ssp),
> +		ssp_unwind_stop_reason_to_err_string
> +		  (trailing->unwind_stop_reason));
> +}
> diff --git a/gdb/shadow-stack.h b/gdb/shadow-stack.h index
> 5f540ff7d62..4278620c70c 100644
> --- a/gdb/shadow-stack.h
> +++ b/gdb/shadow-stack.h
> @@ -21,9 +21,17 @@
>  #ifndef GDB_SHADOW_STACK_H
>  #define GDB_SHADOW_STACK_H
> 
> +#include <optional>
> +#include <utility>
> +#include <string>
> +
> +#include "symtab.h"
> +
>  class regcache;
>  class frame_info_ptr;
>  struct value;
> +struct frame_print_options;
> +struct gdbarch;
> 
>  /* If shadow stack is enabled, push the address NEW_ADDR on the shadow
>     stack and update the shadow stack pointer accordingly.  */ @@ -39,4
> +47,87 @@ void shadow_stack_push (regcache *regcache, const
> CORE_ADDR new_addr);  value *dwarf2_prev_ssp (const frame_info_ptr
> &this_frame,
>  			void **this_cache, int regnum);
> 
> +/* Implementation of "backtrace -shadow" command line option.  */
> +
> +void backtrace_shadow_command
> +  (const frame_print_options &fp_opts,
> +   const char *count_exp, int from_tty);
> +
> +enum class ssp_unwind_stop_reason
> +{
> +  /* No particular reason; either we haven't tried unwinding yet, or we
> +     didn't fail.  */
> +  no_error = 0,
> +
> +  /* We could not read the memory of the shadow stack element.  */
> +  memory_read_error,
> +};
> +
> +enum class ssp_frame_type
> +{
> +  /* A normal shadow stack frame which belongs to a return address of the
> +     program execution flow.  */
> +  normal_frame,
> +
> +  /* A shadow stack frame which does not belong to a return address.
> +     It is possible, on x86 for instance, that an element on the shadow
> +     stack is not a return address.  We don't want to print the address
> +     in that case but only a string describing that specific frame type.
> +     This frame type is configured in the target specific implementation
> +     of is_no_return_shadow_stack_address.  */
> +  non_return_frame,
> +};
> +
> +/* Information of a shadow stack frame belonging to a shadow stack
> element
> +   at shadow stack pointer SSP.  */
> +
> +class shadow_stack_frame_info
> +{
> +public:
> +  /* If possible, unwind the previous shadow stack frame info.  RANGE is
> +     the shadow stack memory range [start_address, end_address) belonging
> +     to this frame's shadow stack pointer.  If we cannot unwind the
> +     previous frame info, set the unwind_stop_reason attribute.  If we
> +     reached the bottom of the shadow stack just don't return a value.
> +*/
> +  std::optional<shadow_stack_frame_info>
> unwind_prev_shadow_stack_frame_info
> +    (std::pair<CORE_ADDR, CORE_ADDR> range);
> +
> +  /* The shadow stack pointer.  */
> +  CORE_ADDR ssp;
> +
> +  /* The value of the shadow stack at SSP.  */  CORE_ADDR value;
> +
> +  /* The level of the element on the shadow stack.  */  unsigned long
> + level;
> +
> +  /* Architecture of the current shadow stack frame.  */  struct
> + gdbarch *arch;
> +
> +  /* Optional SAL object of the current shadow stack frame.  */
> + std::optional<symtab_and_line> sal;
> +
> +  /* The shadow stack frame type.  */
> +  ssp_frame_type type;
> +
> +  /* If this is not a normal shadow stack frame belonging to a return
> +     address of the program execution flow then the frame will have the
> +     type ssp_frame_type::non_return_frame.  In this case, this string
> +     is configured to describe this specific frame type and it will
> +     be printed in the shadow stack backtrace instead of the element on
> +     the shadow stack.  */
> +  std::optional<std::string> non_return_description;
> +
> +  /* If unwinding of the previous frame info fails assign this value to a
> +     matching condition ssp_unwind_stop_reason
> +     > ssp_unwind_stop_reason::no_error.  */  ssp_unwind_stop_reason
> + unwind_stop_reason
> +    = ssp_unwind_stop_reason::no_error;
> +
> +  /* Return true if this frame is inside the "main"() function.  If the frame
> +     is outside the "main"() function, does not have a SAL object or if the
> +     symbol information cannot be extracted, return false.  */
> +  bool inside_main_func () const;
> +};
> +
>  #endif /* GDB_SHADOW_STACK_H */
> diff --git a/gdb/stack.c b/gdb/stack.c
> index 7df1d6e33da..e96fbe73947 100644
> --- a/gdb/stack.c
> +++ b/gdb/stack.c
> @@ -51,6 +51,7 @@
>  #include "cli/cli-utils.h"
>  #include "objfiles.h"
>  #include "buffered-streams.h"
> +#include "shadow-stack.h"
> 
>  #include "symfile.h"
>  #include "extension.h"
> @@ -191,6 +192,7 @@ struct backtrace_cmd_options
>    bool full = false;
>    bool no_filters = false;
>    bool hide = false;
> +  bool shadow = false;
>  };
> 
>  using bt_flag_option_def
> @@ -214,6 +216,14 @@ static const gdb::option::option_def
> backtrace_command_option_defs[] = {
>      [] (backtrace_cmd_options *opt) { return &opt->hide; },
>      N_("Causes Python frame filter elided frames to not be printed."),
>    },
> +
> +  bt_flag_option_def {
> +    "shadow",
> +    [] (backtrace_cmd_options *opt) { return &opt->shadow; },
> +    N_("Print shadow stack frames instead of normal frames.\n\ This
> +option may be combined with \"-frame-info\" and\n\ implies
> +'-no-filters' and '-frame-arguments none'."),
> +  },
>  };
> 
>  /* Prototypes for local functions.  */
> @@ -906,11 +916,9 @@ do_gdb_disassembly (struct gdbarch *gdbarch,
>      }
>  }
> 
> -/* Converts the PRINT_FRAME_INFO choice to an optional enum print_what.
> -   Value not present indicates to the caller to use default values
> -   specific to the command being executed.  */
> +/* See stack.h.  */
> 
> -static std::optional<enum print_what>
> +std::optional<enum print_what>
>  print_frame_info_to_print_what (const char *print_frame_info)  {
>    for (int i = 0; print_frame_info_choices[i] != NULL; i++) @@ -960,7 +968,7
> @@ get_user_print_what_frame_info (std::optional<enum print_what>
> *what)
>  /* Return true if PRINT_WHAT is configured to print the location of a
>     frame.  */
> 
> -static bool
> +bool
>  should_print_location (print_what print_what)  {
>    return (print_what == LOCATION
> @@ -969,14 +977,9 @@ should_print_location (print_what print_what)
>  	  || print_what == SHORT_LOCATION);
>  }
> 
> -/* Print the source information for PC and SAL to UIOUT.  Based on the
> -   user-defined configuration disassemble-next-line, display disassembly
> -   of the next source line, in addition to displaying the source line
> -   itself.  Print annotations describing source file and and line number
> -   based on MID_STATEMENT information.  If SHOW_ADDRESS is true, print
> the
> -   program counter PC including, if non-empty, PC_ADDRESS_FLAGS.  */
> +/* See stack.h.  */
> 
> -static void
> +void
>  print_source (ui_out *uiout, gdbarch *gdbarch, CORE_ADDR pc,
>  	      symtab_and_line sal, bool show_address, int mid_statement,
>  	      const std::string &pc_address_flags) @@ -1242,7 +1245,7 @@
> get_last_displayed_sal ()
> 
>  /* Find the function name for the symbol SYM.  */
> 
> -static gdb::unique_xmalloc_ptr<char>
> +gdb::unique_xmalloc_ptr<char>
>  find_symbol_funname (const symbol *sym)  {
>    gdb::unique_xmalloc_ptr<char> funname; @@ -1308,10 +1311,9 @@
> find_frame_funname (const frame_info_ptr &frame, enum language
> *funlang,
>    return funname;
>  }
> 
> -/* Print the library LIB to UIOUT for the printing of frame
> -   information.  */
> +/*  See stack.h.  */
> 
> -static void
> +void
>  print_lib (ui_out *uiout, const char *lib)  {
>    annotate_frame_where ();
> @@ -1320,10 +1322,7 @@ print_lib (ui_out *uiout, const char *lib)
>    uiout->field_string ("from", lib, file_name_style.style ());  }
> 
> -/* Print the filenname of SAL to UIOUT for the printing of frame
> -   information.  */
> -
> -static void
> +void
>  print_filename (ui_out *uiout, symtab_and_line sal)  {
>    annotate_frame_source_begin ();
> @@ -1348,10 +1347,9 @@ print_filename (ui_out *uiout, symtab_and_line
> sal)
>    annotate_frame_source_end ();
>  }
> 
> -/*  If available, print FUNNAME to UIOUT for the printing of frame
> -    information.  */
> +/*  See stack.h.  */
> 
> -static void
> +void
>  print_funname (ui_out *uiout,
>  	       gdb::unique_xmalloc_ptr<char> const &funname)  { @@ -2162,7
> +2160,10 @@ backtrace_command (const char *arg, int from_tty)
>    scoped_restore restore_set_backtrace_options
>      = make_scoped_restore (&user_set_backtrace_options, set_bt_opts);
> 
> -  backtrace_command_1 (fp_opts, bt_cmd_opts, arg, from_tty);
> +  if (!bt_cmd_opts.shadow)
> +    backtrace_command_1 (fp_opts, bt_cmd_opts, arg, from_tty);  else
> +    backtrace_shadow_command (fp_opts, arg, from_tty);
>  }
> 
>  /* Completer for the "backtrace" command.  */
> diff --git a/gdb/stack.h b/gdb/stack.h
> index ad2700b59a7..3f4cf348841 100644
> --- a/gdb/stack.h
> +++ b/gdb/stack.h
> @@ -84,4 +84,47 @@ void frame_apply_all_cmd_completer (struct
> cmd_list_element *ignore,
>  				    completion_tracker &tracker,
>  				    const char *text, const char */*word*/);
> 
> +/* Print the filename of SAL to UIOUT for the printing of frame
> +   information.  */
> +
> +void print_filename (ui_out *uiout, symtab_and_line sal);
> +
> +/* Print the library LIB to UIOUT for the printing of frame
> +   information.  */
> +
> +void print_lib (ui_out *uiout, const char *lib);
> +
> +/* If available, print FUNNAME to UIOUT for the printing of frame
> +   information.  */
> +
> +void print_funname (ui_out *uiout,
> +		    gdb::unique_xmalloc_ptr<char> const &funname);
> +
> +/* Converts the PRINT_FRAME_INFO choice to an optional enum
> print_what.
> +   Value not present indicates to the caller to use default values
> +   specific to the command being executed.  */
> +
> +std::optional<print_what> print_frame_info_to_print_what
> +  (const char *print_frame_info);
> +
> +/* Return true if PRINT_WHAT is configured to print the location of a
> +   frame.  */
> +
> +bool should_print_location (print_what print_what);
> +
> +/* Print the source information for PC and SAL to UIOUT.  Based on the
> +   user-defined configuration disassemble-next-line, display disassembly
> +   of the next source line, in addition to displaying the source line
> +   itself.  Print annotations describing source file and and line number
> +   based on MID_STATEMENT information.  If SHOW_ADDRESS is true, print
> the
> +   program counter PC including, if non-empty, PC_ADDRESS_FLAGS.  */
> +
> +void print_source (ui_out *uiout, gdbarch *gdbarch, CORE_ADDR pc,
> +		   symtab_and_line sal, bool show_address,
> +		   int mid_statement, const std::string &pc_address_flags);
> +
> +/* Find the function name for the symbol SYM.  */
> +
> +gdb::unique_xmalloc_ptr<char> find_symbol_funname (const symbol
> *sym);
> +
>  #endif /* GDB_STACK_H */
> diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> index e67a0c2e4a1..0347313b647 100644
> --- a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> @@ -109,6 +109,53 @@ save_vars { ::env(GLIBC_TUNABLES) } {
>  	gdb_test "print /x \$pl3_ssp" "= $ssp_call2" "check pl3_ssp of frame
> 0"
>      }
> 
> +    set fill "\[^\r\n\]+"
> +    # Build shadow stack frames to test the 'backtrace -shadow' command.
> +    set sspval_main [get_valueof "/z" "*(long long int*)$ssp_main" ""]
> +    set sspval_call1 [get_valueof "/z" "*(long long int*)$ssp_call1" ""]
> +    set sspval_call2 [get_valueof "/z" "*(long long int*)$ssp_call2" ""]
> +    set frame1 "#0\[ \t\]*$sspval_call2 in call1$fill"
> +    set frame2 "#1\[ \t\]*$sspval_call1 in main$fill"
> +    set frame3 "#2\[ \t\]*$sspval_main$fill"
> +
> +    # Test bt -shadow, which prints until the main function.
> +    gdb_test "bt -shadow" \
> +	[multi_line \
> +	    "$frame1" \
> +	    "$frame2" ] \
> +	"test shadow stack backtrace until main."
> +
> +    # With -past-main set, we can still only test that we print the first
> +    # 3 frames correctly, as the shadow stack enablement might depend on
> +    # the underlying OS.
> +    gdb_test "bt -shadow -past-main" \
> +	[multi_line \
> +	    "$frame1" \
> +	    "$frame2" \
> +	    "$frame3" \
> +	    ".*" ] \
> +	"test shadow stack backtrace until the bottom of the stack."
> +
> +    gdb_test "bt -shadow 1" \
> +	[multi_line \
> +	    "$frame1" \
> +	    "\\(More shadow stack frames follow...\\)" ] \
> +	"test shadow stack backtrace with a positive value for count"
> +
> +    # We can only test that we print a single frame, as the shadow stack
> +    # enablement might depend on the underlying OS.
> +    gdb_test "bt -shadow -1" "^#$decimal\[ \t\]*$hex$fill" \
> +	"test shadow stack backtrace with a negative value for count"
> +
> +    # Test backtrace limit
> +    gdb_test_no_output "set backtrace limit 2"
> +    gdb_test "bt -shadow" \
> +	[multi_line \
> +	    "$frame1" \
> +	    "$frame2" ] \
> +	"test shadow stack backtrace with limit"
> +    gdb_test_no_output "set backtrace limit unlimited" "restore backtrace
> limit default"
> +
>      with_test_prefix "test return from current frame" {
>  	gdb_test "return (int) 1" "#0.*call1.*" \
>  	"Test shadow stack return from current frame" \
> diff --git a/gdb/testsuite/gdb.base/options.exp
> b/gdb/testsuite/gdb.base/options.exp
> index 35487ead6a7..8ca6e677aba 100644
> --- a/gdb/testsuite/gdb.base/options.exp
> +++ b/gdb/testsuite/gdb.base/options.exp
> @@ -286,7 +286,6 @@ proc_with_prefix test-backtrace {} {
>      clean_restart
> 
>      test_gdb_complete_unique "backtrace" "backtrace"
> -    test_gdb_complete_none "backtrace "
> 
>      gdb_test "backtrace -" "Ambiguous option at: -"
>      gdb_test "backtrace --" "No stack\\."
> @@ -302,6 +301,7 @@ proc_with_prefix test-backtrace {} {
>  	"-past-entry"
>  	"-past-main"
>  	"-raw-frame-arguments"
> +	"-shadow"
>      }
> 
>      # Test that we complete the qualifiers, if there's any.
> --
> 2.34.1
> 
> Intel Deutschland GmbH
> 
> Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
> Tel: +49 89 991 430, www.intel.de
> Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
> Chairperson of the Supervisory Board: Nicole Lau
> Registered Seat: Munich
> Commercial Register: Amtsgericht Muenchen HRB 186928
> 

Intel Deutschland GmbH

Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

  reply	other threads:[~2026-07-08  9:14 UTC|newest]

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

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=SN7PR11MB7638B841D7927ECC93B2A547F9FF2@SN7PR11MB7638.namprd11.prod.outlook.com \
    --to=christina.schimpe@intel.com \
    --cc=gdb-patches@sourceware.org \
    --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