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
next prev parent 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