@@ -57,6 +57,9 @@ info locals
*** Changes in GDB 18
+* 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.
@@ -8788,6 +8788,29 @@ 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
+(gdb) bt -shadow
+#0 0x000055555555514a in call1 at amd64-shadow-stack.c:27
+#1 0x000055555555515f in main at amd64-shadow-stack.c:38
+@end group
+@end smallexample
+
@end table
The optional @var{qualifier} is maintained for backward compatibility.
@@ -27263,6 +27286,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
@@ -27351,6 +27375,7 @@ registers
@end itemize
+@anchor{CET}
@subsubsection Intel Control-Flow Enforcement Technology.
@cindex Intel Control-Flow Enforcement Technology.
@@ -1751,6 +1751,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
@@ -32,6 +32,7 @@
#include "registry.h"
#include "solib.h"
#include "find-memory-region.h"
+#include "shadow-stack.h"
struct floatformat;
struct ui_file;
@@ -2782,6 +2782,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
@@ -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,472 @@ 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 (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 {};
+
+ /* 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);
+ 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_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 (!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));
+}
@@ -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,61 @@ 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,
+};
+
+/* 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;
+
+ /* 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 */
@@ -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"
@@ -192,6 +193,7 @@ struct backtrace_cmd_options
bool full = false;
bool no_filters = false;
bool hide = false;
+ bool shadow = false;
};
using bt_flag_option_def
@@ -215,6 +217,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'."),
+ },
};
/* Option for printing shadowed variables. */
@@ -945,11 +955,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++)
@@ -999,7 +1007,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
@@ -1008,14 +1016,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)
@@ -1280,7 +1283,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;
@@ -1346,10 +1349,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 ();
@@ -1358,10 +1360,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 ();
@@ -1386,10 +1385,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)
{
@@ -2201,7 +2199,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. */
@@ -99,4 +99,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 */
@@ -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" \
@@ -287,7 +287,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\\."
@@ -303,6 +302,7 @@ proc_with_prefix test-backtrace {} {
"-past-entry"
"-past-main"
"-raw-frame-arguments"
+ "-shadow"
}
# Test that we complete the qualifiers, if there's any.