From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id uSeQA26D0mimLA8AWB0awg (envelope-from ) for ; Tue, 23 Sep 2025 07:24:30 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=SUfwBZRM; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 089581E0BA; Tue, 23 Sep 2025 07:24:30 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,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 server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (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 370C51E047 for ; Tue, 23 Sep 2025 07:24:27 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id CFD933858419 for ; Tue, 23 Sep 2025 11:24:26 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CFD933858419 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=SUfwBZRM Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.16]) by sourceware.org (Postfix) with ESMTPS id 237D43858C78 for ; Tue, 23 Sep 2025 11:19:42 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 237D43858C78 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 237D43858C78 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=198.175.65.16 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758626382; cv=none; b=XR74fJbtLR6Oy6lx7o6V+vQYrqYg74VM/TQBgjDfWFGSUY+3sYmcvZGAoC1YkMrUBiAc+izWK/q04tPcqcXVB7DJXX5eIhB5KpZ12oX4YIPnwyWdws2+RIG3oV8oNjlLDztN1yVr8zwXI7ZQOgXEVSgWB4gLw8vpwSaan0m89yY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758626382; c=relaxed/simple; bh=z3j/gf+2Tp9Lh7LFCFYoRz4ec1udNSsgPxxiW78SuQ0=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=ZrSjcqrJspX5g7IwZkyVkz+/ihqtf2I0pv80BLU3CaA+5X04ghGs6UGcHz6kFUWNKIEHesl6vVCHl1man7bbcNXL3n+/rA+yzdD+pCa7oYlMwh4jeUwYnKoZAKiHdP8A+ojDOQvcmFjYt9dm7qtTDjH/JnC344neg1NpDKPOP1M= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 237D43858C78 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1758626382; x=1790162382; h=from:to:subject:date:message-id:in-reply-to:references: mime-version:content-transfer-encoding; bh=z3j/gf+2Tp9Lh7LFCFYoRz4ec1udNSsgPxxiW78SuQ0=; b=SUfwBZRMSeuxPVJgfXx0iLVcJf+6/oGIX/3DBBPxlozjAIPvZHBbjutY s35CoCpgTXG7PoK4bhvQlVVjv5Rhwkrcs6GJpzpw4+edojt/BZqKVAKtc n/O4me8PlNl9XQG4ExEc73EW5C/dKdMV+37tDgeNwR6Eo0/NAlm37/jqS Fusxx6tviA7AQq1P8mn7JnG93W3HcmuCR/eXh6kLw3W2TBZLSyL9NkVpN c1a/AS6oqMBR38fm1qnVcWIVFTO3ZVcpkQGa5JBV0yWk5IaEQMYSO/ARD KJgucKwufavho6NcvwYmF1dKpVJua5mXL22NXeE6yvU5F9Ol+p499WPCs A==; X-CSE-ConnectionGUID: CAqxNg/RRG69eMEmeO92tw== X-CSE-MsgGUID: 1naT66oZTbif10yDdk8ycA== X-IronPort-AV: E=McAfee;i="6800,10657,11561"; a="61071761" X-IronPort-AV: E=Sophos;i="6.18,287,1751266800"; d="scan'208";a="61071761" Received: from fmviesa007.fm.intel.com ([10.60.135.147]) by orvoesa108.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Sep 2025 04:19:41 -0700 X-CSE-ConnectionGUID: HnpxZT3IRHiiDllYMzyXng== X-CSE-MsgGUID: eWr0UTpRT/WjI1CO29tqsg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.18,287,1751266800"; d="scan'208";a="176335256" Received: from gkldtt-dev-004.igk.intel.com (HELO localhost) ([10.123.221.202]) by fmviesa007-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Sep 2025 04:19:40 -0700 From: Christina Schimpe To: gdb-patches@sourceware.org Subject: [PATCH 6/9] gdb: Implement 'bt shadow' to print the shadow stack backtrace. Date: Tue, 23 Sep 2025 11:18:39 +0000 Message-Id: <20250923111842.4091694-7-christina.schimpe@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250923111842.4091694-1-christina.schimpe@intel.com> References: <20250923111842.4091694-1-christina.schimpe@intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 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 Add a subcommand 'bt shadow' for the ordinary backtrace command which prints the shadow stack backtrace. Similar to the ordinary backtrace command 'bt shadow' can be configured using COUNT and the command line option -frame-info. However, we always print the address and the command is not affected by the setting "print address" as well as the setting "print frame-info location-and-address". Also we do not print the frame arguments. Usage: backtrace|bt shadow [OPTION]... [COUNT | -COUNT] Help output: ~~ (gdb) help bt shadow Print backtrace of all shadow stack frames, or innermost COUNT frames. Usage: backtrace shadow [OPTION]... [COUNT | -COUNT] Options: -frame-info auto|source-line|location|source-and-location|location-and-address|short-location Set printing of shadow stack frame information. With a negative COUNT, print outermost -COUNT elements. ~~ Example for the output of 'bt shadow' on amd64 linux: ~~ (gdb) bt shadow /#0 0x000000000040111f in call1 at amd64-shadow-stack.c:14 /#1 0x000000000040112f in main at amd64-shadow-stack.c:21 /#2 0x00007ffff7c3fe70 in __libc_start_call_main at ../sysdeps/nptl/libc_start_call_main.h:58 /#3 0x00007ffff7c3ff20 in __libc_start_main_impl at ../csu/libc-start.c:128 /#4 0x0000000000401045 in _start ~~ This commit also adds a test for 'bt shadow' on amd64. Note that 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 | 7 + gdb/annotate.c | 93 +++- gdb/annotate.h | 18 +- gdb/doc/gdb.texinfo | 48 ++ gdb/shadow-stack.c | 504 +++++++++++++++++- gdb/shadow-stack.h | 19 + gdb/stack.c | 104 ++-- gdb/stack.h | 55 ++ .../gdb.arch/amd64-shadow-stack-cmds.exp | 88 +++ gdb/testsuite/gdb.base/help.exp | 6 +- gdb/testsuite/gdb.base/options.exp | 7 +- 11 files changed, 885 insertions(+), 64 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 8be367d2424..eb96ff2ae08 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -33,6 +33,13 @@ single-inf-arg in qSupported reply with the single-inf-arg feature to indicate that it is able to accept arguments as a single string. +* New commands + +backtrace shadow [option]... [count | -count] +bt shadow [option]... [count | -count] + Print backtrace of all shadow stack frames, or innermost 'count' frames. + The command is a subcommand of the ordinary backtrace command. + *** Changes in GDB 17 * Debugging Linux programs that use x86-64 or x86-64 with 32-bit pointer diff --git a/gdb/annotate.c b/gdb/annotate.c index 55db2e2e8e1..5acae566095 100644 --- a/gdb/annotate.c +++ b/gdb/annotate.c @@ -506,10 +506,15 @@ annotate_frame_address_end (void) } void -annotate_frame_function_name (void) +annotate_frame_function_name (bool shadowstack_frame) { if (annotation_level == 2) - printf_unfiltered (("\n\032\032frame-function-name\n")); + { + if (!shadowstack_frame) + printf_unfiltered (("\n\032\032frame-function-name\n")); + else + printf_unfiltered (("\n\032\032shadow-stack-frame-function-name\n")); + } } void @@ -520,45 +525,75 @@ annotate_frame_args (void) } void -annotate_frame_source_begin (void) +annotate_frame_source_begin (bool shadowstack_frame) { if (annotation_level == 2) - printf_unfiltered (("\n\032\032frame-source-begin\n")); + { + if (!shadowstack_frame) + printf_unfiltered (("\n\032\032frame-source-begin\n")); + else + printf_unfiltered (("\n\032\032shadow-stack-frame-source-begin\n")); + } } void -annotate_frame_source_file (void) +annotate_frame_source_file (bool shadowstack_frame) { if (annotation_level == 2) - printf_unfiltered (("\n\032\032frame-source-file\n")); + { + if (!shadowstack_frame) + printf_unfiltered (("\n\032\032frame-source-file\n")); + else + printf_unfiltered (("\n\032\032shadow-stack-frame-source-file\n")); + } } void -annotate_frame_source_file_end (void) +annotate_frame_source_file_end (bool shadowstack_frame) { if (annotation_level == 2) - printf_unfiltered (("\n\032\032frame-source-file-end\n")); + { + if (!shadowstack_frame) + printf_unfiltered (("\n\032\032frame-source-file-end\n")); + else + printf_unfiltered (("\n\032\032shadow-stack-frame-source-file-end\n")); + } } void -annotate_frame_source_line (void) +annotate_frame_source_line (bool shadowstack_frame) { if (annotation_level == 2) - printf_unfiltered (("\n\032\032frame-source-line\n")); + { + if (!shadowstack_frame) + printf_unfiltered (("\n\032\032frame-source-line\n")); + else + printf_unfiltered (("\n\032\032shadow-stack-frame-source-line\n")); + } } void -annotate_frame_source_end (void) +annotate_frame_source_end (bool shadowstack_frame) { if (annotation_level == 2) - printf_unfiltered (("\n\032\032frame-source-end\n")); + { + if (!shadowstack_frame) + printf_unfiltered (("\n\032\032frame-source-end\n")); + else + printf_unfiltered (("\n\032\032shadow-stack-frame-source-end\n")); + } } void -annotate_frame_where (void) +annotate_frame_where (bool shadowstack_frame) { if (annotation_level == 2) - printf_unfiltered (("\n\032\032frame-where\n")); + { + if (!shadowstack_frame) + printf_unfiltered (("\n\032\032frame-where\n")); + else + printf_unfiltered (("\n\032\032shadow-stack-frame-where\n")); + } } void @@ -568,6 +603,36 @@ annotate_frame_end (void) printf_unfiltered (("\n\032\032frame-end\n")); } + +/* Annotations for shadow stack frames. */ + +void +annotate_shadowstack_frame_begin (int level, gdbarch *gdbarch, CORE_ADDR pc) +{ + if (annotation_level > 1) + printf_unfiltered (("\n\032\032shadow-stack-frame-begin %d %s\n"), + level, paddress (gdbarch, pc)); +} + +void annotate_shadowstack_frame_address (void) +{ + if (annotation_level == 2) + printf_unfiltered (("\n\032\032shadow-stack-frame-address\n")); +} + +void +annotate_shadowstack_frame_address_end (void) +{ + if (annotation_level == 2) + printf_unfiltered (("\n\032\032shadow-stack-frame-address-end\n")); +} + +void annotate_shadowstack_frame_end () +{ + if (annotation_level == 2) + printf_unfiltered (("\n\032\032shadow-stack-frame-end\n")); +} + void annotate_array_section_begin (int idx, struct type *elttype) { diff --git a/gdb/annotate.h b/gdb/annotate.h index 0f394f5ccdc..675b0b771a0 100644 --- a/gdb/annotate.h +++ b/gdb/annotate.h @@ -121,15 +121,19 @@ extern void annotate_function_call (void); extern void annotate_signal_handler_caller (void); extern void annotate_frame_address (void); extern void annotate_frame_address_end (void); -extern void annotate_frame_function_name (void); +extern void annotate_frame_function_name (bool shadowstack_frame = false); extern void annotate_frame_args (void); -extern void annotate_frame_source_begin (void); -extern void annotate_frame_source_file (void); -extern void annotate_frame_source_file_end (void); -extern void annotate_frame_source_line (void); -extern void annotate_frame_source_end (void); -extern void annotate_frame_where (void); +extern void annotate_frame_source_begin (bool shadowstack_frame = false); +extern void annotate_frame_source_file (bool shadowstack_frame = false); +extern void annotate_frame_source_file_end (bool shadowstack_frame = false); +extern void annotate_frame_source_line (bool shadowstack_frame = false); +extern void annotate_frame_source_end (bool shadowstack_frame = false); +extern void annotate_frame_where (bool shadowstack_frame = false); extern void annotate_frame_end (void); +extern void annotate_shadowstack_frame_begin (int, gdbarch *, CORE_ADDR); +extern void annotate_shadowstack_frame_address (void); +extern void annotate_shadowstack_frame_address_end (void); +extern void annotate_shadowstack_frame_end (void); extern void annotate_array_section_begin (int, struct type *); extern void annotate_elt_rep (unsigned int); diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index e8515883820..ebda4546b58 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -8840,6 +8840,53 @@ Display an absolute filename. Show the current way to display filenames. @end table +@subsection Shadow stack backtrace +@value{GDBN} provides a subcommand of the backtrace command to print the +shadow stack backtrace. + +@anchor{shadowstack-backtrace-command} +@kindex backtrace shadow +@kindex bt shadow @r{(@code{backtrace shadow})} +To print a backtrace of the entire shadow stack, use the +@code{backtrace shadow} command, or its alias @code{bt shadow}. This +command will print 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 (@xref{CET}) on x86. + +@table @code +@item backtrace shadow [@var{option}]@dots{} @dots{} [@var{count}] +@itemx bt shadow [@var{option}]@dots{} @dots{} [@var{count}] +Print the backtrace of all shadow stack frames +@end table + +In contrast to the backtrace command, + +@itemize @bullet +@item the options @samp{-past-main} or @samp{-past-entry} are not supported +and all available shadow stack frames are printed by default +@item Python frame filters are not supported +@item frame local variables are not printed +@item frame arguments are not printed and therefore the options +@samp{-frame-arguments} and @samp{-raw-frame-arguments} are not supported. +@item configuring the option @samp{-frame-info} or its corresponding +global setting @ref{set print frame-info} to @code{location} is treated +same as configuring it to @code{location-and-address}, as @value{GDBN} +always prints the address for shadow stack frames. +@end itemize + +This is how a shadow stack backtrace looks like on amd64: +@smallexample +@group +#0 0x000000000040111f in call1 at amd64-shadow-stack.c:14 +#1 0x000000000040112f in main at amd64-shadow-stack.c:21 +#2 0x00007ffff7c3fe70 in __libc_start_call_main at ../sysdeps/nptl/libc_start_call_main.h:58 +#3 0x00007ffff7c3ff20 in __libc_start_main_impl at ../csu/libc-start.c:128 +#4 0x0000000000401045 in _start +@end group +@end smallexample + @node Selection @section Selecting a Frame @@ -27132,6 +27179,7 @@ registers @end itemize +@node CET @subsubsection Intel Control-Flow Enforcement Technology. @cindex Intel Control-Flow Enforcement Technology. diff --git a/gdb/shadow-stack.c b/gdb/shadow-stack.c index d153d5fc846..759ef6989ea 100644 --- a/gdb/shadow-stack.c +++ b/gdb/shadow-stack.c @@ -23,6 +23,11 @@ #include "frame.h" #include "frame-unwind.h" #include "shadow-stack.h" +#include "annotate.h" +#include "stack.h" +#include "solib.h" +#include "event-top.h" +#include "cli/cli-style.h" enum class ssp_update_direction { @@ -38,17 +43,19 @@ enum class ssp_update_direction static CORE_ADDR update_shadow_stack_pointer (gdbarch *gdbarch, CORE_ADDR ssp, + const unsigned int count, const ssp_update_direction direction) { bool increment = (gdbarch_stack_grows_down (gdbarch)) ? (direction == ssp_update_direction::bottom) : (direction == ssp_update_direction::top); - CORE_ADDR new_ssp; + const int element_size + = gdbarch_shadow_stack_element_size_aligned (gdbarch); if (increment) - return ssp + gdbarch_shadow_stack_element_size_aligned (gdbarch); + return ssp + count * element_size; else - return ssp - gdbarch_shadow_stack_element_size_aligned (gdbarch); + return ssp - count * element_size; } /* See shadow-stack.h. */ @@ -68,7 +75,7 @@ void shadow_stack_push (gdbarch *gdbarch, regcache *regcache, return; const CORE_ADDR new_ssp - = update_shadow_stack_pointer (gdbarch, *ssp, + = update_shadow_stack_pointer (gdbarch, *ssp, 1, ssp_update_direction::top); /* If NEW_SSP does not point to shadow stack memory, we assume the stack @@ -131,7 +138,7 @@ dwarf2_prev_ssp (const frame_info_ptr &this_frame, void **this_cache, implement this unwinder without support for shadow stack switches for now. */ const CORE_ADDR new_ssp - = update_shadow_stack_pointer (gdbarch, ssp, + = update_shadow_stack_pointer (gdbarch, ssp, 1, ssp_update_direction::bottom); /* On x86, if NEW_SSP points to the end of RANGE, it indicates @@ -165,3 +172,490 @@ dwarf2_prev_ssp (const frame_info_ptr &this_frame, void **this_cache, return retval; } + +/* Option definitions for some shadow stack related settings. */ + +using shadowstack_enum_option_def + = gdb::option::enum_option_def; + +static const gdb::option::option_def shadowstack_print_option_defs[] = { + + shadowstack_enum_option_def { + "frame-info", + print_frame_info_choices, + [] (shadow_stack_print_options *opt) { return &opt->print_frame_info; }, + nullptr, /* show_cmd_cb */ + N_("Set printing of shadow stack frame information."), + N_("Show printing of shadow stack frame information."), + nullptr /* help_doc */ + } + +}; + +/* 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_frame_sal). 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; +} + +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; +}; + +static +gdb::unique_xmalloc_ptr find_pc_funname (CORE_ADDR pc) +{ + symbol *func = find_pc_function (pc); + if (func) + return find_symbol_funname (func); + + gdb::unique_xmalloc_ptr funname; + bound_minimal_symbol msymbol = lookup_minimal_symbol_by_pc (pc); + if (msymbol.minsym != nullptr) + funname.reset (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, gdbarch *gdbarch, + const shadow_stack_print_options &print_options, + const shadow_stack_frame_info &frame, print_what print_what) +{ + if (print_options.print_frame_info != print_frame_info_auto) + { + /* Use the specific frame information desired by the user. */ + print_what + = *print_frame_info_to_print_what (print_options.print_frame_info); + } + + /* In contrast to find_frame_sal which is used for the ordinary backtrace + command, we always want to print the line that is actually referred + to by the address in the linetable. That's why we always pass 0 here + as second argument. */ + symtab_and_line sal = find_pc_line (frame.value, 0); + + if (should_print_location (print_what) || sal.symtab == nullptr) + { + gdb::unique_xmalloc_ptr funname + = find_pc_funname (frame.value); + + { /* Extra scope to print frame tuple. */ + ui_out_emit_tuple tuple_emitter (uiout, "shadow-stack-frame"); + + annotate_shadowstack_frame_begin (frame.level, gdbarch, + frame.value); + + uiout->text ("#"); + uiout->field_fmt_signed (2, ui_left, "level", frame.level); + + annotate_shadowstack_frame_address (); + + /* 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 ()); + + annotate_shadowstack_frame_address_end (); + + uiout->text (" in "); + print_funname (uiout, funname, true); + + if (print_what != SHORT_LOCATION && sal.symtab != nullptr) + print_filename (uiout, sal, true); + + 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, true); + } + } /* Extra scope to print frame tuple. */ + + uiout->text ("\n"); + } + + if (print_what == SRC_LINE || print_what == SRC_AND_LOC) + { + int 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 + 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, gdbarch, frame.value, sal, print_address, + mid_statement, ""); + } + + annotate_shadowstack_frame_end (); + 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 + (gdbarch *gdbarch, const shadow_stack_print_options &print_options, + const shadow_stack_frame_info &frame, print_what print_what) +{ + do_with_buffered_output + (do_print_shadow_stack_frame_info, current_uiout, gdbarch, + print_options, frame, print_what); +} + + +/* Extract a char array which can be used for printing a reasonable + error message for REASON. Note that in case REASON has the value + NO_ERROR the returned array is empty. */ + +static const char * +ssp_unwind_stop_reason_to_err_string (ssp_unwind_stop_reason reason) +{ + switch (reason) + { + case ssp_unwind_stop_reason::no_error: + return _(""); + case ssp_unwind_stop_reason::memory_read_error: + return _("shadow stack memory read failure"); + } + + gdb_assert_not_reached ("invalid reason"); +} + + +/* Read the memory at shadow stack pointer SSP and assign it to + RETURN_VALUE. In case we cannot read the memory, set REASON to + ssp_unwind_stop_reason::memory_read_error and return false. */ + +static bool +read_shadow_stack_memory (gdbarch *gdbarch, CORE_ADDR ssp, + CORE_ADDR *return_value, + ssp_unwind_stop_reason *reason) +{ + /* 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); + if (!safe_read_memory_unsigned_integer (ssp, element_size, byte_order, + return_value)) + { + *reason = ssp_unwind_stop_reason::memory_read_error; + return false; + } + + return true; +} + +/* If possible, return a shadow stack frame info which is COUNT elements + above the bottom of the shadow stack. FRAME should point to the top + 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 +get_trailing_outermost_shadow_stack_frame_info + (gdbarch *gdbarch, const std::pair range, + const ULONGEST count, shadow_stack_frame_info &frame) +{ + /* Compute the number of bytes on the shadow stack, starting at + FRAME->SSP, which depends on the direction the shadow stack + grows. */ + const int element_size + = gdbarch_shadow_stack_element_size_aligned (gdbarch); + const unsigned long shadow_stack_bytes + = (gdbarch_stack_grows_down (gdbarch)) + ? range.second - frame.ssp : frame.ssp - range.first + element_size; + + gdb_assert ((shadow_stack_bytes % element_size) == 0); + const unsigned long shadow_stack_size + = shadow_stack_bytes / element_size; + const long level = shadow_stack_size - count; + + /* COUNT exceeds the number of elements on the shadow stack. Return the + starting shadow stack frame info FRAME. */ + if (level <= 0) + return std::optional (frame); + + CORE_ADDR new_ssp = update_shadow_stack_pointer + (gdbarch, frame.ssp, count, ssp_update_direction::bottom); + + if (gdbarch_stack_grows_down (gdbarch)) + gdb_assert (new_ssp < range.second); + else + gdb_assert (new_ssp >= range.first); + + CORE_ADDR new_value; + if (!read_shadow_stack_memory (gdbarch, new_ssp, &new_value, + &frame.unwind_stop_reason)) + return {}; + + return std::optional + ({new_ssp, new_value, (ULONGEST) level, + ssp_unwind_stop_reason::no_error}); +} + +std::optional +shadow_stack_frame_info::unwind_prev_shadow_stack_frame_info + (gdbarch *gdbarch, std::pair range) +{ + /* 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 (gdbarch, this->ssp, 1, + ssp_update_direction::bottom); + + if (gdbarch_stack_grows_down (gdbarch)) + { + /* The shadow stack grows downwards. */ + if (new_ssp >= range.second) + { + /* We reached the bottom of the shadow stack. */ + return {}; + } + /* We updated new_ssp towards the bottom 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 bottom of the shadow stack. */ + return {}; + } + /* We updated new_ssp towards the bottom of the shadow stack before, + and new_ssp must be pointing to shadow stack memory. */ + gdb_assert (new_ssp <= range.second); + } + + CORE_ADDR new_value; + if (!read_shadow_stack_memory (gdbarch, new_ssp, &new_value, + &this->unwind_stop_reason)) + return {}; + + return std::optional + ({new_ssp, new_value, this->level + 1, + ssp_unwind_stop_reason::no_error}); +} + +/* Print all elements on the shadow stack or just the innermost COUNT_EXP + frames. */ + +static void +backtrace_shadow_command (const shadow_stack_print_options &print_options, + 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)) + 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 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 target.")); + + /* 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. */ + std::pair range; + if (!gdbarch_address_in_shadow_stack_memory_range (gdbarch, *start_ssp, + &range)) + { + /* If the current shadow stack pointer does not point to shadow + stack memory, the shadow stack is empty. */ + 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 current; + CORE_ADDR new_value; + if (read_shadow_stack_memory (gdbarch, *start_ssp, &new_value, &reason)) + current = {*start_ssp, new_value, 0, + ssp_unwind_stop_reason::no_error}; + + std::optional 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 + (gdbarch, range, std::abs (count), *current); + + if (!trailing.has_value ()) + reason = current->unwind_stop_reason; + } + } + + if (!trailing.has_value ()) + { + if (reason > ssp_unwind_stop_reason::no_error) + error (_("Cannot print shadow stack backtrace: %s.\n"), + ssp_unwind_stop_reason_to_err_string (reason)); + else + gdb_assert_not_reached ("invalid reason"); + } + + current = trailing; + while (current.has_value () && count--) + { + QUIT; + + print_shadow_stack_frame_info (gdbarch, print_options, *current, + LOCATION); + + trailing = current; + current = current->unwind_prev_shadow_stack_frame_info (gdbarch, + range); + } + + /* If we've stopped before the end, mention that. */ + if (current && from_tty) + gdb_printf (_("(More shadow stack frames follow...)\n")); + + /* 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.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 (gdbarch, trailing->ssp), + ssp_unwind_stop_reason_to_err_string + (trailing->unwind_stop_reason)); +} + +void +backtrace_shadow_command (const char *arg, int from_tty) +{ + shadow_stack_print_options print_options; + print_options.print_frame_info + = user_frame_print_options.print_frame_info; + + auto grp = make_backtrace_shadow_options_def_group (&print_options); + gdb::option::process_options + (&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp); + + if (arg != nullptr && *arg == '\0') + arg = nullptr; + + backtrace_shadow_command (print_options, arg, from_tty); +} + +std::array +make_backtrace_shadow_options_def_group + (shadow_stack_print_options *print_options) +{ + return {{ + { {shadowstack_print_option_defs}, print_options } + }}; +} diff --git a/gdb/shadow-stack.h b/gdb/shadow-stack.h index 5c3ba80974e..2d33efafd22 100644 --- a/gdb/shadow-stack.h +++ b/gdb/shadow-stack.h @@ -36,4 +36,23 @@ void shadow_stack_push (gdbarch *gdbarch, regcache *regcache, value * dwarf2_prev_ssp (const frame_info_ptr &this_frame, void **this_cache, int regnum); +/* Data for the printing of shadow stack frame information, exposed as + command options. */ + +struct shadow_stack_print_options +{ + const char *print_frame_info = print_frame_info_auto; +}; + +/* Implementation of "backtrace shadow" comand. */ + +void backtrace_shadow_command (const char *arg, int from_tty); + +/* Create an option_def_group array grouping all the "backtrace shadow" + options, with SSP_OPTS as contexts. */ + +std::array + make_backtrace_shadow_options_def_group + (shadow_stack_print_options *print_options); + #endif /* GDB_SHADOW_STACK_H */ diff --git a/gdb/stack.c b/gdb/stack.c index 40756f74a00..30a7d8621be 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -50,6 +50,7 @@ #include "linespec.h" #include "cli/cli-utils.h" #include "objfiles.h" +#include "shadow-stack.h" #include "symfile.h" #include "extension.h" @@ -86,7 +87,7 @@ const char print_frame_info_source_and_location[] = "source-and-location"; const char print_frame_info_location_and_address[] = "location-and-address"; const char print_frame_info_short_location[] = "short-location"; -static const char *const print_frame_info_choices[] = +const char *const print_frame_info_choices[] = { print_frame_info_auto, print_frame_info_source_line, @@ -962,11 +963,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 +std::optional print_frame_info_to_print_what (const char *print_frame_info) { for (int i = 0; print_frame_info_choices[i] != NULL; i++) @@ -1016,7 +1015,7 @@ get_user_print_what_frame_info (std::optional *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 @@ -1025,14 +1024,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) @@ -1298,7 +1292,7 @@ get_last_displayed_sal () /* Find the function name for the symbol SYM. */ -static gdb::unique_xmalloc_ptr +gdb::unique_xmalloc_ptr find_symbol_funname (const symbol *sym) { gdb::unique_xmalloc_ptr funname; @@ -1364,31 +1358,28 @@ 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 -print_lib (ui_out *uiout, const char *lib) +void +print_lib (ui_out *uiout, const char *lib, bool shadowstack_frame) { - annotate_frame_where (); + annotate_frame_where (shadowstack_frame); uiout->wrap_hint (2); uiout->text (" from "); uiout->field_string ("from", lib, file_name_style.style ()); } -/* Print the filenname of SAL to UIOUT for the printing of frame - information. */ - -static void -print_filename (ui_out *uiout, symtab_and_line sal) +void +print_filename (ui_out *uiout, symtab_and_line sal, bool shadowstack_frame) { - annotate_frame_source_begin (); + annotate_frame_source_begin (shadowstack_frame); const char *filename_display; filename_display = symtab_to_filename_for_display (sal.symtab); uiout->wrap_hint (3); uiout->text (" at "); - annotate_frame_source_file (); + annotate_frame_source_file (shadowstack_frame); + uiout->field_string ("file", filename_display, file_name_style.style ()); if (uiout->is_mi_like_p ()) @@ -1397,21 +1388,21 @@ print_filename (ui_out *uiout, symtab_and_line sal) uiout->field_string ("fullname", fullname); } - annotate_frame_source_file_end (); + annotate_frame_source_file_end (shadowstack_frame); uiout->text (":"); - annotate_frame_source_line (); + annotate_frame_source_line (shadowstack_frame); uiout->field_signed ("line", sal.line, line_number_style.style ()); - annotate_frame_source_end (); + annotate_frame_source_end (shadowstack_frame); } -/* 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 const &funname) + gdb::unique_xmalloc_ptr const &funname, + bool shadowstack_frame) { - annotate_frame_function_name (); + annotate_frame_function_name (shadowstack_frame); string_file stb; gdb_puts (funname ? funname.get () : "??", &stb); uiout->field_stream ("func", stb, function_name_style.style ()); @@ -2223,6 +2214,23 @@ backtrace_command (const char *arg, int from_tty) backtrace_command_1 (fp_opts, bt_cmd_opts, arg, from_tty); } +/* Completer for the "backtrace shadow" sub-command. */ + +static void +backtrace_shadow_command_completer (struct cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char */*word*/) +{ + const auto group + = make_backtrace_shadow_options_def_group (nullptr); + if (gdb::option::complete_options + (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group)) + return; + + const char *word = advance_to_expression_complete_word_point (tracker, text); + expression_completer (ignore, tracker, text, word); +} + /* Completer for the "backtrace" command. */ static void @@ -3314,6 +3322,9 @@ static struct cmd_list_element *select_frame_cmd_list = NULL; /* Commands with a prefix of `info frame'. */ static struct cmd_list_element *info_frame_cmd_list = NULL; +/* Commands with a prefix of `backtrace' of `bt'. */ +static cmd_list_element *bt_list = nullptr; + INIT_GDB_FILE (stack) { struct cmd_list_element *cmd; @@ -3503,10 +3514,31 @@ With a negative COUNT, print outermost -COUNT frames."), backtrace_opts); cmd_list_element *backtrace_cmd - = add_com ("backtrace", class_stack, backtrace_command, - backtrace_help.c_str ()); + = add_prefix_cmd ("backtrace", class_stack, backtrace_command, + backtrace_help.c_str (), &bt_list, 1, &cmdlist); set_cmd_completer_handle_brkchars (backtrace_cmd, backtrace_command_completer); + const auto backtrace_shadow_opts + = make_backtrace_shadow_options_def_group (nullptr); + + static std::string backtrace_shadow_help + = gdb::option::build_help (_("\ +Print backtrace of all shadow stack frames, or innermost COUNT frames.\n\ +Usage: backtrace shadow [OPTION]... [COUNT | -COUNT]\n\ +\n\ +Options:\n\ +%OPTIONS%\n\ +\n\ +With a negative COUNT, print outermost -COUNT elements."), + backtrace_shadow_opts); + + cmd_list_element *bt_shadow_cmd = add_cmd ("shadow", class_stack, + backtrace_shadow_command, + backtrace_shadow_help.c_str (), + &bt_list); + set_cmd_completer_handle_brkchars + (bt_shadow_cmd, backtrace_shadow_command_completer); + add_com_alias ("bt", backtrace_cmd, class_stack, 0); add_com_alias ("where", backtrace_cmd, class_stack, 0); diff --git a/gdb/stack.h b/gdb/stack.h index 6b2efb8fe60..966cf5caed0 100644 --- a/gdb/stack.h +++ b/gdb/stack.h @@ -84,4 +84,59 @@ void frame_apply_all_cmd_completer (struct cmd_list_element *ignore, completion_tracker &tracker, const char *text, const char */*word*/); +/* Print the filenname of SAL to UIOUT for the printing of frame + information. In case SHADOWSTACK_FRAME is true, we annotate for a + shadow stack frame. If it is false (default), we annotate for a + normal frame. */ + +void print_filename (ui_out *uiout, symtab_and_line sal, + bool shadowstack_frame = false); + +/* Print the library LIB to UIOUT for the printing of frame + information. In case SHADOWSTACK_FRAME is true, we annotate for a + shadow stack frame. If it is false (default), we annotate for a + normal frame. */ + +void print_lib (ui_out *uiout, const char *lib, + bool shadowstack_frame = false); + +/* If available, print FUNNAME to UIOUT for the printing of frame + information. In case SHADOWSTACK_FRAME is true, we annotate for a + shadow stack frame. If it is false (default), we annotate for a + normal frame. */ + +void print_funname (ui_out *uiout, + gdb::unique_xmalloc_ptr const &funname, + bool shadowstack_frame = false); + +/* 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_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 find_symbol_funname (const symbol *sym); + +/* The possible choices of "set print frame-info". */ +extern const char *const print_frame_info_choices[7]; + #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 0ae172d7c41..4563e49d9e4 100644 --- a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp @@ -109,6 +109,94 @@ 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 frame0 "#0\[ \t\]*$sspval_call2 in call1$fill" + set frame1 "#1\[ \t\]*$sspval_call1 in main$fill" + set frame2 "#2\[ \t\]*$sspval_main$fill" + + # We can 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" \ + [multi_line \ + "$frame0" \ + "$frame1" \ + "$frame2" \ + ".*" ] \ + "test shadow stack backtrace until the bottom of the stack." + + gdb_test "bt shadow 2" \ + [multi_line \ + "$frame0" \ + "$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 \ + "$frame0" \ + "$frame1" ] \ + "test shadow stack backtrace with limit" + gdb_test_no_output "set backtrace limit unlimited" "restore backtrace limit default" + + # Test annotations for bt shadow. + save_vars { ::gdb_prompt } { + + # Just like for other gdb tests testing annotations, note: + # When this prompt is in use the gdb_test procedure cannot be used + # because it assumes that the last char of the gdb_prompt is a + # white space. This is not true with this annotated prompt. + # One alternative is to use gdb_test_multiple. + set ::gdb_prompt "\r\n\032\032pre-prompt\r\n$::gdb_prompt \r\n\032\032prompt\r\n" + + gdb_test_multiple "set annotate 2" "set annotate 2" { + -re "set annotate 2\r\n$gdb_prompt$" { + pass "annotation set at level 2" + } + -re ".*$gdb_prompt$" { + fail "annotation set at level 2" + } + timeout { + fail "annotation set at level 2 (timeout)" + } + } + + set inner_frame_re [multi_line \ + "shadow-stack-frame-address" \ + "$hex\r\n\032\032shadow-stack-frame-address-end" \ + "\ in \r\n\032\032shadow-stack-frame-function-name" \ + "$fill\r\n\032\032shadow-stack-frame-source-begin" \ + " at \r\n\032\032shadow-stack-frame-source-file\r\n$fill" \ + "\032\032shadow-stack-frame-source-file-end" \ + ":\r\n\032\032shadow-stack-frame-source-line\r\n$decimal" \ + "\032\032shadow-stack-frame-source-end\r\n\r\n\r\n"] + + set re [multi_line \ + "shadow-stack-frame-begin 0 $hex" \ + "#0 \r\n\032\032$inner_frame_re\032\032shadow-stack-frame-end" \ + "\r\n\032\032shadow-stack-frame-begin 1 $hex" \ + "#1 \r\n\032\032$inner_frame_re\032\032shadow-stack-frame-end" \ + "\\(More shadow stack frames follow...\\)" \ + "$gdb_prompt$" ] + + gdb_test_multiple "backtrace shadow 2" "backtrace shadow with annotations" { + -re $re { + pass $gdb_test_name + } + } + } + gdb_test "set annotate 0" ".*post-prompt.*" "set annotate 0" + 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/help.exp b/gdb/testsuite/gdb.base/help.exp index ec41382a9af..8fbc6005232 100644 --- a/gdb/testsuite/gdb.base/help.exp +++ b/gdb/testsuite/gdb.base/help.exp @@ -168,7 +168,11 @@ gdb_test "apropos An alias of command backtrace with arg 10" \ "mybt10 -- An alias of command backtrace with arg 10\." \ "apropos after documenting aliases showing mybt10 doc" -gdb_test "help mybt" " alias mybt = backtrace \[\r\n\]+An alias of command backtrace without any args\." \ +set bt_alias_doc "alias mybt = backtrace \[\r\n\]+An alias of command backtrace without any args\." +set bt_shadow_doc "\[\r\n\]+List of \"backtrace\" subcommands:\[\r\n\]+backtrace shadow -- Print backtrace of all shadow stack frames, or innermost COUNT frames." +set bt_shadow_help_doc "\[\r\n\]+Type \"help backtrace\" followed by subcommand name for full documentation." + +gdb_test "help mybt" "$bt_alias_doc$bt_shadow_doc$bt_shadow_help_doc.*" \ "help mybt after documenting aliases showing mybt doc" # Check pre-defined aliases cannot be documented. diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp index 8ac9f4ee755..350bf6205b8 100644 --- a/gdb/testsuite/gdb.base/options.exp +++ b/gdb/testsuite/gdb.base/options.exp @@ -288,11 +288,16 @@ proc_with_prefix test-backtrace {} { clean_restart test_gdb_complete_unique "backtrace" "backtrace" - test_gdb_complete_none "backtrace " + test_gdb_complete_unique "backtrace shadow" "backtrace shadow" + test_gdb_complete_none "backtrace shadow " gdb_test "backtrace -" "Ambiguous option at: -" gdb_test "backtrace --" "No stack\\." gdb_test "backtrace -- -" "No stack\\." + gdb_test "backtrace shadow -" \ + "Requires an argument. Valid arguments are auto, source-line.*\\." + gdb_test "backtrace shadow --" "No shadow stack\\." + gdb_test "backtrace shadow -- -" "No shadow stack\\." test_gdb_complete_multiple "backtrace " "-" "" { "-entry-values" -- 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