From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id wZ/VLuuD0mimLA8AWB0awg (envelope-from ) for ; Tue, 23 Sep 2025 07:26:35 -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=L39CKwQT; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id BAE4D1E0BA; Tue, 23 Sep 2025 07:26:35 -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 6BBCE1E047 for ; Tue, 23 Sep 2025 07:26:34 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 16AC73858C2D for ; Tue, 23 Sep 2025 11:26:34 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 16AC73858C2D 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=L39CKwQT Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.16]) by sourceware.org (Postfix) with ESMTPS id D9C5D3858D26 for ; Tue, 23 Sep 2025 11:20:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D9C5D3858D26 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 D9C5D3858D26 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=1758626403; cv=none; b=Anb0WNftXhxUDDnYdQi1HqaxcU6eARuq4cipXu+dmBmNGn1dyehBQV9R8ZwCsumu1K7XF7vM53gPl5s0dLSR8R40KpO5kw8FNACg4dgBCsAofimpBOzme0PcMIH+DOf5xxUfCf04HNjX+MSt87DrjgU2s55fdTe0MoD8Lm9PD/E= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758626403; c=relaxed/simple; bh=Sy++6DkXf7V13Bf/LqOxg1J0VxeiuFwNbWm1UzmrLzw=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=sZuOT1H+xyxZS3UCW/KynGX6sxxEHyMjE1atdpvwvrcMw5zyAKdlXH33KaXh1sjSHg4ras7aBKJdY4xHTftxA2Jd3lZe+4pcA76bee2NxfAIYOBpezMnX4vE+9KiTEsRabVTtxbobJtH0qGkVjzQtjeQtHMgLJO44wqaqhdd6Ag= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D9C5D3858D26 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1758626403; x=1790162403; h=from:to:subject:date:message-id:in-reply-to:references: mime-version:content-transfer-encoding; bh=Sy++6DkXf7V13Bf/LqOxg1J0VxeiuFwNbWm1UzmrLzw=; b=L39CKwQToHKxK1BUNwesPK/4PNKonyzgPz6MdPJ2/iwm84WGx2bg8mxa lwyJfcnAPN/yVUVmNKYj48r+nSX9uj7UzHikPdBLGhNYeL7b541ubZynM rnYrziR42fgCgVx5ieItzvhEPpyPnVBhlX+bFyi/DR6IMnm22Wohs3d9J JgQ9DECx/6ZtYaJ3GIzqrCfQn+MqPDfnUyM8ayo1FuOG7sm+f49JDioIb MTxY8IPAw3d1AyC9wLLhT8QlAdKJBSo9R409JrjrGyarRr4JVjtQLLClp xp1pb9D255/TKnSL9KVpEe4y/XYp/Dph8kfkV8ZA+3wANwn1BS4BQG7WL g==; X-CSE-ConnectionGUID: 9Vh2ApfPQ6OCfcXWMTg+ig== X-CSE-MsgGUID: igoYv2P9Q8S0gvhmpnjBxA== X-IronPort-AV: E=McAfee;i="6800,10657,11561"; a="61071814" X-IronPort-AV: E=Sophos;i="6.18,287,1751266800"; d="scan'208";a="61071814" 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:20:03 -0700 X-CSE-ConnectionGUID: 5uppXn9tTt6ChB/lfe082A== X-CSE-MsgGUID: XisF5mIARs60H7/v0KQ0Vw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.18,287,1751266800"; d="scan'208";a="176335347" 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:20:01 -0700 From: Christina Schimpe To: gdb-patches@sourceware.org Subject: [PATCH 9/9] gdb, mi: Add -shadow-stack-list-frames command Date: Tue, 23 Sep 2025 11:18:42 +0000 Message-Id: <20250923111842.4091694-10-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 the mi command for the subcommand "backtrace shadow". Similar to the mi interface for the ordinary backtrace command, support low-frame and high-frame as command line parameters. Example print of a full shadow stack backtrace: ~~~ (gdb) -shadow-stack-list-frames ^done,shadow-stack=[ shadow-stack-frame={level="0",addr="0x00007ffff7c3fe70", func="__libc_start_call_main",file="../sysdeps/nptl/libc_start_call_main.h", fullname="/usr/[...]/sysdeps/nptl/libc_start_call_main.h", line="58",arch="i386:x86-64"}, shadow-stack-frame={level="1",addr="0x00007ffff7c3ff20", func="__libc_start_main_impl",file="../csu/libc-start.c", fullname="/usr/[...]/csu/libc-start.c", line="128",arch="i386:x86-64"}, shadow-stack-frame={level="2",addr="0x0000000000401075", func="_start",arch="i386:x86-64"}] ~~~ Example print of a shadow stack backtrace using low- and high-frame: ~~~ (gdb) -shadow-stack-list-frames 0 1 ^done,shadow-stack=[ shadow-stack-frame={level="0",addr="0x00007ffff7c3fe70", func="__libc_start_call_main",file="../sysdeps/nptl/libc_start_call_main.h", fullname="/usr/[...]/sysdeps/nptl/libc_start_call_main.h", line="58",arch="i386:x86-64"}, shadow-stack-frame={level="1",addr="0x00007ffff7c3ff20", func="__libc_start_main_impl",file="../csu/libc-start.c", fullname="/usr/[...]/csu/libc-start.c", line="128",arch="i386:x86-64"}] ~~~ --- gdb/NEWS | 10 ++ gdb/doc/gdb.texinfo | 47 ++++++ gdb/mi/mi-cmd-stack.c | 146 ++++++++++++++++++ gdb/mi/mi-cmds.c | 2 + gdb/mi/mi-cmds.h | 1 + gdb/shadow-stack.c | 17 +- gdb/shadow-stack.h | 11 ++ .../gdb.mi/mi-shadow-stack-signal.exp | 69 +++++++++ gdb/testsuite/gdb.mi/mi-shadow-stack.exp | 65 ++++++++ 9 files changed, 365 insertions(+), 3 deletions(-) create mode 100644 gdb/testsuite/gdb.mi/mi-shadow-stack-signal.exp create mode 100644 gdb/testsuite/gdb.mi/mi-shadow-stack.exp diff --git a/gdb/NEWS b/gdb/NEWS index eb96ff2ae08..3c52f88b71d 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -40,6 +40,16 @@ 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. +* New MI commands + +-shadow-stack-list-frames + + Added new MI command '-shadow-stack-list-frames' which is equivalent to + the CLI subcommand 'backtrace shadow' but supports 'low-frame' and + 'high-frame' as command line parameters. The parameters are used to + print shadow stack frames between certain levels on the shadow stack + only. + *** Changes in GDB 17 * Debugging Linux programs that use x86-64 or x86-64 with 32-bit pointer diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index a0fde385a8e..aa8a33902b5 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -35308,6 +35308,53 @@ Show a single frame: (gdb) @end smallexample +@anchor{-shadow-stack-list-frames} +@findex -shadow-stack-list-frames +@subheading The @code{-shadow-stack-list-frames} Command + +@subsubheading Synopsis + +@smallexample + -shadow-stack-list-frames [ @var{low-frame} @var{high-frame} ] +@end smallexample + +List the shadow stack frames currently on the shadow stack. In case the +element on the shadow stack is a return address, @value{GDBN} prints the +same fields as in -stack-list-frames with the return address on the shadow +stack as @var{addr}. +If the element on the shadow stack is not a return address, @value{GDBN} +only prints @var{level}, @var{addr} and @var{arch}. + +If invoked without arguments, this command prints a backtrace for the +whole shadow stack. Like the -stack-list-frames command, if given two +integer arguments, it shows the frames whose levels are between +the two arguments (inclusive). + +@subsubheading @value{GDBN} Command + +The corresponding @value{GDBN} command is @samp{backtrace shadow}. + +@subsubheading Example + +Show shadow stack frames between @var{low-frame} and @var{high-frame}: + +@smallexample +(gdb) +-shadow-stack-list-frames 0 2 +^done,shadow-stack=[ +shadow-stack-frame=@{level="0",addr="0x00007ffff7c54d90", + func="__restore_rt",from="/lib64/libc.so.6",arch="i386:x86-64"@}, +shadow-stack-frame=@{level="1",addr="0x80007ffff79fffd8",arch="i386:x86-64"@}, +shadow-stack-frame=@{level="2",addr="0x00007ffff7c54ce6", + func="__GI_raise",file="../sysdeps/posix/raise.c", + fullname="/usr/src/debug/glibc-2.34-90.bkc.el9.x86_64/sysdeps/posix/raise.c", + line="27",arch="i386:x86-64"@}] +(gdb) +@end smallexample + +For frame 1 we can see that we only print @var{level}, @var{addr} and +@var{arch}. This is due to an element on the shadow stack which is not a +return address. @findex -stack-list-locals @anchor{-stack-list-locals} diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c index 52ae11b39af..08f43f023f6 100644 --- a/gdb/mi/mi-cmd-stack.c +++ b/gdb/mi/mi-cmd-stack.c @@ -32,6 +32,9 @@ #include "mi-parse.h" #include #include "inferior.h" +#include "gdbarch.h" +#include "gdbcore.h" +#include "arch-utils.h" enum what_to_list { locals, arguments, all }; @@ -773,3 +776,146 @@ mi_cmd_stack_info_frame (const char *command, const char *const *argv, print_frame_info (user_frame_print_options, get_selected_frame (NULL), 1, LOC_AND_ADDRESS, 0, 1); } + +/* Parse arguments of -shadow-stack-list-frames command and set FRAME_LOW + and FRAME_HIGH accordingly. Throw an error in case the arguments are + invalid. */ +static void +mi_cmd_shadow_stack_list_frames_parse_args (const char *const *argv, + int argc, int &frame_low, + int &frame_high) +{ + /* There should either be low - high range, or no arguments. */ + if ((argc != 0) && (argc != 2)) + error (_("-shadow-stack-list-frames: Usage: [FRAME_LOW FRAME_HIGH]")); + + /* If there is a range, set it. */ + if (argc == 2) + { + frame_low = atoi (argv[0]); + frame_high = atoi (argv[1]); + std::string err_str; + if (frame_low < 0) + { + err_str = "``" + std::to_string (frame_low) + "''"; + if (frame_high < 0) + err_str += " and ``" + std::to_string (frame_high) + "''"; + } + else if (frame_high < 0) + err_str = "``" + std::to_string (frame_high) + "''"; + + if (!err_str.empty ()) + { + err_str = "-shadow-stack-list-frames: Invalid option " + err_str; + error (_("%s."), err_str.c_str ()); + } + } + else + { + /* No arguments, print the whole shadow stack backtrace. */ + frame_low = -1; + frame_high = -1; + } +} + +/* Print a list of the shadow stack frames. Args can be none, in which + case we want to print the whole shadow stack backtrace, or a pair of + numbers specifying the frame numbers at which to start and stop the + display. If the two numbers are equal, a single frame will be + displayed. */ + +void +mi_cmd_shadow_stack_list_frames (const char *command, + const char *const *argv, + int argc) +{ + int frame_low; + int frame_high; + + mi_cmd_shadow_stack_list_frames_parse_args (argv, argc, frame_low, + frame_high); + + if (!target_has_stack ()) + error (_("-shadow-stack-list-frames: No shadow stack.")); + + gdbarch *gdbarch = get_current_arch (); + if (!gdbarch_address_in_shadow_stack_memory_range_p (gdbarch)) + error (_("-shadow-stack-list-frames: Printing of 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-list-frames: Shadow stack is not enabled for \ + the current target.")); + + ui_out_emit_list list_emitter (current_uiout, "shadow-stack"); + + /* 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 START_SSP points off the shadow stack memory range, we cannot + print the shadow stack backtrace. This is possible, for + instance, on x86 if NEW_SSP points to the end of RANGE which + means that the shadow stack is empty. */ + return; + } + + /* For ARM's Guarded Control Stack, if START_SSP points one element + before the end of RANGE, it means that the shadow stack pointer + is valid but the shadow stack is empty. */ + if (gdbarch_top_addr_empty_shadow_stack_p (gdbarch) + && gdbarch_top_addr_empty_shadow_stack (gdbarch, *start_ssp, range)) + return; + + std::optional curr; + CORE_ADDR new_value; + const int addr_size_byte = gdbarch_addr_bit (gdbarch) / 8; + const bfd_endian byte_order = gdbarch_byte_order (gdbarch); + if (!safe_read_memory_unsigned_integer (*start_ssp, addr_size_byte, + byte_order, &new_value)) + error (_("-shadow-stack-list-frames: Cannot read shadow stack memory.")); + + curr = {*start_ssp, new_value, 0, ssp_unwind_stop_reason::no_error}; + + /* Let's position curr on the shadow stack frame at which to start the + display. This could be the innermost frame if the whole shadow stack + needs displaying, or if frame_low is 0. */ + int frame_num = 0; + for (; curr.has_value () && frame_num < frame_low; frame_num++) + curr = curr->unwind_prev_shadow_stack_frame_info (gdbarch, range); + + if (!curr.has_value ()) + error (_("-shadow-stack-list-frames: Not enough frames on the shadow \ + stack.")); + + shadow_stack_print_options print_options; + print_options.print_frame_info + = user_frame_print_options.print_frame_info; + + /* Now let's print the shadow stack frames up to frame_high, or until + the bottom of the shadow stack. */ + for (; curr.has_value () && (frame_num <= frame_high || frame_high == -1); + frame_num++) + { + QUIT; + print_shadow_stack_frame_info (gdbarch, print_options, *curr, + LOCATION); + curr = curr->unwind_prev_shadow_stack_frame_info (gdbarch, range); + } +} diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c index abd3fb9b53d..8125891a9cc 100644 --- a/gdb/mi/mi-cmds.c +++ b/gdb/mi/mi-cmds.c @@ -303,6 +303,8 @@ add_builtin_mi_commands () add_mi_cmd_mi ("stack-info-frame", mi_cmd_stack_info_frame); add_mi_cmd_mi ("stack-list-arguments", mi_cmd_stack_list_args); add_mi_cmd_mi ("stack-list-frames", mi_cmd_stack_list_frames); + add_mi_cmd_mi ("shadow-stack-list-frames", + mi_cmd_shadow_stack_list_frames); add_mi_cmd_mi ("stack-list-locals", mi_cmd_stack_list_locals); add_mi_cmd_mi ("stack-list-variables", mi_cmd_stack_list_variables); add_mi_cmd_mi ("stack-select-frame", mi_cmd_stack_select_frame, diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h index 990ff75485c..e3a6f3e8be2 100644 --- a/gdb/mi/mi-cmds.h +++ b/gdb/mi/mi-cmds.h @@ -100,6 +100,7 @@ extern mi_cmd_argv_ftype mi_cmd_stack_list_frames; extern mi_cmd_argv_ftype mi_cmd_stack_list_locals; extern mi_cmd_argv_ftype mi_cmd_stack_list_variables; extern mi_cmd_argv_ftype mi_cmd_stack_select_frame; +extern mi_cmd_argv_ftype mi_cmd_shadow_stack_list_frames; extern mi_cmd_argv_ftype mi_cmd_symbol_list_lines; extern mi_cmd_argv_ftype mi_cmd_symbol_info_functions; extern mi_cmd_argv_ftype mi_cmd_symbol_info_module_functions; diff --git a/gdb/shadow-stack.c b/gdb/shadow-stack.c index 42032f51781..ef8e31c730e 100644 --- a/gdb/shadow-stack.c +++ b/gdb/shadow-stack.c @@ -270,6 +270,12 @@ do_print_shadow_stack_frame_info uiout->field_string ("addr", hex_string_custom (frame.value, element_size * 2), address_style.style ()); + + if (uiout->is_mi_like_p ()) + { + uiout->field_string + ("arch", (gdbarch_bfd_arch_info (gdbarch))->printable_name); + } uiout->text ("\n"); gdb_flush (gdb_stdout); return; @@ -333,6 +339,12 @@ do_print_shadow_stack_frame_info if (lib != nullptr) print_lib (uiout, lib, true); } + + if (uiout->is_mi_like_p ()) + { + uiout->field_string + ("arch", (gdbarch_bfd_arch_info (gdbarch))->printable_name); + } } /* Extra scope to print frame tuple. */ uiout->text ("\n"); @@ -355,10 +367,9 @@ do_print_shadow_stack_frame_info gdb_flush (gdb_stdout); } -/* Redirect output to a temporary buffer for the duration of - do_print_shadow_stack_frame_info. */ +/* See shadow-stack.h. */ -static void +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) diff --git a/gdb/shadow-stack.h b/gdb/shadow-stack.h index f801d077820..1b0413df26d 100644 --- a/gdb/shadow-stack.h +++ b/gdb/shadow-stack.h @@ -92,4 +92,15 @@ class shadow_stack_frame_info = ssp_unwind_stop_reason::no_error; }; +/* 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. + Redirect output to a temporary buffer for the duration of + do_print_shadow_stack_frame_info. */ + +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); + #endif /* GDB_SHADOW_STACK_H */ diff --git a/gdb/testsuite/gdb.mi/mi-shadow-stack-signal.exp b/gdb/testsuite/gdb.mi/mi-shadow-stack-signal.exp new file mode 100644 index 00000000000..219f1b8ad78 --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-shadow-stack-signal.exp @@ -0,0 +1,69 @@ +# Copyright 2024-2025 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Test the mi command -shadow-stack-list-frames for signal handling on linux. + +load_lib mi-support.exp +set MIFLAGS "-i=mi" + +require allow_ssp_tests {istarget "*-*-linux*"} + +save_vars { ::env(GLIBC_TUNABLES) } { + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" + + set srcfile "${srcdir}/gdb.arch/amd64-shadow-stack-signal.c" + set testfile mi-shadow-stack + + # Test shadow-stack-list-frames for shadow stack element which is no + # return address. + if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + {debug additional_flags="-fcf-protection=return"}] } { + return + } + + if { [mi_clean_restart $testfile] } { + return + } + + mi_runto_main + mi_send_resuming_command "exec-continue" "continue till signal" + + set r_signal "reason=\"signal-received\",signal-name=\"SIGUSR1\",signal-meaning=\"User defined signal 1\"" + gdb_expect { + -re ".*stopped,${r_signal}.*$mi_gdb_prompt" { + pass "Wait for user interrupt" + } + timeout { + fail "Wait for user interrupt (timeout)" + return + } + } + + mi_gdb_test "break handler" \ + {(&.*)*.*~"Breakpoint 2 at.*\\n".*=breakpoint-created,bkpt=\{number="2",type="breakpoint".*\}.*\n\^done} + + mi_execute_to "exec-continue" "breakpoint-hit" "handler" ".*" ".*" ".*" \ + {"" "disp=\"keep\""} "continue to handler" + + # We only test the frame belonging to the shadow stack element which + # is not a return address. This frame is trigged by the signal + # exception. + set any "\[^\"\]+" + mi_gdb_test "231-shadow-stack-list-frames 1 1" \ + "231\\^done,shadow-stack=\\\[shadow-stack-frame=\{level=\"1\",addr=\"$hex\"\,arch=\"$any\"\}\\\]" \ + "test shadow-stack-list-frames" + + mi_gdb_exit +} diff --git a/gdb/testsuite/gdb.mi/mi-shadow-stack.exp b/gdb/testsuite/gdb.mi/mi-shadow-stack.exp new file mode 100644 index 00000000000..bbbc7ec5460 --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-shadow-stack.exp @@ -0,0 +1,65 @@ +# Copyright 2024-2025 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Test the mi command -shadow-stack-list-frames. + +load_lib mi-support.exp +set MIFLAGS "-i=mi" + +require allow_ssp_tests + +save_vars { ::env(GLIBC_TUNABLES) } { + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" + + set srcfile "${srcdir}/gdb.arch/amd64-shadow-stack.c" + set testfile mi-shadow-stack + + if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + {debug additional_flags="-fcf-protection=return"}] } { + restart_and_run_infcall_call2 + } + + if { [mi_clean_restart $testfile] } { + return + } + + mi_runto_main + + mi_gdb_test "break call2" \ + {(&.*)*.*~"Breakpoint 2 at.*\\n".*=breakpoint-created,bkpt=\{number="2",type="breakpoint".*\}.*\n\^done} + + mi_execute_to "exec-continue" "breakpoint-hit" "call2" ".*" ".*" ".*" \ + {"" "disp=\"keep\""} "continue to call2" + + set any "\[^\"\]+" + set any_remaining_frame_attr "\[^\r\n]+" + + # It's enough to test the first 3 frames. For frame 3 we just test that it + # exists as other attributes might depend on the environment. + set frame_start "shadow-stack-frame=\{level=" + set frame1 "$frame_start\"0\",addr=\"$hex\",func=\"call1\",file=\"$any\",fullname=\"$any\",line=\"$decimal\",arch=\"$any\"\}" + set frame2 "$frame_start\"1\",addr=\"$hex\",func=\"main\",file=\"$any\",fullname=\"$any\",line=\"$decimal\",arch=\"$any\"\}" + set frame3 "$frame_start\"2\",addr=\"$hex\"$any_remaining_frame_attr\}" + mi_gdb_test "231-shadow-stack-list-frames" \ + "231\\^done,shadow-stack=\\\[$frame1.*$frame2.*$frame3.*" \ + "test shadow-stack-list-frames" + + # Test low-frame/high-frame + mi_gdb_test "231-shadow-stack-list-frames 1 2" \ + "231\\^done,shadow-stack=\\\[$frame2.*$frame3\\\]" \ + "test shadow-stack-list-frames low/high-frames" + + mi_gdb_exit +} -- 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