From: Christina Schimpe <christina.schimpe@intel.com>
To: gdb-patches@sourceware.org
Subject: [PATCH 7/9] gdb: Provide gdbarch hook to distinguish shadow stack backtrace elements.
Date: Tue, 23 Sep 2025 11:18:40 +0000 [thread overview]
Message-ID: <20250923111842.4091694-8-christina.schimpe@intel.com> (raw)
In-Reply-To: <20250923111842.4091694-1-christina.schimpe@intel.com>
On x86 with CET there can be elements on the shadow stack which are not
return addresses. In this case, we just want to print the element itself
in the shadow stack backtrace, but no further information.
Provide a gdbarch hook to distinguish between return and non-return
addresses and use it to print the shadow stack backtrace as described
above.
---
gdb/doc/gdb.texinfo | 19 ++++++++++++
gdb/gdbarch-gen.c | 32 ++++++++++++++++++++
gdb/gdbarch-gen.h | 15 +++++++++
gdb/gdbarch.h | 1 +
gdb/gdbarch_components.py | 17 +++++++++++
gdb/shadow-stack.c | 64 +++++++++++++++++----------------------
gdb/shadow-stack.h | 37 ++++++++++++++++++++++
7 files changed, 148 insertions(+), 37 deletions(-)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index ebda4546b58..a0fde385a8e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -8887,6 +8887,25 @@ This is how a shadow stack backtrace looks like on amd64:
@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. For such shadow stack elements, the
+shadow stack frame just contains the level and the address on the shadow
+stack, as shown in the following example by frame 1:
+
+@smallexample
+@group
+(gdb) bt shadow 4
+#0 0x00007ffff7c54d90 in __restore_rt from /lib64/libc.so.6
+#1 0x80007ffff79fffd8
+#2 0x00007ffff7c54ce6 in __GI_raise at ../sysdeps/posix/raise.c:27
+#3 0x000000000040115d in main at /tmp/amd64-shadow-stack-signal.c:32
+(More shadow stack frames follow...)
+@end group
+@end smallexample
+
+
@node Selection
@section Selecting a Frame
diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c
index 640576f4b85..21def8fd3e7 100644
--- a/gdb/gdbarch-gen.c
+++ b/gdb/gdbarch-gen.c
@@ -266,6 +266,7 @@ struct gdbarch
gdbarch_top_addr_empty_shadow_stack_ftype *top_addr_empty_shadow_stack = nullptr;
gdbarch_address_in_shadow_stack_memory_range_ftype *address_in_shadow_stack_memory_range = 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
@@ -543,6 +544,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
/* Skip verify of top_addr_empty_shadow_stack, has predicate. */
/* Skip verify of address_in_shadow_stack_memory_range, 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 ());
@@ -1432,6 +1434,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 != NULL)
gdbarch->dump_tdep (gdbarch, file);
}
@@ -5659,3 +5667,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 != NULL);
+ return gdbarch->is_no_return_shadow_stack_address != NULL;
+}
+
+bool
+gdbarch_is_no_return_shadow_stack_address (struct gdbarch *gdbarch, const shadow_stack_frame_info &frame)
+{
+ gdb_assert (gdbarch != NULL);
+ gdb_assert (gdbarch->is_no_return_shadow_stack_address != NULL);
+ 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, frame);
+}
+
+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 34a32c346e8..0b92a448c78 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -1856,3 +1856,18 @@ extern void set_gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gd
extern int gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch);
extern 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 'backtrace shadow' and the
+ shadow stack can contain elements which are not return addresses, this
+ function has to be provided.
+
+ Return true, if FRAME does not contain a return address in FRAME->VALUE
+ but another valid value for the architecture's shadow stack. */
+
+extern bool gdbarch_is_no_return_shadow_stack_address_p (struct gdbarch *gdbarch);
+
+typedef bool (gdbarch_is_no_return_shadow_stack_address_ftype) (struct gdbarch *gdbarch, const shadow_stack_frame_info &frame);
+extern bool gdbarch_is_no_return_shadow_stack_address (struct gdbarch *gdbarch, const shadow_stack_frame_info &frame);
+extern 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 20497612811..57b8f021e93 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -31,6 +31,7 @@
#include "gdbsupport/gdb-checked-static-cast.h"
#include "registry.h"
#include "solib.h"
+#include "shadow-stack.h"
struct floatformat;
struct ui_file;
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 5223553941f..d925f5a7e87 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -2938,3 +2938,20 @@ this value.
predefault="8",
invalid=False,
)
+
+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 'backtrace shadow' and the
+shadow stack can contain elements which are not return addresses, this
+function has to be provided.
+
+Return true, if FRAME does not contain a return address in FRAME->VALUE
+but another valid value for the architecture's shadow stack.
+""",
+ type="bool",
+ name="is_no_return_shadow_stack_address",
+ params=[("const shadow_stack_frame_info &", "frame")],
+ predicate=True,
+)
diff --git a/gdb/shadow-stack.c b/gdb/shadow-stack.c
index 759ef6989ea..42032f51781 100644
--- a/gdb/shadow-stack.c
+++ b/gdb/shadow-stack.c
@@ -222,43 +222,6 @@ pc_in_middle_of_statement (CORE_ADDR pc, symtab_and_line sal)
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)
{
@@ -285,6 +248,33 @@ do_print_shadow_stack_frame_info
const shadow_stack_print_options &print_options,
const shadow_stack_frame_info &frame, print_what print_what)
{
+ if (gdbarch_is_no_return_shadow_stack_address_p (gdbarch)
+ && gdbarch_is_no_return_shadow_stack_address (gdbarch, frame))
+ {
+ /* It is possible, for the x86 architecture for instance, that an
+ element on the shadow stack is not a return address. We still
+ want to print the address in that case but no further
+ information. */
+ ui_out_emit_tuple tuple_emitter (uiout, "shadow-stack-frame");
+ uiout->text ("#");
+ uiout->field_fmt_signed (2, ui_left, "level", frame.level);
+
+ /* 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 ());
+ uiout->text ("\n");
+ gdb_flush (gdb_stdout);
+ return;
+ }
+
if (print_options.print_frame_info != print_frame_info_auto)
{
/* Use the specific frame information desired by the user. */
diff --git a/gdb/shadow-stack.h b/gdb/shadow-stack.h
index 2d33efafd22..f801d077820 100644
--- a/gdb/shadow-stack.h
+++ b/gdb/shadow-stack.h
@@ -55,4 +55,41 @@ std::array<gdb::option::option_def_group, 1>
make_backtrace_shadow_options_def_group
(shadow_stack_print_options *print_options);
+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;
+};
+
#endif /* GDB_SHADOW_STACK_H */
--
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 to print the shadow stack backtrace 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 ` [PATCH 6/9] gdb: Implement 'bt shadow' to print the shadow stack backtrace Christina Schimpe
2025-09-23 11:47 ` 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 ` Christina Schimpe [this message]
2025-09-23 11:49 ` [PATCH 7/9] gdb: Provide gdbarch hook to distinguish shadow stack backtrace elements 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-8-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