From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id 2tD/FeWMm2dqbx8AWB0awg (envelope-from ) for ; Thu, 30 Jan 2025 09:29:57 -0500 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=LY6I8I3b; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 401141E105; Thu, 30 Jan 2025 09:29:57 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-7.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_MSPIKE_H2 autolearn=ham autolearn_force=no version=4.0.0 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 E404A1E08E for ; Thu, 30 Jan 2025 09:29:55 -0500 (EST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A80413857BB3 for ; Thu, 30 Jan 2025 14:29:55 +0000 (GMT) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 05CF63858C53 for ; Thu, 30 Jan 2025 14:29:22 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 05CF63858C53 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 05CF63858C53 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1738247363; cv=none; b=SXkdRSd0uRPesWDCur8I51Qt7dx2RSODEY1sCfdvonQhcmPVIvpCioKtDBQhcCpmV4nMEsVnjE4ylo/CAo+RL+K2+4T55Qe7tj5DZxpfTuuA+SEnfI1fF5AoYEjaywnYmux7UaHr6zt6OY4uacZOvhD0sxDgT5JSwyKEJQgOfIY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1738247363; c=relaxed/simple; bh=FYE7tRduw2RxyHprQjbv3spEh+G2F1twk5dBTkco1WI=; h=DKIM-Signature:Message-ID:Date:MIME-Version:Subject:To:From; b=iSSvSQ6bMqAu6DDYeLEGwQv50znW6NtyxMtfkCFfw9b/E110aLHa1WsHD8lkASekrefrwzi/YN1UJ+wEjLDADafRITnRWHdxL/Ngus6ouu1sU3U9Hh3lnTWP9HKHKzH3ASSilzFWvt7yhQscs/MmebqjNDxqEiHabE4a/B3HAdk= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 05CF63858C53 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=LY6I8I3b DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1738247361; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ki+5Lv3VXibUckdK+i1NpLEJog2uAKQaxeTnGNStHCA=; b=LY6I8I3b9J4RqKLzNYSSueuj2ggCQ/94U0e+A3y3bFTj6NCuqO7sOlTfH5AhuYU23n+mZy GbRefP1+x+heSwTI9nMqOHZrRFwpj/tBZhbMYvaxkWUoBTrETTDBwV34AbBaIILzJY3rpL ummY35RKylrSyK0Hn4271mb6k23xayI= Received: from mail-qk1-f199.google.com (mail-qk1-f199.google.com [209.85.222.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-608--_8RkR3FNM2Zj3EggvUWlw-1; Thu, 30 Jan 2025 09:29:20 -0500 X-MC-Unique: -_8RkR3FNM2Zj3EggvUWlw-1 X-Mimecast-MFC-AGG-ID: -_8RkR3FNM2Zj3EggvUWlw Received: by mail-qk1-f199.google.com with SMTP id af79cd13be357-7b6c51069f5so150995885a.3 for ; Thu, 30 Jan 2025 06:29:20 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1738247360; x=1738852160; h=content-transfer-encoding:in-reply-to:from:content-language :references:to:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=ki+5Lv3VXibUckdK+i1NpLEJog2uAKQaxeTnGNStHCA=; b=QJJ1eaOshM3cAT0d07v5TCrHlqu0GvmUpOq8OPkW2iwkmkbKOphlLdfnGyG8qsOZ39 +RDBOygvrEw2WKJMrhYPJGJmF19eUcCte4LHb+tVE62pnkEx5+0Cr+/ZzhSdVRWJcZ8o Mc0rMW2s1k3L5EOsbpeJF5F2XnSZFIeF3ElxtEvlC7n0kipcTa5HHvBbGvRTZ7OT4xCY fEHFpSf+ky2btj/s1v9vEIBnKBsoUlOIcqcxmtpIiGS9Druf8LHfSff7+0+/qkMeC9Pk bx2ZuLuuPGzGyPhSX4puEjjda3xg5/IhofbetVcTKzKDBjIvkZDGsnxYJS0r6FSCH4lp Lj0g== X-Forwarded-Encrypted: i=1; AJvYcCWtpehVUX95XkA3GhqJDt4F6q7GdcOzmZeV/9UwdKla1Ynv1UnSVZduK5/InG3ecf/1W7OZqwcxxD1xLA==@sourceware.org X-Gm-Message-State: AOJu0YyVT1EOmIVtSD3gP2mmCPaIvuAkxEsxbeWMhHE8cdiFtJWo8arA rfLWsxkuoBJbCdU09lRKDRlWbY7lThvSmxuwqF8joYBL8htn9KZQWzLu68iZ1jvsFE5rRNn9T4g DiM/yIi48MtoFJoiwd7XRzeVHfaSjEZiupVbybXFVREjb8BwJ8IvaD3W4MX8= X-Gm-Gg: ASbGnctSYsTUXDHkNEe/xRlFYfcDwM0ju0s1Uygu+Qv4hhSkGj347JqV0Q79V5TsnJc m0jAeCoJaN+i2gCtLz/SkdHyqU208gxCrU2XqHVQevzQ3nUGP42q+PA4jqYg8d4sUvUls1c1cnl ltkvCRsNvUV68Ex7dfQGADy4MxFR/6IFVONRK3at2plO7qewDS9hQ+7do3tbqhyDDj49rl4Y5Sk xU1LO4KqxGR7a3dOif1TBiz58OlGd8OCOCAxjTbOWgtK7xCWSEAGuITbwlihtw+rZrWYSTvL5yI qpTfhT2cpUOOsNjo X-Received: by 2002:a05:620a:4101:b0:7b6:eed4:695c with SMTP id af79cd13be357-7bffcd43d99mr1134313185a.32.1738247360026; Thu, 30 Jan 2025 06:29:20 -0800 (PST) X-Google-Smtp-Source: AGHT+IEvlzqBQebbR+p1DBkHs9ZaXqVWtTqq+UfOZjjneY1ebJbVUc5RIo/08G9RcnGPwPNq/iPT0A== X-Received: by 2002:a05:620a:4101:b0:7b6:eed4:695c with SMTP id af79cd13be357-7bffcd43d99mr1134308585a.32.1738247359515; Thu, 30 Jan 2025 06:29:19 -0800 (PST) Received: from ?IPV6:2804:14d:8084:9a69::1002? ([2804:14d:8084:9a69::1002]) by smtp.gmail.com with ESMTPSA id af79cd13be357-7c00a90d0casm78267485a.101.2025.01.30.06.29.18 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 30 Jan 2025 06:29:19 -0800 (PST) Message-ID: <4f873c4a-a4ba-451f-8908-bc40ab93c43a@redhat.com> Date: Thu, 30 Jan 2025 11:29:17 -0300 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH 08/12] gdb: Handle shadow stack pointer register unwinding for amd64 linux. To: "Schimpe, Christina" , gdb-patches@sourceware.org References: <20241220200501.324191-1-christina.schimpe@intel.com> <20241220200501.324191-9-christina.schimpe@intel.com> From: Guinevere Larsen In-Reply-To: <20241220200501.324191-9-christina.schimpe@intel.com> X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: KYZNDmoGnUnQmAPjLvLTYD2tRGrd9389tbRIXZ854qY_1738247360 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed 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 On 12/20/24 5:04 PM, Schimpe, Christina wrote: > Unwind the $pl3_ssp register. > We now have an updated value for the shadow stack pointer when > moving up or down the frame level. Note that $pl3_ssp can > become unavailable when moving to a frame before the shadow > stack enablement. In the example below, shadow stack is enabled > in the function 'call1'. Thus, when moving to a frame level above > the function, $pl3_ssp will become unavaiable. > Following the restriction of the linux kernel, implement the unwinding > for amd64 linux only. > > Before this patch: > ~~~ > Breakpoint 1, call2 (j=3) at sample.c:44 > 44 return 42; > (gdb) p $pl3_ssp > $1 = (void *) 0x7ffff79ffff8 > (gdb) up > 55 call2 (3); > (gdb) p $pl3_ssp > $2 = (void *) 0x7ffff79ffff8 > (gdb) up > 68 call1 (43); > (gdb) p $pl3_ssp > $3 = (void *) 0x7ffff79ffff8 > ~~~ > > After this patch: > ~~~ > Breakpoint 1, call2 (j=3) at sample.c:44 > 44 return 42; > (gdb) p $pl3_ssp > $1 = (void *) 0x7ffff79ffff8 > (gdb) up > 55 call2 (3); > (gdb) p $pl3_ssp > $2 = (void *) 0x7ffff7a00000 > (gdb) up > 68 call1 (43i); > (gdb) p $pl3_ssp > $3 = > ~~~ > > As we now have an updated value for each selected frame, the > return command is now enabled for shadow stack enabled programs, too. > > We therefore add a test for the return command and shadow stack support, > and for an updated shadow stack pointer after a frame level change. > --- > gdb/amd64-linux-tdep.c | 69 +++++++++++++++ > gdb/linux-tdep.c | 47 ++++++++++ > gdb/linux-tdep.h | 7 ++ > .../gdb.arch/amd64-shadow-stack-cmds.exp | 88 +++++++++++++++++++ > gdb/testsuite/gdb.arch/amd64-shadow-stack.c | 13 +++ > 5 files changed, 224 insertions(+) > create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp > > diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c > index 95f643b1217..895feac85e8 100644 > --- a/gdb/amd64-linux-tdep.c > +++ b/gdb/amd64-linux-tdep.c > @@ -45,6 +45,8 @@ > #include "arch/amd64-linux-tdesc.h" > #include "inferior.h" > #include "x86-tdep.h" > +#include "dwarf2/frame.h" > +#include "frame-unwind.h" > > /* The syscall's XML filename for i386. */ > #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml" > @@ -1873,6 +1875,72 @@ amd64_linux_remove_non_address_bits_watchpoint (gdbarch *gdbarch, > return (addr & amd64_linux_lam_untag_mask ()); > } > > +static value * > +amd64_linux_dwarf2_prev_ssp (const frame_info_ptr &this_frame, > + void **this_cache, int regnum) > +{ > + value *v = frame_unwind_got_register (this_frame, regnum, regnum); > + gdb_assert (v != nullptr); > + > + gdbarch *gdbarch = get_frame_arch (this_frame); > + > + if (v->entirely_available () && !v->optimized_out ()) > + { > + int size = register_size (gdbarch, regnum); > + bfd_endian byte_order = gdbarch_byte_order (gdbarch); > + CORE_ADDR ssp = extract_unsigned_integer (v->contents_all ().data (), > + size, byte_order); > + > + /* Starting with v6.6., the Linux kernel supports CET shadow stack. > + Using /proc/PID/smaps we can only check if the current shadow > + stack pointer SSP points to shadow stack memory. Only if this is > + the case a valid previous shadow stack pointer can be > + calculated. */ > + std::pair range; > + if (linux_address_in_shadow_stack_mem_range (ssp, &range)) > + { > + /* The shadow stack grows downwards. To compute the previous > + shadow stack pointer, we need to increment SSP. > + For x32 the shadow stack elements are still 64-bit aligned. > + Thus, we cannot use gdbarch_addr_bit to compute the new stack > + pointer. */ > + const bfd_arch_info *binfo = gdbarch_bfd_arch_info (gdbarch); > + const int bytes_per_word > + = (binfo->bits_per_word / binfo->bits_per_byte); In patch 10 of this series, you introduce amd64_linux_shadow_stack_element_size_aligned to simplify this calculation. is there any reason you didn't introduce it here? -- Cheers, Guinevere Larsen She/Her/Hers > + CORE_ADDR new_ssp = ssp + bytes_per_word; > + > + /* If NEW_SSP points to the end of or before (<=) the current > + shadow stack memory range we consider NEW_SSP as valid (but > + empty). */ > + if (new_ssp <= range.second) > + return frame_unwind_got_address (this_frame, regnum, new_ssp); > + } > + } > + > + /* Return a value which is marked as unavailable in case we could not > + calculate a valid previous shadow stack pointer. */ > + value *retval > + = value::allocate_register (get_next_frame_sentinel_okay (this_frame), > + regnum, register_type (gdbarch, regnum)); > + retval->mark_bytes_unavailable (0, retval->type ()->length ()); > + return retval; > +} > + > +static void > +amd64_init_reg (gdbarch *gdbarch, int regnum, dwarf2_frame_state_reg *reg, > + const frame_info_ptr &this_frame) > +{ > + if (regnum == gdbarch_pc_regnum (gdbarch)) > + reg->how = DWARF2_FRAME_REG_RA; > + else if (regnum == gdbarch_sp_regnum (gdbarch)) > + reg->how = DWARF2_FRAME_REG_CFA; > + else if (regnum == AMD64_PL3_SSP_REGNUM) > + { > + reg->how = DWARF2_FRAME_REG_FN; > + reg->loc.fn = amd64_linux_dwarf2_prev_ssp; > + } > +} > + > static void > amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch, > int num_disp_step_buffers) > @@ -1927,6 +1995,7 @@ amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch, > > set_gdbarch_remove_non_address_bits_watchpoint > (gdbarch, amd64_linux_remove_non_address_bits_watchpoint); > + dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg); > } > > static void > diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c > index d3452059ce2..3a20b12c3d7 100644 > --- a/gdb/linux-tdep.c > +++ b/gdb/linux-tdep.c > @@ -47,6 +47,7 @@ > > #include > #include > +#include > > /* This enum represents the values that the user can choose when > informing the Linux kernel about which memory mappings will be > @@ -96,6 +97,10 @@ struct smaps_vmflags > /* Memory map has memory tagging enabled. */ > > unsigned int memory_tagging : 1; > + > + /* Memory map used for shadow stack. */ > + > + unsigned int shadow_stack_memory : 1; > }; > > /* Data structure that holds the information contained in the > @@ -537,6 +542,8 @@ decode_vmflags (char *p, struct smaps_vmflags *v) > v->shared_mapping = 1; > else if (strcmp (s, "mt") == 0) > v->memory_tagging = 1; > + else if (strcmp (s, "ss") == 0) > + v->shadow_stack_memory = 1; > } > } > > @@ -2744,6 +2751,46 @@ show_dump_excluded_mappings (struct ui_file *file, int from_tty, > " flag is %s.\n"), value); > } > > +/* See linux-tdep.h. */ > + > +bool > +linux_address_in_shadow_stack_mem_range > + (CORE_ADDR addr, std::pair *range) > +{ > + if (!target_has_execution () || current_inferior ()->fake_pid_p) > + return false; > + > + const int pid = current_inferior ()->pid; > + > + std::string smaps_file = string_printf ("/proc/%d/smaps", pid); > + > + gdb::unique_xmalloc_ptr data > + = target_fileio_read_stralloc (nullptr, smaps_file.c_str ()); > + > + if (data == nullptr) > + return false; > + > + const std::vector smaps > + = parse_smaps_data (data.get (), std::move (smaps_file)); > + > + auto find_addr_mem_range = [&addr] (const smaps_data &map) > + { > + bool addr_in_mem_range > + = (addr >= map.start_address && addr < map.end_address); > + return (addr_in_mem_range && map.vmflags.shadow_stack_memory); > + }; > + auto it = std::find_if (smaps.begin (), smaps.end (), find_addr_mem_range); > + > + if (it != smaps.end ()) > + { > + range->first = it->start_address; > + range->second = it->end_address; > + return true; > + } > + > + return false; > +} > + > /* To be called from the various GDB_OSABI_LINUX handlers for the > various GNU/Linux architectures and machine types. > > diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h > index bf4220bf75b..97dcc75a79c 100644 > --- a/gdb/linux-tdep.h > +++ b/gdb/linux-tdep.h > @@ -117,4 +117,11 @@ extern CORE_ADDR linux_get_hwcap2 (); > extern struct link_map_offsets *linux_ilp32_fetch_link_map_offsets (); > extern struct link_map_offsets *linux_lp64_fetch_link_map_offsets (); > > +/* Returns true if ADDR belongs to a shadow stack memory range. If this > + is the case, assign the shadow stack memory range to RANGE > + [start_address, end_address). */ > + > +extern bool linux_address_in_shadow_stack_mem_range > + (CORE_ADDR addr, std::pair *range); > + > #endif /* GDB_LINUX_TDEP_H */ > diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp > new file mode 100644 > index 00000000000..17f32ce3964 > --- /dev/null > +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp > @@ -0,0 +1,88 @@ > +# Copyright 2018-2024 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 shadow stack enabling for frame level update and the return command. > + > +require allow_ssp_tests > + > +standard_testfile amd64-shadow-stack.c > + > +save_vars { ::env(GLIBC_TUNABLES) } { > + > + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" > + > + if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ > + {debug additional_flags="-fcf-protection=return"}] } { > + return -1 > + } > + > + clean_restart ${binfile} > + if { ![runto_main] } { > + return -1 > + } > + > + set call1_line [ gdb_get_line_number "break call1" ] > + set call2_line [ gdb_get_line_number "break call2" ] > + > + # Extract shadow stack pointer inside main, call1 and call2 function. > + gdb_breakpoint $call1_line > + gdb_breakpoint $call2_line > + set ssp_main [get_valueof /x "\$pl3_ssp" 0 "get value of ssp in main"] > + gdb_continue_to_breakpoint "break call1" ".*break call1.*" > + set ssp_call1 [get_valueof /x "\$pl3_ssp" 0 "get value of ssp in call1"] > + gdb_continue_to_breakpoint "break call2" ".*break call2.*" > + set ssp_call2 [get_valueof /x "\$pl3_ssp" 0 "get value of ssp in call2"] > + > + with_test_prefix "test frame level update" { > + gdb_test "up" "call1.*" "move to frame 1" > + gdb_test "print /x \$pl3_ssp" "= $ssp_call1" "check pl3_ssp of frame 1" > + gdb_test "up" "main.*" "move to frame 2" > + gdb_test "print /x \$pl3_ssp" "= $ssp_main" "check pl3_ssp of frame 2" > + gdb_test "frame 0" "call2.*" "move to frame 0" > + gdb_test "print /x \$pl3_ssp" "= $ssp_call2" "check pl3_ssp of frame 0" > + } > + > + with_test_prefix "test return from current frame" { > + gdb_test "return (int) 1" "#0.*call1.*" \ > + "Test shadow stack return from current frame" \ > + "Make.*return now\\? \\(y or n\\) " "y" > + > + # Potential CET violations often only occur after resuming normal execution. > + # Therefore, it is important to test normal program continuation after > + # testing the return command. > + gdb_continue_to_end > + } > + > + clean_restart ${binfile} > + if { ![runto_main] } { > + return -1 > + } > + > + with_test_prefix "test return from past frame" { > + gdb_breakpoint $call2_line > + gdb_continue_to_breakpoint "break call2" ".*break call2.*" > + > + gdb_test "frame 1" ".*in call1.*" > + > + gdb_test "return (int) 1" "#0.*main.*" \ > + "Test shadow stack return from past frame" \ > + "Make.*return now\\? \\(y or n\\) " "y" > + > + # Potential CET violations often only occur after resuming normal execution. > + # Therefore, it is important to test normal program continuation after > + # testing the return command. > + gdb_continue_to_end > + } > +} > diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack.c b/gdb/testsuite/gdb.arch/amd64-shadow-stack.c > index 643ef2d5f56..80389730bc2 100644 > --- a/gdb/testsuite/gdb.arch/amd64-shadow-stack.c > +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack.c > @@ -15,8 +15,21 @@ > You should have received a copy of the GNU General Public License > along with this program. If not, see . */ > > +static int > +call2 () > +{ > + return 42; /* break call2. */ > +} > + > +static int > +call1 () > +{ > + return call2 (); /* break call1. */ > +} > + > int > main () > { > + call1 (); /* break main. */ > return 0; > }