From: Christina Schimpe <christina.schimpe@intel.com>
To: gdb-patches@sourceware.org
Subject: [PATCH 6/9] gdb: Implement 'bt shadow' to print the shadow stack backtrace.
Date: Tue, 23 Sep 2025 11:18:39 +0000 [thread overview]
Message-ID: <20250923111842.4091694-7-christina.schimpe@intel.com> (raw)
In-Reply-To: <20250923111842.4091694-1-christina.schimpe@intel.com>
Add a subcommand 'bt shadow' for the ordinary backtrace command which
prints the shadow stack backtrace.
Similar to the ordinary backtrace command 'bt shadow' can be configured
using COUNT and the command line option -frame-info. However, we always
print the address and the command is not affected by the setting
"print address" as well as the setting "print frame-info location-and-address".
Also we do not print the frame arguments.
Usage: backtrace|bt shadow [OPTION]... [COUNT | -COUNT]
Help output:
~~
(gdb) help bt shadow
Print backtrace of all shadow stack frames, or innermost COUNT frames.
Usage: backtrace shadow [OPTION]... [COUNT | -COUNT]
Options:
-frame-info auto|source-line|location|source-and-location|location-and-address|short-location
Set printing of shadow stack frame information.
With a negative COUNT, print outermost -COUNT elements.
~~
Example for the output of 'bt shadow' on amd64 linux:
~~
(gdb) bt shadow
/#0 0x000000000040111f in call1 at amd64-shadow-stack.c:14
/#1 0x000000000040112f in main at amd64-shadow-stack.c:21
/#2 0x00007ffff7c3fe70 in __libc_start_call_main at ../sysdeps/nptl/libc_start_call_main.h:58
/#3 0x00007ffff7c3ff20 in __libc_start_main_impl at ../csu/libc-start.c:128
/#4 0x0000000000401045 in _start
~~
This commit also adds a test for 'bt shadow' on amd64. Note that
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 | 7 +
gdb/annotate.c | 93 +++-
gdb/annotate.h | 18 +-
gdb/doc/gdb.texinfo | 48 ++
gdb/shadow-stack.c | 504 +++++++++++++++++-
gdb/shadow-stack.h | 19 +
gdb/stack.c | 104 ++--
gdb/stack.h | 55 ++
.../gdb.arch/amd64-shadow-stack-cmds.exp | 88 +++
gdb/testsuite/gdb.base/help.exp | 6 +-
gdb/testsuite/gdb.base/options.exp | 7 +-
11 files changed, 885 insertions(+), 64 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 8be367d2424..eb96ff2ae08 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -33,6 +33,13 @@ single-inf-arg in qSupported
reply with the single-inf-arg feature to indicate that it is able to
accept arguments as a single string.
+* New commands
+
+backtrace shadow [option]... [count | -count]
+bt shadow [option]... [count | -count]
+ Print backtrace of all shadow stack frames, or innermost 'count' frames.
+ The command is a subcommand of the ordinary backtrace command.
+
*** Changes in GDB 17
* Debugging Linux programs that use x86-64 or x86-64 with 32-bit pointer
diff --git a/gdb/annotate.c b/gdb/annotate.c
index 55db2e2e8e1..5acae566095 100644
--- a/gdb/annotate.c
+++ b/gdb/annotate.c
@@ -506,10 +506,15 @@ annotate_frame_address_end (void)
}
void
-annotate_frame_function_name (void)
+annotate_frame_function_name (bool shadowstack_frame)
{
if (annotation_level == 2)
- printf_unfiltered (("\n\032\032frame-function-name\n"));
+ {
+ if (!shadowstack_frame)
+ printf_unfiltered (("\n\032\032frame-function-name\n"));
+ else
+ printf_unfiltered (("\n\032\032shadow-stack-frame-function-name\n"));
+ }
}
void
@@ -520,45 +525,75 @@ annotate_frame_args (void)
}
void
-annotate_frame_source_begin (void)
+annotate_frame_source_begin (bool shadowstack_frame)
{
if (annotation_level == 2)
- printf_unfiltered (("\n\032\032frame-source-begin\n"));
+ {
+ if (!shadowstack_frame)
+ printf_unfiltered (("\n\032\032frame-source-begin\n"));
+ else
+ printf_unfiltered (("\n\032\032shadow-stack-frame-source-begin\n"));
+ }
}
void
-annotate_frame_source_file (void)
+annotate_frame_source_file (bool shadowstack_frame)
{
if (annotation_level == 2)
- printf_unfiltered (("\n\032\032frame-source-file\n"));
+ {
+ if (!shadowstack_frame)
+ printf_unfiltered (("\n\032\032frame-source-file\n"));
+ else
+ printf_unfiltered (("\n\032\032shadow-stack-frame-source-file\n"));
+ }
}
void
-annotate_frame_source_file_end (void)
+annotate_frame_source_file_end (bool shadowstack_frame)
{
if (annotation_level == 2)
- printf_unfiltered (("\n\032\032frame-source-file-end\n"));
+ {
+ if (!shadowstack_frame)
+ printf_unfiltered (("\n\032\032frame-source-file-end\n"));
+ else
+ printf_unfiltered (("\n\032\032shadow-stack-frame-source-file-end\n"));
+ }
}
void
-annotate_frame_source_line (void)
+annotate_frame_source_line (bool shadowstack_frame)
{
if (annotation_level == 2)
- printf_unfiltered (("\n\032\032frame-source-line\n"));
+ {
+ if (!shadowstack_frame)
+ printf_unfiltered (("\n\032\032frame-source-line\n"));
+ else
+ printf_unfiltered (("\n\032\032shadow-stack-frame-source-line\n"));
+ }
}
void
-annotate_frame_source_end (void)
+annotate_frame_source_end (bool shadowstack_frame)
{
if (annotation_level == 2)
- printf_unfiltered (("\n\032\032frame-source-end\n"));
+ {
+ if (!shadowstack_frame)
+ printf_unfiltered (("\n\032\032frame-source-end\n"));
+ else
+ printf_unfiltered (("\n\032\032shadow-stack-frame-source-end\n"));
+ }
}
void
-annotate_frame_where (void)
+annotate_frame_where (bool shadowstack_frame)
{
if (annotation_level == 2)
- printf_unfiltered (("\n\032\032frame-where\n"));
+ {
+ if (!shadowstack_frame)
+ printf_unfiltered (("\n\032\032frame-where\n"));
+ else
+ printf_unfiltered (("\n\032\032shadow-stack-frame-where\n"));
+ }
}
void
@@ -568,6 +603,36 @@ annotate_frame_end (void)
printf_unfiltered (("\n\032\032frame-end\n"));
}
\f
+
+/* Annotations for shadow stack frames. */
+
+void
+annotate_shadowstack_frame_begin (int level, gdbarch *gdbarch, CORE_ADDR pc)
+{
+ if (annotation_level > 1)
+ printf_unfiltered (("\n\032\032shadow-stack-frame-begin %d %s\n"),
+ level, paddress (gdbarch, pc));
+}
+
+void annotate_shadowstack_frame_address (void)
+{
+ if (annotation_level == 2)
+ printf_unfiltered (("\n\032\032shadow-stack-frame-address\n"));
+}
+
+void
+annotate_shadowstack_frame_address_end (void)
+{
+ if (annotation_level == 2)
+ printf_unfiltered (("\n\032\032shadow-stack-frame-address-end\n"));
+}
+
+void annotate_shadowstack_frame_end ()
+{
+ if (annotation_level == 2)
+ printf_unfiltered (("\n\032\032shadow-stack-frame-end\n"));
+}
+
void
annotate_array_section_begin (int idx, struct type *elttype)
{
diff --git a/gdb/annotate.h b/gdb/annotate.h
index 0f394f5ccdc..675b0b771a0 100644
--- a/gdb/annotate.h
+++ b/gdb/annotate.h
@@ -121,15 +121,19 @@ extern void annotate_function_call (void);
extern void annotate_signal_handler_caller (void);
extern void annotate_frame_address (void);
extern void annotate_frame_address_end (void);
-extern void annotate_frame_function_name (void);
+extern void annotate_frame_function_name (bool shadowstack_frame = false);
extern void annotate_frame_args (void);
-extern void annotate_frame_source_begin (void);
-extern void annotate_frame_source_file (void);
-extern void annotate_frame_source_file_end (void);
-extern void annotate_frame_source_line (void);
-extern void annotate_frame_source_end (void);
-extern void annotate_frame_where (void);
+extern void annotate_frame_source_begin (bool shadowstack_frame = false);
+extern void annotate_frame_source_file (bool shadowstack_frame = false);
+extern void annotate_frame_source_file_end (bool shadowstack_frame = false);
+extern void annotate_frame_source_line (bool shadowstack_frame = false);
+extern void annotate_frame_source_end (bool shadowstack_frame = false);
+extern void annotate_frame_where (bool shadowstack_frame = false);
extern void annotate_frame_end (void);
+extern void annotate_shadowstack_frame_begin (int, gdbarch *, CORE_ADDR);
+extern void annotate_shadowstack_frame_address (void);
+extern void annotate_shadowstack_frame_address_end (void);
+extern void annotate_shadowstack_frame_end (void);
extern void annotate_array_section_begin (int, struct type *);
extern void annotate_elt_rep (unsigned int);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index e8515883820..ebda4546b58 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -8840,6 +8840,53 @@ Display an absolute filename.
Show the current way to display filenames.
@end table
+@subsection Shadow stack backtrace
+@value{GDBN} provides a subcommand of the backtrace command to print the
+shadow stack backtrace.
+
+@anchor{shadowstack-backtrace-command}
+@kindex backtrace shadow
+@kindex bt shadow @r{(@code{backtrace shadow})}
+To print a backtrace of the entire shadow stack, use the
+@code{backtrace shadow} command, or its alias @code{bt shadow}. This
+command will print 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 (@xref{CET}) on x86.
+
+@table @code
+@item backtrace shadow [@var{option}]@dots{} @dots{} [@var{count}]
+@itemx bt shadow [@var{option}]@dots{} @dots{} [@var{count}]
+Print the backtrace of all shadow stack frames
+@end table
+
+In contrast to the backtrace command,
+
+@itemize @bullet
+@item the options @samp{-past-main} or @samp{-past-entry} are not supported
+and all available shadow stack frames are printed by default
+@item Python frame filters are not supported
+@item frame local variables are not printed
+@item frame arguments are not printed and therefore the options
+@samp{-frame-arguments} and @samp{-raw-frame-arguments} are not supported.
+@item configuring the option @samp{-frame-info} or its corresponding
+global setting @ref{set print frame-info} to @code{location} is treated
+same as configuring it to @code{location-and-address}, as @value{GDBN}
+always prints the address for shadow stack frames.
+@end itemize
+
+This is how a shadow stack backtrace looks like on amd64:
+@smallexample
+@group
+#0 0x000000000040111f in call1 at amd64-shadow-stack.c:14
+#1 0x000000000040112f in main at amd64-shadow-stack.c:21
+#2 0x00007ffff7c3fe70 in __libc_start_call_main at ../sysdeps/nptl/libc_start_call_main.h:58
+#3 0x00007ffff7c3ff20 in __libc_start_main_impl at ../csu/libc-start.c:128
+#4 0x0000000000401045 in _start
+@end group
+@end smallexample
+
@node Selection
@section Selecting a Frame
@@ -27132,6 +27179,7 @@ registers
@end itemize
+@node CET
@subsubsection Intel Control-Flow Enforcement Technology.
@cindex Intel Control-Flow Enforcement Technology.
diff --git a/gdb/shadow-stack.c b/gdb/shadow-stack.c
index d153d5fc846..759ef6989ea 100644
--- a/gdb/shadow-stack.c
+++ b/gdb/shadow-stack.c
@@ -23,6 +23,11 @@
#include "frame.h"
#include "frame-unwind.h"
#include "shadow-stack.h"
+#include "annotate.h"
+#include "stack.h"
+#include "solib.h"
+#include "event-top.h"
+#include "cli/cli-style.h"
enum class ssp_update_direction
{
@@ -38,17 +43,19 @@ enum class ssp_update_direction
static CORE_ADDR
update_shadow_stack_pointer (gdbarch *gdbarch, CORE_ADDR ssp,
+ const unsigned int count,
const ssp_update_direction direction)
{
bool increment = (gdbarch_stack_grows_down (gdbarch))
? (direction == ssp_update_direction::bottom)
: (direction == ssp_update_direction::top);
- CORE_ADDR new_ssp;
+ const int element_size
+ = gdbarch_shadow_stack_element_size_aligned (gdbarch);
if (increment)
- return ssp + gdbarch_shadow_stack_element_size_aligned (gdbarch);
+ return ssp + count * element_size;
else
- return ssp - gdbarch_shadow_stack_element_size_aligned (gdbarch);
+ return ssp - count * element_size;
}
/* See shadow-stack.h. */
@@ -68,7 +75,7 @@ void shadow_stack_push (gdbarch *gdbarch, regcache *regcache,
return;
const CORE_ADDR new_ssp
- = update_shadow_stack_pointer (gdbarch, *ssp,
+ = update_shadow_stack_pointer (gdbarch, *ssp, 1,
ssp_update_direction::top);
/* If NEW_SSP does not point to shadow stack memory, we assume the stack
@@ -131,7 +138,7 @@ dwarf2_prev_ssp (const frame_info_ptr &this_frame, void **this_cache,
implement this unwinder without support for shadow stack
switches for now. */
const CORE_ADDR new_ssp
- = update_shadow_stack_pointer (gdbarch, ssp,
+ = update_shadow_stack_pointer (gdbarch, ssp, 1,
ssp_update_direction::bottom);
/* On x86, if NEW_SSP points to the end of RANGE, it indicates
@@ -165,3 +172,490 @@ dwarf2_prev_ssp (const frame_info_ptr &this_frame, void **this_cache,
return retval;
}
+
+/* Option definitions for some shadow stack related settings. */
+
+using shadowstack_enum_option_def
+ = gdb::option::enum_option_def<shadow_stack_print_options>;
+
+static const gdb::option::option_def shadowstack_print_option_defs[] = {
+
+ shadowstack_enum_option_def {
+ "frame-info",
+ print_frame_info_choices,
+ [] (shadow_stack_print_options *opt) { return &opt->print_frame_info; },
+ nullptr, /* show_cmd_cb */
+ N_("Set printing of shadow stack frame information."),
+ N_("Show printing of shadow stack frame information."),
+ nullptr /* help_doc */
+ }
+
+};
+
+/* 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_frame_sal). 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;
+}
+
+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
+};
+
+/* 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
+ (gdbarch *gdbarch, 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;
+ /* 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;
+};
+
+static
+gdb::unique_xmalloc_ptr<char> find_pc_funname (CORE_ADDR pc)
+{
+ symbol *func = find_pc_function (pc);
+ if (func)
+ 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.reset (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, gdbarch *gdbarch,
+ const shadow_stack_print_options &print_options,
+ const shadow_stack_frame_info &frame, print_what print_what)
+{
+ if (print_options.print_frame_info != print_frame_info_auto)
+ {
+ /* Use the specific frame information desired by the user. */
+ print_what
+ = *print_frame_info_to_print_what (print_options.print_frame_info);
+ }
+
+ /* In contrast to find_frame_sal which is used for the ordinary backtrace
+ command, we always want to print the line that is actually referred
+ to by the address in the linetable. That's why we always pass 0 here
+ as second argument. */
+ symtab_and_line sal = find_pc_line (frame.value, 0);
+
+ if (should_print_location (print_what) || sal.symtab == nullptr)
+ {
+ gdb::unique_xmalloc_ptr<char> funname
+ = find_pc_funname (frame.value);
+
+ { /* Extra scope to print frame tuple. */
+ ui_out_emit_tuple tuple_emitter (uiout, "shadow-stack-frame");
+
+ annotate_shadowstack_frame_begin (frame.level, gdbarch,
+ frame.value);
+
+ uiout->text ("#");
+ uiout->field_fmt_signed (2, ui_left, "level", frame.level);
+
+ annotate_shadowstack_frame_address ();
+
+ /* 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 (gdbarch);
+
+ uiout->field_string
+ ("addr", hex_string_custom (frame.value, element_size * 2),
+ address_style.style ());
+
+ annotate_shadowstack_frame_address_end ();
+
+ uiout->text (" in ");
+ print_funname (uiout, funname, true);
+
+ if (print_what != SHORT_LOCATION && sal.symtab != nullptr)
+ print_filename (uiout, sal, true);
+
+ 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, true);
+ }
+ } /* Extra scope to print frame tuple. */
+
+ uiout->text ("\n");
+ }
+
+ if (print_what == SRC_LINE || print_what == SRC_AND_LOC)
+ {
+ int 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
+ 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, gdbarch, frame.value, sal, print_address,
+ mid_statement, "");
+ }
+
+ annotate_shadowstack_frame_end ();
+ 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
+ (gdbarch *gdbarch, const shadow_stack_print_options &print_options,
+ const shadow_stack_frame_info &frame, print_what print_what)
+{
+ do_with_buffered_output
+ (do_print_shadow_stack_frame_info, current_uiout, gdbarch,
+ print_options, frame, print_what);
+}
+
+
+/* Extract a char array which can be used for printing a reasonable
+ error message for REASON. Note that in case REASON has the value
+ NO_ERROR the returned array is empty. */
+
+static const char *
+ssp_unwind_stop_reason_to_err_string (ssp_unwind_stop_reason reason)
+{
+ switch (reason)
+ {
+ case ssp_unwind_stop_reason::no_error:
+ return _("");
+ case ssp_unwind_stop_reason::memory_read_error:
+ return _("shadow stack memory read failure");
+ }
+
+ gdb_assert_not_reached ("invalid reason");
+}
+
+
+/* Read the memory at shadow stack pointer SSP and assign it to
+ RETURN_VALUE. In case we cannot read the memory, set REASON to
+ ssp_unwind_stop_reason::memory_read_error and return false. */
+
+static bool
+read_shadow_stack_memory (gdbarch *gdbarch, CORE_ADDR ssp,
+ CORE_ADDR *return_value,
+ ssp_unwind_stop_reason *reason)
+{
+ /* 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);
+ if (!safe_read_memory_unsigned_integer (ssp, element_size, byte_order,
+ return_value))
+ {
+ *reason = ssp_unwind_stop_reason::memory_read_error;
+ return false;
+ }
+
+ return true;
+}
+
+/* If possible, return a shadow stack frame info which is COUNT elements
+ above the bottom of the shadow stack. FRAME should point to the top
+ 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
+ (gdbarch *gdbarch, const std::pair<CORE_ADDR, CORE_ADDR> range,
+ const ULONGEST count, shadow_stack_frame_info &frame)
+{
+ /* Compute the number of bytes on the shadow stack, starting at
+ FRAME->SSP, which depends on the direction the shadow stack
+ grows. */
+ const int element_size
+ = gdbarch_shadow_stack_element_size_aligned (gdbarch);
+ const unsigned long shadow_stack_bytes
+ = (gdbarch_stack_grows_down (gdbarch))
+ ? range.second - frame.ssp : frame.ssp - range.first + element_size;
+
+ gdb_assert ((shadow_stack_bytes % element_size) == 0);
+ const unsigned long shadow_stack_size
+ = shadow_stack_bytes / element_size;
+ const long level = shadow_stack_size - count;
+
+ /* COUNT exceeds the number of elements on the shadow stack. Return the
+ starting shadow stack frame info FRAME. */
+ if (level <= 0)
+ return std::optional<shadow_stack_frame_info> (frame);
+
+ CORE_ADDR new_ssp = update_shadow_stack_pointer
+ (gdbarch, frame.ssp, count, ssp_update_direction::bottom);
+
+ if (gdbarch_stack_grows_down (gdbarch))
+ gdb_assert (new_ssp < range.second);
+ else
+ gdb_assert (new_ssp >= range.first);
+
+ CORE_ADDR new_value;
+ if (!read_shadow_stack_memory (gdbarch, new_ssp, &new_value,
+ &frame.unwind_stop_reason))
+ return {};
+
+ return std::optional<shadow_stack_frame_info>
+ ({new_ssp, new_value, (ULONGEST) level,
+ ssp_unwind_stop_reason::no_error});
+}
+
+std::optional<shadow_stack_frame_info>
+shadow_stack_frame_info::unwind_prev_shadow_stack_frame_info
+ (gdbarch *gdbarch, std::pair<CORE_ADDR, CORE_ADDR> range)
+{
+ /* 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 (gdbarch, this->ssp, 1,
+ ssp_update_direction::bottom);
+
+ if (gdbarch_stack_grows_down (gdbarch))
+ {
+ /* The shadow stack grows downwards. */
+ if (new_ssp >= range.second)
+ {
+ /* We reached the bottom of the shadow stack. */
+ return {};
+ }
+ /* We updated new_ssp towards the bottom 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 bottom of the shadow stack. */
+ return {};
+ }
+ /* We updated new_ssp towards the bottom of the shadow stack before,
+ and new_ssp must be pointing to shadow stack memory. */
+ gdb_assert (new_ssp <= range.second);
+ }
+
+ CORE_ADDR new_value;
+ if (!read_shadow_stack_memory (gdbarch, new_ssp, &new_value,
+ &this->unwind_stop_reason))
+ return {};
+
+ return std::optional<shadow_stack_frame_info>
+ ({new_ssp, new_value, this->level + 1,
+ ssp_unwind_stop_reason::no_error});
+}
+
+/* Print all elements on the shadow stack or just the innermost COUNT_EXP
+ frames. */
+
+static void
+backtrace_shadow_command (const shadow_stack_print_options &print_options,
+ 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))
+ 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 target."));
+
+ /* 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. */
+ std::pair<CORE_ADDR, CORE_ADDR> range;
+ if (!gdbarch_address_in_shadow_stack_memory_range (gdbarch, *start_ssp,
+ &range))
+ {
+ /* If the current shadow stack pointer does not point to shadow
+ stack memory, the shadow stack is empty. */
+ 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;
+ CORE_ADDR new_value;
+ if (read_shadow_stack_memory (gdbarch, *start_ssp, &new_value, &reason))
+ current = {*start_ssp, new_value, 0,
+ ssp_unwind_stop_reason::no_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
+ (gdbarch, range, std::abs (count), *current);
+
+ if (!trailing.has_value ())
+ reason = current->unwind_stop_reason;
+ }
+ }
+
+ if (!trailing.has_value ())
+ {
+ if (reason > ssp_unwind_stop_reason::no_error)
+ error (_("Cannot print shadow stack backtrace: %s.\n"),
+ ssp_unwind_stop_reason_to_err_string (reason));
+ else
+ gdb_assert_not_reached ("invalid reason");
+ }
+
+ current = trailing;
+ while (current.has_value () && count--)
+ {
+ QUIT;
+
+ print_shadow_stack_frame_info (gdbarch, print_options, *current,
+ LOCATION);
+
+ trailing = current;
+ current = current->unwind_prev_shadow_stack_frame_info (gdbarch,
+ range);
+ }
+
+ /* If we've stopped before the end, mention that. */
+ if (current && from_tty)
+ gdb_printf (_("(More shadow stack frames follow...)\n"));
+
+ /* 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.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 (gdbarch, trailing->ssp),
+ ssp_unwind_stop_reason_to_err_string
+ (trailing->unwind_stop_reason));
+}
+
+void
+backtrace_shadow_command (const char *arg, int from_tty)
+{
+ shadow_stack_print_options print_options;
+ print_options.print_frame_info
+ = user_frame_print_options.print_frame_info;
+
+ auto grp = make_backtrace_shadow_options_def_group (&print_options);
+ gdb::option::process_options
+ (&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp);
+
+ if (arg != nullptr && *arg == '\0')
+ arg = nullptr;
+
+ backtrace_shadow_command (print_options, arg, from_tty);
+}
+
+std::array<gdb::option::option_def_group, 1>
+make_backtrace_shadow_options_def_group
+ (shadow_stack_print_options *print_options)
+{
+ return {{
+ { {shadowstack_print_option_defs}, print_options }
+ }};
+}
diff --git a/gdb/shadow-stack.h b/gdb/shadow-stack.h
index 5c3ba80974e..2d33efafd22 100644
--- a/gdb/shadow-stack.h
+++ b/gdb/shadow-stack.h
@@ -36,4 +36,23 @@ void shadow_stack_push (gdbarch *gdbarch, regcache *regcache,
value * dwarf2_prev_ssp (const frame_info_ptr &this_frame,
void **this_cache, int regnum);
+/* Data for the printing of shadow stack frame information, exposed as
+ command options. */
+
+struct shadow_stack_print_options
+{
+ const char *print_frame_info = print_frame_info_auto;
+};
+
+/* Implementation of "backtrace shadow" comand. */
+
+void backtrace_shadow_command (const char *arg, int from_tty);
+
+/* Create an option_def_group array grouping all the "backtrace shadow"
+ options, with SSP_OPTS as contexts. */
+
+std::array<gdb::option::option_def_group, 1>
+ make_backtrace_shadow_options_def_group
+ (shadow_stack_print_options *print_options);
+
#endif /* GDB_SHADOW_STACK_H */
diff --git a/gdb/stack.c b/gdb/stack.c
index 40756f74a00..30a7d8621be 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -50,6 +50,7 @@
#include "linespec.h"
#include "cli/cli-utils.h"
#include "objfiles.h"
+#include "shadow-stack.h"
#include "symfile.h"
#include "extension.h"
@@ -86,7 +87,7 @@ const char print_frame_info_source_and_location[] = "source-and-location";
const char print_frame_info_location_and_address[] = "location-and-address";
const char print_frame_info_short_location[] = "short-location";
-static const char *const print_frame_info_choices[] =
+const char *const print_frame_info_choices[] =
{
print_frame_info_auto,
print_frame_info_source_line,
@@ -962,11 +963,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++)
@@ -1016,7 +1015,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
@@ -1025,14 +1024,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)
@@ -1298,7 +1292,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;
@@ -1364,31 +1358,28 @@ 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
-print_lib (ui_out *uiout, const char *lib)
+void
+print_lib (ui_out *uiout, const char *lib, bool shadowstack_frame)
{
- annotate_frame_where ();
+ annotate_frame_where (shadowstack_frame);
uiout->wrap_hint (2);
uiout->text (" from ");
uiout->field_string ("from", lib, file_name_style.style ());
}
-/* Print the filenname of SAL to UIOUT for the printing of frame
- information. */
-
-static void
-print_filename (ui_out *uiout, symtab_and_line sal)
+void
+print_filename (ui_out *uiout, symtab_and_line sal, bool shadowstack_frame)
{
- annotate_frame_source_begin ();
+ annotate_frame_source_begin (shadowstack_frame);
const char *filename_display;
filename_display = symtab_to_filename_for_display (sal.symtab);
uiout->wrap_hint (3);
uiout->text (" at ");
- annotate_frame_source_file ();
+ annotate_frame_source_file (shadowstack_frame);
+
uiout->field_string ("file", filename_display, file_name_style.style ());
if (uiout->is_mi_like_p ())
@@ -1397,21 +1388,21 @@ print_filename (ui_out *uiout, symtab_and_line sal)
uiout->field_string ("fullname", fullname);
}
- annotate_frame_source_file_end ();
+ annotate_frame_source_file_end (shadowstack_frame);
uiout->text (":");
- annotate_frame_source_line ();
+ annotate_frame_source_line (shadowstack_frame);
uiout->field_signed ("line", sal.line, line_number_style.style ());
- annotate_frame_source_end ();
+ annotate_frame_source_end (shadowstack_frame);
}
-/* 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)
+ gdb::unique_xmalloc_ptr<char> const &funname,
+ bool shadowstack_frame)
{
- annotate_frame_function_name ();
+ annotate_frame_function_name (shadowstack_frame);
string_file stb;
gdb_puts (funname ? funname.get () : "??", &stb);
uiout->field_stream ("func", stb, function_name_style.style ());
@@ -2223,6 +2214,23 @@ backtrace_command (const char *arg, int from_tty)
backtrace_command_1 (fp_opts, bt_cmd_opts, arg, from_tty);
}
+/* Completer for the "backtrace shadow" sub-command. */
+
+static void
+backtrace_shadow_command_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char */*word*/)
+{
+ const auto group
+ = make_backtrace_shadow_options_def_group (nullptr);
+ if (gdb::option::complete_options
+ (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
+ return;
+
+ const char *word = advance_to_expression_complete_word_point (tracker, text);
+ expression_completer (ignore, tracker, text, word);
+}
+
/* Completer for the "backtrace" command. */
static void
@@ -3314,6 +3322,9 @@ static struct cmd_list_element *select_frame_cmd_list = NULL;
/* Commands with a prefix of `info frame'. */
static struct cmd_list_element *info_frame_cmd_list = NULL;
+/* Commands with a prefix of `backtrace' of `bt'. */
+static cmd_list_element *bt_list = nullptr;
+
INIT_GDB_FILE (stack)
{
struct cmd_list_element *cmd;
@@ -3503,10 +3514,31 @@ With a negative COUNT, print outermost -COUNT frames."),
backtrace_opts);
cmd_list_element *backtrace_cmd
- = add_com ("backtrace", class_stack, backtrace_command,
- backtrace_help.c_str ());
+ = add_prefix_cmd ("backtrace", class_stack, backtrace_command,
+ backtrace_help.c_str (), &bt_list, 1, &cmdlist);
set_cmd_completer_handle_brkchars (backtrace_cmd, backtrace_command_completer);
+ const auto backtrace_shadow_opts
+ = make_backtrace_shadow_options_def_group (nullptr);
+
+ static std::string backtrace_shadow_help
+ = gdb::option::build_help (_("\
+Print backtrace of all shadow stack frames, or innermost COUNT frames.\n\
+Usage: backtrace shadow [OPTION]... [COUNT | -COUNT]\n\
+\n\
+Options:\n\
+%OPTIONS%\n\
+\n\
+With a negative COUNT, print outermost -COUNT elements."),
+ backtrace_shadow_opts);
+
+ cmd_list_element *bt_shadow_cmd = add_cmd ("shadow", class_stack,
+ backtrace_shadow_command,
+ backtrace_shadow_help.c_str (),
+ &bt_list);
+ set_cmd_completer_handle_brkchars
+ (bt_shadow_cmd, backtrace_shadow_command_completer);
+
add_com_alias ("bt", backtrace_cmd, class_stack, 0);
add_com_alias ("where", backtrace_cmd, class_stack, 0);
diff --git a/gdb/stack.h b/gdb/stack.h
index 6b2efb8fe60..966cf5caed0 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -84,4 +84,59 @@ void frame_apply_all_cmd_completer (struct cmd_list_element *ignore,
completion_tracker &tracker,
const char *text, const char */*word*/);
+/* Print the filenname of SAL to UIOUT for the printing of frame
+ information. In case SHADOWSTACK_FRAME is true, we annotate for a
+ shadow stack frame. If it is false (default), we annotate for a
+ normal frame. */
+
+void print_filename (ui_out *uiout, symtab_and_line sal,
+ bool shadowstack_frame = false);
+
+/* Print the library LIB to UIOUT for the printing of frame
+ information. In case SHADOWSTACK_FRAME is true, we annotate for a
+ shadow stack frame. If it is false (default), we annotate for a
+ normal frame. */
+
+void print_lib (ui_out *uiout, const char *lib,
+ bool shadowstack_frame = false);
+
+/* If available, print FUNNAME to UIOUT for the printing of frame
+ information. In case SHADOWSTACK_FRAME is true, we annotate for a
+ shadow stack frame. If it is false (default), we annotate for a
+ normal frame. */
+
+void print_funname (ui_out *uiout,
+ gdb::unique_xmalloc_ptr<char> const &funname,
+ bool shadowstack_frame = false);
+
+/* 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);
+
+/* The possible choices of "set print frame-info". */
+extern const char *const print_frame_info_choices[7];
+
#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 0ae172d7c41..4563e49d9e4 100644
--- a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
+++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
@@ -109,6 +109,94 @@ 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 frame0 "#0\[ \t\]*$sspval_call2 in call1$fill"
+ set frame1 "#1\[ \t\]*$sspval_call1 in main$fill"
+ set frame2 "#2\[ \t\]*$sspval_main$fill"
+
+ # We can 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" \
+ [multi_line \
+ "$frame0" \
+ "$frame1" \
+ "$frame2" \
+ ".*" ] \
+ "test shadow stack backtrace until the bottom of the stack."
+
+ gdb_test "bt shadow 2" \
+ [multi_line \
+ "$frame0" \
+ "$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 \
+ "$frame0" \
+ "$frame1" ] \
+ "test shadow stack backtrace with limit"
+ gdb_test_no_output "set backtrace limit unlimited" "restore backtrace limit default"
+
+ # Test annotations for bt shadow.
+ save_vars { ::gdb_prompt } {
+
+ # Just like for other gdb tests testing annotations, note:
+ # When this prompt is in use the gdb_test procedure cannot be used
+ # because it assumes that the last char of the gdb_prompt is a
+ # white space. This is not true with this annotated prompt.
+ # One alternative is to use gdb_test_multiple.
+ set ::gdb_prompt "\r\n\032\032pre-prompt\r\n$::gdb_prompt \r\n\032\032prompt\r\n"
+
+ gdb_test_multiple "set annotate 2" "set annotate 2" {
+ -re "set annotate 2\r\n$gdb_prompt$" {
+ pass "annotation set at level 2"
+ }
+ -re ".*$gdb_prompt$" {
+ fail "annotation set at level 2"
+ }
+ timeout {
+ fail "annotation set at level 2 (timeout)"
+ }
+ }
+
+ set inner_frame_re [multi_line \
+ "shadow-stack-frame-address" \
+ "$hex\r\n\032\032shadow-stack-frame-address-end" \
+ "\ in \r\n\032\032shadow-stack-frame-function-name" \
+ "$fill\r\n\032\032shadow-stack-frame-source-begin" \
+ " at \r\n\032\032shadow-stack-frame-source-file\r\n$fill" \
+ "\032\032shadow-stack-frame-source-file-end" \
+ ":\r\n\032\032shadow-stack-frame-source-line\r\n$decimal" \
+ "\032\032shadow-stack-frame-source-end\r\n\r\n\r\n"]
+
+ set re [multi_line \
+ "shadow-stack-frame-begin 0 $hex" \
+ "#0 \r\n\032\032$inner_frame_re\032\032shadow-stack-frame-end" \
+ "\r\n\032\032shadow-stack-frame-begin 1 $hex" \
+ "#1 \r\n\032\032$inner_frame_re\032\032shadow-stack-frame-end" \
+ "\\(More shadow stack frames follow...\\)" \
+ "$gdb_prompt$" ]
+
+ gdb_test_multiple "backtrace shadow 2" "backtrace shadow with annotations" {
+ -re $re {
+ pass $gdb_test_name
+ }
+ }
+ }
+ gdb_test "set annotate 0" ".*post-prompt.*" "set annotate 0"
+
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/help.exp b/gdb/testsuite/gdb.base/help.exp
index ec41382a9af..8fbc6005232 100644
--- a/gdb/testsuite/gdb.base/help.exp
+++ b/gdb/testsuite/gdb.base/help.exp
@@ -168,7 +168,11 @@ gdb_test "apropos An alias of command backtrace with arg 10" \
"mybt10 -- An alias of command backtrace with arg 10\." \
"apropos after documenting aliases showing mybt10 doc"
-gdb_test "help mybt" " alias mybt = backtrace \[\r\n\]+An alias of command backtrace without any args\." \
+set bt_alias_doc "alias mybt = backtrace \[\r\n\]+An alias of command backtrace without any args\."
+set bt_shadow_doc "\[\r\n\]+List of \"backtrace\" subcommands:\[\r\n\]+backtrace shadow -- Print backtrace of all shadow stack frames, or innermost COUNT frames."
+set bt_shadow_help_doc "\[\r\n\]+Type \"help backtrace\" followed by subcommand name for full documentation."
+
+gdb_test "help mybt" "$bt_alias_doc$bt_shadow_doc$bt_shadow_help_doc.*" \
"help mybt after documenting aliases showing mybt doc"
# Check pre-defined aliases cannot be documented.
diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp
index 8ac9f4ee755..350bf6205b8 100644
--- a/gdb/testsuite/gdb.base/options.exp
+++ b/gdb/testsuite/gdb.base/options.exp
@@ -288,11 +288,16 @@ proc_with_prefix test-backtrace {} {
clean_restart
test_gdb_complete_unique "backtrace" "backtrace"
- test_gdb_complete_none "backtrace "
+ test_gdb_complete_unique "backtrace shadow" "backtrace shadow"
+ test_gdb_complete_none "backtrace shadow "
gdb_test "backtrace -" "Ambiguous option at: -"
gdb_test "backtrace --" "No stack\\."
gdb_test "backtrace -- -" "No stack\\."
+ gdb_test "backtrace shadow -" \
+ "Requires an argument. Valid arguments are auto, source-line.*\\."
+ gdb_test "backtrace shadow --" "No shadow stack\\."
+ gdb_test "backtrace shadow -- -" "No shadow stack\\."
test_gdb_complete_multiple "backtrace " "-" "" {
"-entry-values"
--
2.34.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
next prev parent reply other threads:[~2025-09-23 11:24 UTC|newest]
Thread overview: 67+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-23 11:18 [PATCH 0/9] Add new command " Christina Schimpe
2025-09-23 11:18 ` [PATCH 1/9] gdb: Generalize handling of the shadow stack pointer Christina Schimpe
2025-10-31 1:31 ` Thiago Jung Bauermann
2025-11-17 11:18 ` Schimpe, Christina
2025-11-26 4:19 ` Thiago Jung Bauermann
2025-12-30 10:39 ` Schimpe, Christina
2025-09-23 11:18 ` [PATCH 2/9] gdb: Refactor 'stack.c:print_frame' Christina Schimpe
2025-10-03 20:05 ` Tom Tromey
2025-09-23 11:18 ` [PATCH 3/9] gdb: Introduce 'stack.c:print_pc' function without frame argument Christina Schimpe
2025-10-03 19:56 ` Tom Tromey
2025-09-23 11:18 ` [PATCH 4/9] gdb: Refactor 'find_symbol_funname' and 'info_frame_command_core' in stack.c Christina Schimpe
2025-10-03 19:55 ` Tom Tromey
2025-09-23 11:18 ` [PATCH 5/9] gdb: Refactor 'stack.c:print_frame_info' Christina Schimpe
2025-10-03 20:03 ` Tom Tromey
2025-09-23 11:18 ` Christina Schimpe [this message]
2025-09-23 11:47 ` [PATCH 6/9] gdb: Implement 'bt shadow' to print the shadow stack backtrace Eli Zaretskii
2025-09-25 11:06 ` Schimpe, Christina
2025-09-25 13:19 ` Eli Zaretskii
2025-09-25 14:58 ` Simon Marchi
2025-09-26 7:45 ` Schimpe, Christina
2025-10-29 15:05 ` Schimpe, Christina
2025-10-29 15:28 ` Guinevere Larsen
2025-11-03 19:47 ` Schimpe, Christina
2025-11-04 11:53 ` Guinevere Larsen
2025-11-05 16:33 ` Schimpe, Christina
2025-10-13 1:17 ` Thiago Jung Bauermann
2025-10-13 7:19 ` Schimpe, Christina
2025-10-31 4:39 ` Thiago Jung Bauermann
2025-11-06 14:23 ` Schimpe, Christina
2025-10-03 20:15 ` Tom Tromey
2025-10-12 19:45 ` Schimpe, Christina
2026-02-19 17:24 ` Tom Tromey
2026-03-02 12:24 ` Schimpe, Christina
2025-10-31 4:02 ` Thiago Jung Bauermann
2025-11-17 20:14 ` Schimpe, Christina
2025-11-26 4:07 ` Thiago Jung Bauermann
2025-11-26 16:29 ` Thiago Jung Bauermann
2026-01-22 17:04 ` Schimpe, Christina
2026-03-06 2:35 ` Thiago Jung Bauermann
2026-01-15 14:05 ` Schimpe, Christina
2025-09-23 11:18 ` [PATCH 7/9] gdb: Provide gdbarch hook to distinguish shadow stack backtrace elements Christina Schimpe
2025-09-23 11:49 ` Eli Zaretskii
2025-09-25 11:10 ` Schimpe, Christina
2025-11-02 21:20 ` Thiago Jung Bauermann
2025-11-12 17:28 ` Schimpe, Christina
2025-11-16 18:39 ` Thiago Jung Bauermann
2025-11-17 11:51 ` Schimpe, Christina
2025-09-23 11:18 ` [PATCH 8/9] gdb: Implement the hook 'is_no_return_shadow_stack_address' for amd64 linux Christina Schimpe
2025-11-26 4:22 ` Thiago Jung Bauermann
2025-09-23 11:18 ` [PATCH 9/9] gdb, mi: Add -shadow-stack-list-frames command Christina Schimpe
2025-09-23 11:53 ` Eli Zaretskii
2025-09-25 11:32 ` Schimpe, Christina
2025-10-03 20:17 ` Tom Tromey
2025-10-12 19:54 ` Schimpe, Christina
2025-10-13 0:06 ` Thiago Jung Bauermann
2025-11-26 4:26 ` Thiago Jung Bauermann
2026-01-22 17:01 ` Schimpe, Christina
2026-03-06 2:44 ` Thiago Jung Bauermann
2025-09-25 11:46 ` [PATCH 0/9] Add new command to print the shadow stack backtrace Schimpe, Christina
2025-10-08 1:46 ` Thiago Jung Bauermann
2025-10-13 1:18 ` Thiago Jung Bauermann
2025-10-13 6:34 ` Schimpe, Christina
2025-10-29 14:52 ` Schimpe, Christina
2025-10-31 0:47 ` Thiago Jung Bauermann
2025-12-30 10:16 ` Schimpe, Christina
2026-03-06 2:30 ` Thiago Jung Bauermann
2026-03-12 9:53 ` Schimpe, Christina
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250923111842.4091694-7-christina.schimpe@intel.com \
--to=christina.schimpe@intel.com \
--cc=gdb-patches@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox