RE: [PATCH v2 6/9] gdb: Add command option 'bt -shadow' to print the shadow stack backtrace.

Schimpe, Christina
2026-04-10T12:13:12Z

Hi Thiago,

> -----Original Message-----
> From: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Sent: Freitag, 10. April 2026 08:21
> To: Schimpe, Christina <christina.schimpe@intel.com>
> Cc: gdb-patches@sourceware.org
> Subject: Re: [PATCH v2 6/9] gdb: Add command option 'bt -shadow' to print
> the shadow stack backtrace.
>
> Hello Christina,
>
> "Schimpe, Christina" <christina.schimpe@intel.com> writes:
>
> > Thanks a lot for your feedback.
>
> You're welcome! Thank you for moving this forward. Sorry for the big delays
> on my side. More things going on at once than I'd like.
>
> > For the hook gdbarch_get_shadow_stack_size I still need your GCS
> > implementation. 😊
> > I suggest a separate patch for this with you as the only author and
> > I'll include this in my v3 then, too. Does that make sense?
>
> I think you also need aarch64_linux_is_no_return_shadow_stack_address,
> right? I'm including both in a a patch at the end of this email.
>
> There are FIXMEs in them to remind myself to do something better than
> assert if ssp is an invalid address. Perhaps throw an error as you mention
> below.
>
> Also, feel free to not provide the aarch64 versions of the hooks in this patch
> and the following one. I was planning to send them after your series goes in.
>
> The reason I wanted an aarch64 hook in the first patch was so that existing
> GCS functionality doesn't regress, but the hooks in the other patches are for
> new functionality so it's fine to have only the amd64 implementation.

Yes, of course. I only realized this now... It's probably a good idea to simply create
a new aarch64 specific series once the one for amd64 is in.
But still, it's interesting to see how you implement that for GCS, so thanks for sharing.

I'll not include the patches in my v3 then, expect for the first one. Even though the GCS
feature should not break without it, I think it's good to have it all together in this case.

> >> > + const unsigned long shadow_stack_bytes = range.second - *ssp;
> >> > +
> >> > + gdb_assert ((shadow_stack_bytes % 8) == 0);
> >>
> >> I don't think this should be an assert. If it fails, it triggers an
> >> internal error in GDB. In this case it could indeed mean an internal
> >> error (GDB somehow got the SSP or range wrong), but it could also be
> >> (and probably more likely) an inconsistent state of the inferior.
> >> This can happen in a program being debugged so GDB should be able to
> >> handle it gracefully, and if possible provide useful information to the user.
> >
> > I agree. This is rather something that is outside GDB's control.
> >
> > From the documentation for internal errors:
> > "Internal errors indicate programming errors such as assertion failures, as
> opposed to
> > more general errors beyond the application's control. "
> >
> > So based on that I rather would choose a normal error, not an internal error.
> > What do you think ?
>
> The effect of the error being thrown would be just that the "bt -shadow"
> command is interrupted, right? If so, I think it's a good idea.

Yes, at the moment we call this function only in
get_trailing_outermost_shadow_stack_frame_info (before we printed any frame).

I'd suggest something like: "Invalid shadow stack state." This should be generic enough
if this hook is ever called somewhere else.

Christina

