From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id AXOhHaMsc2neDhEAWB0awg (envelope-from ) for ; Fri, 23 Jan 2026 03:09:07 -0500 Authentication-Results: simark.ca; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=K/F+SjiY; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 707601E0E1; Fri, 23 Jan 2026 03:09:07 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_INVALID,DKIM_SIGNED,HTML_MESSAGE,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 985441E093 for ; Fri, 23 Jan 2026 03:09:04 -0500 (EST) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 00C084BCA412 for ; Fri, 23 Jan 2026 08:09:04 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 00C084BCA412 Authentication-Results: sourceware.org; dkim=fail reason="signature verification failed" (2048-bit key, unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=K/F+SjiY Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) by sourceware.org (Postfix) with ESMTPS id D8F454BCA40E for ; Fri, 23 Jan 2026 08:06:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D8F454BCA40E Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=intel.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org D8F454BCA40E Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1769155610; cv=none; b=D4fwKl5sVIcH4QcOIddY45nsuMCNJ1768Rtr0ESAtmK6smSXy+WK/VFEMgMiKsmozbtxWDDTYnYXEmEVdoGbAk17Gu8d8JKewaq/D9cSieyboh4V2qPXzc0H6oq+ynlk3sgXuucF8dwlYxAMUebIoDCBAHmWUnA9U16yB5AsoqE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1769155610; c=relaxed/simple; bh=RLIaXHEXZm0gVBqHlxnNX9uRi0QuCTgAdXaHZttk/EA=; h=DKIM-Signature:MIME-Version:From:To:Subject:Date:Message-Id: MIME-Version; b=GnuNpTD2WxqdR3lXuspTz4SjreJd5DvUj6+9ECvp8JjHBLaofpbTNPhq1wAX7geMWmGdp0UrwZh/t3gyTwix2Kdf+ngCac/FuC539CwRG3HWNqHJRSwCNM9xsjtEGyhW0w7REGu1GvPu7q3PLGLOZ90uEa/tjw8fKWQu/AeCyjE= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D8F454BCA40E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1769155610; x=1800691610; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=RLIaXHEXZm0gVBqHlxnNX9uRi0QuCTgAdXaHZttk/EA=; b=K/F+SjiY/+GS2qSdkQabT8KQ0ysKmOniNdiy6U4ULogT0mVVKVRoLsyI mEirNHdxXnwSogLdw5QypUdfS7wwicTvJykJpbsKfI3/3McbIzv+yY8Ry wNnV1qr+JINmAU1ZEL7sSq1kXTS90BcDrG+wEnV0+A/Bgk0HKKckdbMik bH+UtgBvrRMm0IlbGx6HP+kMhlAdv71+dZtwJfH3O6wEB+MYvUIn0Y1Oe S3aUrAfuaFAdIduE11k3K3S+HGqd9dD18GWz+SIrHQa2ZtoCjSkoHP5Wo 5gQ5u2ciyeXYVgKzBLfVKUowGjBNpj9K87E6PeNXD8lNjI0NAZ/Ng3+mu A==; X-CSE-ConnectionGUID: AINQnSX0T1ykIOKXNsjXPQ== X-CSE-MsgGUID: JHmuoPKkSymvRoOIAgpgUw== X-IronPort-AV: E=McAfee;i="6800,10657,11679"; a="69603149" X-IronPort-AV: E=Sophos;i="6.21,248,1763452800"; d="scan'208";a="69603149" Content-Type: multipart/mixed; boundary="===============7876168674366956567==" MIME-Version: 1.0 Received: from orviesa009.jf.intel.com ([10.64.159.149]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Jan 2026 00:06:21 -0800 X-CSE-ConnectionGUID: 1+ARa6NaQDyqP7vySAUNrA== X-CSE-MsgGUID: JAQzdD7hQpqARMssEoM4Ng== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.21,248,1763452800"; d="scan'208";a="206785468" Received: from gkldtt-dev-004.igk.intel.com (HELO localhost) ([10.123.221.202]) by orviesa009-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Jan 2026 00:06:20 -0800 From: Christina Schimpe To: gdb-patches@sourceware.org Cc: thiago.bauermann@linaro.org Subject: [PATCH v2 7/9] gdb: Provide gdbarch hook to distinguish shadow stack backtrace elements. Date: Fri, 23 Jan 2026 08:05:29 +0000 Message-Id: <20260123080532.878738-8-christina.schimpe@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260123080532.878738-1-christina.schimpe@intel.com> References: <20260123080532.878738-1-christina.schimpe@intel.com> MIME-Version: 1.0 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org --===============7876168674366956567== Content-Transfer-Encoding: 8bit On x86 with CET or on ARM with GCS, there can be elements on the shadow stack which are not return addresses. In this case, we don't want to print the shadow stack element, but a string instead which describes the frame similar to the normal backtrace command for dummy frames or signals. Provide a gdbarch hook to distinguish between return and non-return addresses and to configure a string which is printed instead of the shadow stack element. --- gdb/doc/gdb.texinfo | 20 +++++++++++++ gdb/gdbarch-gen.c | 32 ++++++++++++++++++++ gdb/gdbarch-gen.h | 17 +++++++++++ gdb/gdbarch.h | 1 + gdb/gdbarch_components.py | 21 +++++++++++++ gdb/shadow-stack.c | 63 ++++++++++++++------------------------- gdb/shadow-stack.h | 40 +++++++++++++++++++++++++ 7 files changed, 154 insertions(+), 40 deletions(-) diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index f50d96a09f3..f7dc7c4397d 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -8772,6 +8772,26 @@ This is how a shadow stack backtrace looks like on amd64: #4 0x0000555555555065 in _start @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 +#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. diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c index be6570ad97d..76e841698af 100644 --- a/gdb/gdbarch-gen.c +++ b/gdb/gdbarch-gen.c @@ -263,6 +263,7 @@ struct gdbarch gdbarch_top_addr_empty_shadow_stack_ftype *top_addr_empty_shadow_stack = nullptr; int shadow_stack_element_size_aligned = 8; gdbarch_get_shadow_stack_size_ftype *get_shadow_stack_size = nullptr; + 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 @@ -537,6 +538,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of top_addr_empty_shadow_stack, has predicate. */ /* Skip verify of shadow_stack_element_size_aligned, invalid_p == 0. */ /* Skip verify of get_shadow_stack_size, has predicate. */ + /* 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 ()); @@ -1420,6 +1422,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) gdb_printf (file, "gdbarch_dump: get_shadow_stack_size = <%s>\n", host_address_to_string (gdbarch->get_shadow_stack_size)); + 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); } @@ -5603,3 +5611,27 @@ set_gdbarch_get_shadow_stack_size (struct gdbarch *gdbarch, { gdbarch->get_shadow_stack_size = get_shadow_stack_size; } + +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, std::string &frame_type) +{ + 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, frame_type); +} + +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 155d60b1b8b..e24a5415ce0 100644 --- a/gdb/gdbarch-gen.h +++ b/gdb/gdbarch-gen.h @@ -1843,3 +1843,20 @@ extern bool gdbarch_get_shadow_stack_size_p (struct gdbarch *gdbarch); typedef long (gdbarch_get_shadow_stack_size_ftype) (struct gdbarch *gdbarch, const std::optional ssp, const std::pair range); extern long gdbarch_get_shadow_stack_size (struct gdbarch *gdbarch, const std::optional ssp, const std::pair range); extern void set_gdbarch_get_shadow_stack_size (struct gdbarch *gdbarch, gdbarch_get_shadow_stack_size_ftype *get_shadow_stack_size); + +/* 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 options '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. In this case, + also the string frame_type has to be configured to display the type in the + shadow stack backtrace. */ + +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, std::string &frame_type); +extern bool gdbarch_is_no_return_shadow_stack_address (struct gdbarch *gdbarch, const shadow_stack_frame_info &frame, std::string &frame_type); +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 fe59846b916..6e966e12f21 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 1f24a3a6d72..d39cba6e016 100644 --- a/gdb/gdbarch_components.py +++ b/gdb/gdbarch_components.py @@ -2911,3 +2911,24 @@ In case shadow stack is not enabled for the current thread, return -1. ], predicate=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 options '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. In this case, +also the string frame_type has to be configured to display the type in the +shadow stack backtrace. +""", + type="bool", + name="is_no_return_shadow_stack_address", + params=[ + ("const shadow_stack_frame_info &", "frame"), + ("std::string &", "frame_type")], + predicate=True, +) diff --git a/gdb/shadow-stack.c b/gdb/shadow-stack.c index 82d98c7336c..e930187604e 100644 --- a/gdb/shadow-stack.c +++ b/gdb/shadow-stack.c @@ -203,46 +203,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 unwind_prev_shadow_stack_frame_info - (gdbarch *gdbarch, std::pair 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; -}; - /* Attempt to obtain the function name based on the symbol of PC. */ static gdb::unique_xmalloc_ptr @@ -271,6 +231,29 @@ do_print_shadow_stack_frame_info const frame_print_options &fp_opts, const shadow_stack_frame_info &frame, print_what print_what) { + std::string frame_type; + if (gdbarch_is_no_return_shadow_stack_address_p (gdbarch) + && gdbarch_is_no_return_shadow_stack_address (gdbarch, + frame, + frame_type)) + { + gdb_assert (!frame_type.empty ()); + + /* 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); + + uiout->field_string + ("func", frame_type, 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. */ diff --git a/gdb/shadow-stack.h b/gdb/shadow-stack.h index 5370becfc9a..24a6d48efa0 100644 --- a/gdb/shadow-stack.h +++ b/gdb/shadow-stack.h @@ -41,4 +41,44 @@ 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 unwind_prev_shadow_stack_frame_info + (gdbarch *gdbarch, std::pair 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 --===============7876168674366956567== Content-Type: multipart/alternative; boundary="===============7186952824200745428==" MIME-Version: 1.0 Content-Disposition: inline --===============7186952824200745428== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Intel Deutschland GmbH Registered Address: Dornacher Stra=C3=9Fe 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 M=C3=BCnchen HRB 186928 --===============7186952824200745428== Content-Type: text/html; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable
Intel Deutschland GmbH
Registered Address: Dornacher Stra=C3= =9Fe 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.deManaging Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell<= div>Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: M= unich
Commercial Register: Amtsgericht M=C3=BCnchen HRB 186928 --===============7186952824200745428==-- --===============7876168674366956567==--