> >> > + return shadow_stack_bytes / 8;
> >> > +}
>
> --
> Thiago
>
> From 89f07938071f78c479ac045296afd562ab9ef93a Mon Sep 17 00:00:00
> 2001
> From: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Date: Tue, 10 Mar 2026 21:44:35 -0300
> Subject: [PATCH] WIP GDB: aarch64-linux: Add gdbarch hooks for printing
> shadow stack
>
> Enables bt -shadow.
>
> There are still some FIXMEs to address.
> ---
> gdb/aarch64-linux-tdep.c | 38
> ++++++++++++++++++++++++++++++++++++++
> gdb/aarch64-tdep.c | 20 ++++++++++++++++++++
> gdb/arch/aarch64.h | 3 +++
> 3 files changed, 61 insertions(+)
>
> diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index
> f37b28067b8a..e9c9b8480aac 100644
> --- a/gdb/aarch64-linux-tdep.c
> +++ b/gdb/aarch64-linux-tdep.c
> @@ -2615,6 +2615,42 @@ aarch64_linux_get_shadow_stack_pointer
> (gdbarch *gdbarch, regcache *regcache,
> return gcspr;
> }
>
> +/* Return true, if FRAME is a valid shadow stack frame while FRAME.VALUE
> + does not refer to a return address. This can happen, for instance, in
> + case of signals: a signal handling specific GCS cap token will be
> + written to the GCS. In case this is true, configure the string which
> + describes the frame and is displayed instead of the address in the
> + shadow stack backtrace. */
> +
> +static bool
> +aarch64_linux_is_no_return_shadow_stack_address
> + (gdbarch *gdbarch,
> + const shadow_stack_frame_info &frame,
> + std::string &frame_type)
> +{
> + /* FRAME must be a valid shadow stack frame. */
> + bool valid_addr
> + = gdbarch_address_in_shadow_stack_memory_range (gdbarch,
> frame.ssp,
> + nullptr);
> + /* FIXME: Shouldn't be an assert. */
> + gdb_assert (valid_addr == true);
> +
> + /* If the GCS entry isn't a cap token, then it should be a return
> + address. */
> + if ((frame.value & AARCH64_GCS_CAP_ADDR_MASK) != frame.ssp)
> + return false;
> +
> + /* When delivering a signal, the Linux kernel writes a cap token with the
> + token type (bits 0..11) all clear. */ if ((frame.value &
> + AARCH64_GCS_CAP_TOKEN_MASK) == 0)
> + {
> + frame_type = _("<sigframe token>");
> + return true;
> + }
> +
> + return false;
> +}
> +
> /* AArch64 Linux implementation of the report_signal_info gdbarch
> hook. Displays information about possible memory tag violations. */
>
> @@ -3193,6 +3229,8 @@ aarch64_linux_init_abi (struct gdbarch_info info,
> struct gdbarch *gdbarch)
> {
> set_gdbarch_get_shadow_stack_pointer (gdbarch,
>
> aarch64_linux_get_shadow_stack_pointer);
> + set_gdbarch_is_no_return_shadow_stack_address (gdbarch,
> +
> aarch64_linux_is_no_return_shadow_stack_address);
> tdep->fn_prev_gcspr = dwarf2_prev_ssp;
> }
> }
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index
> 112d42c6a1ac..6bf63bcc1f97 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -1926,6 +1926,24 @@ aarch64_top_addr_empty_shadow_stack
> (gdbarch *gdbarch, const CORE_ADDR addr,
> return addr >= range.second - 8;
> }
>
> +/* Return the number of elements which are currently on the shadow stack
> + based on the shadow stack memory RANGE [start_address, end_address)
> + of the current thread. In case shadow stack is not enabled for the
> + current thread, return -1. */
> +
> +static long
> +aarch64_get_shadow_stack_size (gdbarch *gdbarch, const CORE_ADDR ssp,
> + const std::pair<CORE_ADDR, CORE_ADDR> range)
> {
> + const unsigned long shadow_stack_bytes = range.second - ssp;
> +
> + /* FIXME: Shouldn't be an assert. */ gdb_assert
> + ((shadow_stack_bytes % 8) == 0);
> +
> + /* The oldest entry in the GCS isn't an address, just the value '0'.
> +*/
> + return shadow_stack_bytes / 8 - 1;
> +}
> +
> /* Implement the "push_dummy_call" gdbarch method. */
>
> static CORE_ADDR
> @@ -4812,6 +4830,8 @@ aarch64_gdbarch_init (struct gdbarch_info info,
> struct gdbarch_list *arches)
> set_gdbarch_ssp_regnum (gdbarch, tdep->gcs_reg_base);
> set_gdbarch_top_addr_empty_shadow_stack
> (gdbarch, aarch64_top_addr_empty_shadow_stack);
> + set_gdbarch_get_shadow_stack_size (gdbarch,
> + aarch64_get_shadow_stack_size);
> }
>
> /* ABI */
> diff --git a/gdb/arch/aarch64.h b/gdb/arch/aarch64.h index
> 0e9715a4268a..7c69bfd01feb 100644
> --- a/gdb/arch/aarch64.h
> +++ b/gdb/arch/aarch64.h
> @@ -246,6 +246,9 @@ enum aarch64_regnum
> /* Size of the SME2 ZT0 register in bytes. */ #define
> AARCH64_SME2_ZT0_SIZE 64
>
> +#define AARCH64_GCS_CAP_TOKEN_MASK ((uint64_t) 0xFFF) #define
> +AARCH64_GCS_CAP_ADDR_MASK ~AARCH64_GCS_CAP_TOKEN_MASK
> +
> /* Feature check for Floating Point Mode Register. */ #ifndef
> HWCAP2_FPMR #define HWCAP2_FPMR (1ULL << 48)
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