From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id wZLsLh8sc2neDhEAWB0awg (envelope-from ) for ; Fri, 23 Jan 2026 03:06:55 -0500 Authentication-Results: simark.ca; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=flk9oQZ6; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id BC9121E0AD; Fri, 23 Jan 2026 03:06:55 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_INVALID,DKIM_SIGNED,HTML_MESSAGE,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 6CD041E08D for ; Fri, 23 Jan 2026 03:06:53 -0500 (EST) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id D066E4BA901E for ; Fri, 23 Jan 2026 08:06:52 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D066E4BA901E Authentication-Results: sourceware.org; dkim=fail reason="signature verification failed" (2048-bit key, unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=flk9oQZ6 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) by sourceware.org (Postfix) with ESMTPS id 7C8034BA540B for ; Fri, 23 Jan 2026 08:05:48 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7C8034BA540B 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 7C8034BA540B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1769155548; cv=none; b=jRAgiLLX4lCcx8l43Pm10Xt9YsqA1OuKX8bdQULNGZtqYr8PkfUkr8fH39bRkkLq/8oFVcV2Ua1QFzs2+QHCjvhD14F01YTlbbgpVuA86FAzHz/L8EunblXYyjVnAa/5MeKtAtYOmIvAatLmmHS4+GTgNGYDT+tQf/NFTj0VKfw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1769155548; c=relaxed/simple; bh=Qm9ukaiY9wRMv7LvPxpcE8/Sl8XEq7WVMrZg9ZQ1TKY=; h=DKIM-Signature:MIME-Version:From:To:Subject:Date:Message-Id: MIME-Version; b=O+nyd93/97IPRThkMg5pjmk37Ag143XHthrYVnJQiM+Fb87AiswMEYC8p/1plgyYWUoV2HIAOmSEMoZl5Jmso6uAsZLn4yDqUqqz8rYgZPMjbr1a61+giwaejywpufoj2MyVQJlC8ncNwASrOZ0Fm98yoBCffVTlrxeHtjnPdS0= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1769155549; x=1800691549; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=Qm9ukaiY9wRMv7LvPxpcE8/Sl8XEq7WVMrZg9ZQ1TKY=; b=flk9oQZ6lOj0pmFcQk9DKiEassZrxW8NPEwLc5NYkyGtZUP7RRhhFD7f 4KB5EVJvXT7wwTvF4Ap4yNWp2SedR0WVnGamrCUBN0K4ZVwkAm5aU+pZC r9qTywiqwC2zadU7n30Koc2IY1jw+D71PNziTGUG4RojtzorLZ8chVEbM jbtex86hA1BnrY0TMTj9WXKSA19vZFqP/LVzaOxT+of18KgYKzFsGDgjw /G+Qjda+mfSYQsFaOeRAhm69IvltSi1GHTHGdIeyLY8u9ye4oBMkuLPA8 DxHgNndV11DQPE9wfoajLwtQxUHGsNhJEnDpQL1go82A69chMFqu7eGjT w==; X-CSE-ConnectionGUID: /ftM9/9TTUWtsW578wDkFg== X-CSE-MsgGUID: JmAkfmPySBaplx4n2f7/+A== X-IronPort-AV: E=McAfee;i="6800,10657,11679"; a="69603029" X-IronPort-AV: E=Sophos;i="6.21,248,1763452800"; d="scan'208";a="69603029" Content-Type: multipart/mixed; boundary="===============1694012148564620400==" MIME-Version: 1.0 Received: from orviesa009.jf.intel.com ([10.64.159.149]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Jan 2026 00:05:48 -0800 X-CSE-ConnectionGUID: JhUd+xCXSqmLWpUiYp1j2w== X-CSE-MsgGUID: LCSpxmzdSjSbkNYKnn1vQA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.21,248,1763452800"; d="scan'208";a="206785276" Received: from gkldtt-dev-004.igk.intel.com (HELO localhost) ([10.123.221.202]) by orviesa009-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Jan 2026 00:05:46 -0800 From: Christina Schimpe To: gdb-patches@sourceware.org Cc: thiago.bauermann@linaro.org Subject: [PATCH v2 1/9] gdb: Generalize handling of the shadow stack pointer. Date: Fri, 23 Jan 2026 08:05:23 +0000 Message-Id: <20260123080532.878738-2-christina.schimpe@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260123080532.878738-1-christina.schimpe@intel.com> References: <20260123080532.878738-1-christina.schimpe@intel.com> MIME-Version: 1.0 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org --===============1694012148564620400== Content-Transfer-Encoding: 8bit Until now, handling of the shadow stack pointer has been done in the target dependent implementations of the gdbarch hook 'gdbarch_shadow_stack_push'. Also amd64 and aarch64 linux specific unwinders for the shadow stack pointer are implemented. In a following patch a command line option "-shadow" will be added to the backtrace command to print the shadow stack backtrace. This requires more target-independent logic to handle the shadow stack pointer. To avoid that we duplicate the logic, add new source and header files "shadow-stack" for the implementation of shadow_stack_push and shadow stack pointer unwinding in a target-independent way. --- gdb/Makefile.in | 2 + gdb/aarch64-linux-tdep.c | 51 +----------- gdb/aarch64-tdep.c | 46 +++-------- gdb/amd64-linux-tdep.c | 135 +++---------------------------- gdb/amd64-tdep.c | 20 +++++ gdb/gdbarch-gen.c | 126 +++++++++++++++++++++++------ gdb/gdbarch-gen.h | 63 +++++++++++---- gdb/gdbarch_components.py | 81 +++++++++++++++---- gdb/infcall.c | 4 +- gdb/linux-tdep.c | 9 ++- gdb/shadow-stack.c | 166 ++++++++++++++++++++++++++++++++++++++ gdb/shadow-stack.h | 38 +++++++++ 12 files changed, 473 insertions(+), 268 deletions(-) create mode 100644 gdb/shadow-stack.c create mode 100644 gdb/shadow-stack.h diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 161e8128e98..7e8629048f3 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1191,6 +1191,7 @@ COMMON_SFILES = \ sentinel-frame.c \ ser-event.c \ serial.c \ + shadow-stack.c \ skip.c \ solib.c \ solib-target.c \ @@ -1652,6 +1653,7 @@ HFILES_NO_SRCDIR = \ serial.h \ ser-tcp.h \ ser-unix.h \ + shadow-stack.h \ sh-tdep.h \ sim-regno.h \ skip.h \ diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index b85c25ecae1..f37b28067b8 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -64,6 +64,7 @@ #include "elf/common.h" #include "elf/aarch64.h" #include "arch/aarch64-insn.h" +#include "shadow-stack.h" /* For std::pow */ #include @@ -2614,54 +2615,6 @@ aarch64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache, return gcspr; } -/* Implement Guarded Control Stack Pointer Register unwinding. For each - previous GCS pointer check if its address is still in the GCS memory - range. If it's outside the range set the returned value to unavailable, - otherwise return a value containing the new GCS pointer. */ - -static value * -aarch64_linux_dwarf2_prev_gcspr (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 gcspr = extract_unsigned_integer (v->contents_all ().data (), - size, byte_order); - - /* Starting with v6.13, the Linux kernel supports Guarded Control - Stack. Using /proc/PID/smaps we can only check if the current - GCSPR points to GCS memory. Only if this is the case a valid - previous GCS pointer can be calculated. */ - std::pair range; - if (linux_address_in_shadow_stack_mem_range (gcspr, &range)) - { - /* The GCS grows downwards. To compute the previous GCS pointer, - we need to increment the GCSPR. */ - CORE_ADDR new_gcspr = gcspr + 8; - - /* If NEW_GCSPR still points within the current GCS memory range - we consider it to be valid. */ - if (new_gcspr < range.second) - return frame_unwind_got_address (this_frame, regnum, new_gcspr); - } - } - - /* Return a value which is marked as unavailable in case we could not - calculate a valid previous GCS 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; -} - /* AArch64 Linux implementation of the report_signal_info gdbarch hook. Displays information about possible memory tag violations. */ @@ -3240,7 +3193,7 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { set_gdbarch_get_shadow_stack_pointer (gdbarch, aarch64_linux_get_shadow_stack_pointer); - tdep->fn_prev_gcspr = aarch64_linux_dwarf2_prev_gcspr; + tdep->fn_prev_gcspr = dwarf2_prev_ssp; } } diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index f1bdce453db..836af863d3a 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -57,6 +57,9 @@ /* For inferior_ptid and current_inferior (). */ #include "inferior.h" + +#include "shadow-stack.h" + /* For std::sqrt and std::pow. */ #include @@ -1889,29 +1892,6 @@ pass_in_v_vfp_candidate (struct gdbarch *gdbarch, struct regcache *regcache, } } -/* Push LR_VALUE to the Guarded Control Stack. */ - -static void -aarch64_push_gcs_entry (regcache *regs, CORE_ADDR lr_value) -{ - gdbarch *arch = regs->arch (); - aarch64_gdbarch_tdep *tdep = gdbarch_tdep (arch); - CORE_ADDR gcs_addr; - - register_status status = regs->cooked_read (tdep->gcs_reg_base, &gcs_addr); - if (status != REG_VALID) - error (_("Can't read $gcspr.")); - - gcs_addr -= 8; - gdb_byte buf[8]; - store_integer (buf, gdbarch_byte_order (arch), lr_value); - if (target_write_memory (gcs_addr, buf, sizeof (buf)) != 0) - error (_("Can't write to Guarded Control Stack.")); - - /* Update GCSPR. */ - regcache_cooked_write_unsigned (regs, tdep->gcs_reg_base, gcs_addr); -} - /* Remove the newest entry from the Guarded Control Stack. */ static void @@ -1929,15 +1909,6 @@ aarch64_pop_gcs_entry (regcache *regs) regcache_cooked_write_unsigned (regs, tdep->gcs_reg_base, gcs_addr + 8); } -/* Implement the "shadow_stack_push" gdbarch method. */ - -static void -aarch64_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr, - regcache *regcache) -{ - aarch64_push_gcs_entry (regcache, new_addr); -} - /* Implement the "push_dummy_call" gdbarch method. */ static CORE_ADDR @@ -3679,7 +3650,7 @@ aarch64_displaced_step_b (const int is_bl, const int32_t offset, gdbarch_get_shadow_stack_pointer (dsd->regs->arch (), dsd->regs, gcs_is_enabled); if (gcs_is_enabled) - aarch64_push_gcs_entry (dsd->regs, data->insn_addr + 4); + shadow_stack_push (dsd->regs, data->insn_addr + 4); } } @@ -3843,7 +3814,7 @@ aarch64_displaced_step_others (const uint32_t insn, gdbarch_get_shadow_stack_pointer (dsd->regs->arch (), dsd->regs, gcs_is_enabled); if (gcs_is_enabled) - aarch64_push_gcs_entry (dsd->regs, data->insn_addr + 4); + shadow_stack_push (dsd->regs, data->insn_addr + 4); } else aarch64_emit_insn (dsd->insn_buf, insn); @@ -4817,6 +4788,10 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* Register a hook for converting a memory tag to a string. */ set_gdbarch_memtag_to_string (gdbarch, aarch64_memtag_to_string); + /* AArch64's shadow stack pointer is the GCSPR. */ + if (tdep->has_gcs ()) + set_gdbarch_ssp_regnum (gdbarch, tdep->gcs_reg_base); + /* ABI */ set_gdbarch_short_bit (gdbarch, 16); set_gdbarch_int_bit (gdbarch, 32); @@ -4879,9 +4854,6 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_get_pc_address_flags (gdbarch, aarch64_get_pc_address_flags); - if (tdep->has_gcs ()) - set_gdbarch_shadow_stack_push (gdbarch, aarch64_shadow_stack_push); - tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data)); /* Fetch the updated number of registers after we're done adding all diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index 77c6976e071..656daa0f0ee 100644 --- a/gdb/amd64-linux-tdep.c +++ b/gdb/amd64-linux-tdep.c @@ -48,8 +48,6 @@ #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" @@ -1923,18 +1921,6 @@ amd64_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid, return dtv_addr; } -/* Return the number of bytes required to update the shadow stack pointer - by one element. For x32 the shadow stack elements are still 64-bit - aligned. Thus, gdbarch_addr_bit cannot be used to compute the new - stack pointer. */ - -static inline int -amd64_linux_shadow_stack_element_size_aligned (gdbarch *gdbarch) -{ - const bfd_arch_info *binfo = gdbarch_bfd_arch_info (gdbarch); - return (binfo->bits_per_word / binfo->bits_per_byte); -} - /* Read the shadow stack pointer register and return its value, if possible. */ @@ -1966,117 +1952,15 @@ amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache, return ssp; } -/* If shadow stack is enabled, push the address NEW_ADDR to the shadow - stack and increment the shadow stack pointer accordingly. */ +/* Return true if ADDR points to the top of an empty shadow stack, defined by + RANGE [start_address, end_address). */ -static void -amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr, - regcache *regcache) -{ - bool shadow_stack_enabled; - std::optional ssp - = amd64_linux_get_shadow_stack_pointer (gdbarch, regcache, - shadow_stack_enabled); - - /* For amd64/Linux, if SSP has a value that means shadow stack is - enabled. */ - if (!ssp.has_value ()) - return; - else - gdb_assert (shadow_stack_enabled); - - /* The shadow stack grows downwards. To push addresses to the stack, - we need to decrement SSP. */ - const int element_size - = amd64_linux_shadow_stack_element_size_aligned (gdbarch); - const CORE_ADDR new_ssp = *ssp - element_size; - - /* Using /proc/PID/smaps we can only check if NEW_SSP points to shadow - stack memory. If it doesn't, we assume the stack is full. */ - std::pair memrange; - if (!linux_address_in_shadow_stack_mem_range (new_ssp, &memrange)) - error (_("No space left on the shadow stack.")); - - /* On x86 there can be a shadow stack token at bit 63. For x32, the - address size is only 32 bit. Always write back the full 8 bytes to - include the shadow stack token. */ - const bfd_endian byte_order = gdbarch_byte_order (gdbarch); - write_memory_unsigned_integer (new_ssp, element_size, byte_order, - (ULONGEST) new_addr); - - i386_gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); - gdb_assert (tdep->ssp_regnum > -1); - - regcache_raw_write_unsigned (regcache, tdep->ssp_regnum, new_ssp); -} - -/* Implement shadow stack pointer unwinding. For each new shadow stack - pointer check if its address is still in the shadow stack memory range. - If it's outside the range set the returned value to unavailable, - otherwise return a value containing the new shadow stack pointer. */ - -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); - - /* 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. */ - CORE_ADDR new_ssp - = ssp + amd64_linux_shadow_stack_element_size_aligned (gdbarch); - - /* There can be scenarios where we have a shadow stack pointer - but the shadow stack is empty, as no call instruction has - been executed yet. 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; -} - -/* Implement the "init_reg" dwarf2_frame_ops method. */ - -static void -amd64_init_reg (gdbarch *gdbarch, int regnum, dwarf2_frame_state_reg *reg, - const frame_info_ptr &this_frame) +static bool +amd64_linux_top_addr_empty_shadow_stack + (gdbarch *gdbarch, const CORE_ADDR addr, + const std::pair range) { - 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; - } + return addr == range.second; } static void @@ -2137,10 +2021,11 @@ 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); - set_gdbarch_shadow_stack_push (gdbarch, amd64_linux_shadow_stack_push); set_gdbarch_get_shadow_stack_pointer (gdbarch, amd64_linux_get_shadow_stack_pointer); - dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg); + + set_gdbarch_top_addr_empty_shadow_stack + (gdbarch, amd64_linux_top_addr_empty_shadow_stack); } static void diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c index ff2e9dee117..db041618123 100755 --- a/gdb/amd64-tdep.c +++ b/gdb/amd64-tdep.c @@ -51,6 +51,8 @@ #include "x86-tdep.h" #include "amd64-ravenscar-thread.h" #include "gdbsupport/selftest.h" +#include "shadow-stack.h" +#include "dwarf2/frame.h" /* Note that the AMD64 architecture was previously known as x86-64. The latter is (forever) engraved into the canonical system name as @@ -3491,6 +3493,21 @@ amd64_in_indirect_branch_thunk (struct gdbarch *gdbarch, CORE_ADDR pc) AMD64_RIP_REGNUM); } +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 = dwarf2_prev_ssp; + } +} + void amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch, const target_desc *default_tdesc) @@ -3650,6 +3667,9 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch, set_gdbarch_in_indirect_branch_thunk (gdbarch, amd64_in_indirect_branch_thunk); + set_gdbarch_ssp_regnum (gdbarch, tdep->ssp_regnum); + dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg); + register_amd64_ravenscar_ops (gdbarch); } diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c index fb3d070b3c9..794bb4d50dc 100644 --- a/gdb/gdbarch-gen.c +++ b/gdb/gdbarch-gen.c @@ -85,6 +85,7 @@ struct gdbarch int pc_regnum = -1; int ps_regnum = -1; int fp0_regnum = -1; + int ssp_regnum = -1; gdbarch_dwarf2_reg_to_regnum_ftype *dwarf2_reg_to_regnum = no_op_reg_to_regnum; gdbarch_register_name_ftype *register_name = nullptr; gdbarch_register_type_ftype *register_type = nullptr; @@ -257,8 +258,10 @@ struct gdbarch gdbarch_read_core_file_mappings_ftype *read_core_file_mappings = default_read_core_file_mappings; gdbarch_use_target_description_from_corefile_notes_ftype *use_target_description_from_corefile_notes = default_use_target_description_from_corefile_notes; gdbarch_core_parse_exec_context_ftype *core_parse_exec_context = default_core_parse_exec_context; - gdbarch_shadow_stack_push_ftype *shadow_stack_push = nullptr; gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer = default_get_shadow_stack_pointer; + gdbarch_address_in_shadow_stack_memory_range_ftype *address_in_shadow_stack_memory_range = nullptr; + gdbarch_top_addr_empty_shadow_stack_ftype *top_addr_empty_shadow_stack = nullptr; + int shadow_stack_element_size_aligned = 8; }; /* Create a new ``struct gdbarch'' based on information provided by @@ -344,6 +347,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of pc_regnum, invalid_p == 0. */ /* Skip verify of ps_regnum, invalid_p == 0. */ /* Skip verify of fp0_regnum, invalid_p == 0. */ + /* Skip verify of ssp_regnum, invalid_p == 0. */ /* Skip verify of dwarf2_reg_to_regnum, invalid_p == 0. */ if (gdbarch->register_name == 0) log.puts ("\n\tregister_name"); @@ -527,8 +531,10 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of read_core_file_mappings, invalid_p == 0. */ /* Skip verify of use_target_description_from_corefile_notes, invalid_p == 0. */ /* Skip verify of core_parse_exec_context, invalid_p == 0. */ - /* Skip verify of shadow_stack_push, has predicate. */ /* Skip verify of get_shadow_stack_pointer, invalid_p == 0. */ + /* Skip verify of address_in_shadow_stack_memory_range, has predicate. */ + /* Skip verify of top_addr_empty_shadow_stack, has predicate. */ + /* Skip verify of shadow_stack_element_size_aligned, invalid_p == 0. */ if (!log.empty ()) internal_error (_("verify_gdbarch: the following are invalid ...%s"), log.c_str ()); @@ -701,6 +707,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) gdb_printf (file, "gdbarch_dump: fp0_regnum = %s\n", plongest (gdbarch->fp0_regnum)); + gdb_printf (file, + "gdbarch_dump: ssp_regnum = %s\n", + plongest (gdbarch->ssp_regnum)); gdb_printf (file, "gdbarch_dump: dwarf2_reg_to_regnum = <%s>\n", host_address_to_string (gdbarch->dwarf2_reg_to_regnum)); @@ -1385,15 +1394,24 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) gdb_printf (file, "gdbarch_dump: core_parse_exec_context = <%s>\n", host_address_to_string (gdbarch->core_parse_exec_context)); - gdb_printf (file, - "gdbarch_dump: gdbarch_shadow_stack_push_p() = %d\n", - gdbarch_shadow_stack_push_p (gdbarch)); - gdb_printf (file, - "gdbarch_dump: shadow_stack_push = <%s>\n", - host_address_to_string (gdbarch->shadow_stack_push)); gdb_printf (file, "gdbarch_dump: get_shadow_stack_pointer = <%s>\n", host_address_to_string (gdbarch->get_shadow_stack_pointer)); + gdb_printf (file, + "gdbarch_dump: gdbarch_address_in_shadow_stack_memory_range_p() = %d\n", + gdbarch_address_in_shadow_stack_memory_range_p (gdbarch)); + gdb_printf (file, + "gdbarch_dump: address_in_shadow_stack_memory_range = <%s>\n", + host_address_to_string (gdbarch->address_in_shadow_stack_memory_range)); + gdb_printf (file, + "gdbarch_dump: gdbarch_top_addr_empty_shadow_stack_p() = %d\n", + gdbarch_top_addr_empty_shadow_stack_p (gdbarch)); + gdb_printf (file, + "gdbarch_dump: top_addr_empty_shadow_stack = <%s>\n", + host_address_to_string (gdbarch->top_addr_empty_shadow_stack)); + gdb_printf (file, + "gdbarch_dump: shadow_stack_element_size_aligned = %s\n", + plongest (gdbarch->shadow_stack_element_size_aligned)); if (gdbarch->dump_tdep != NULL) gdbarch->dump_tdep (gdbarch, file); } @@ -2141,6 +2159,23 @@ set_gdbarch_fp0_regnum (struct gdbarch *gdbarch, gdbarch->fp0_regnum = fp0_regnum; } +int +gdbarch_ssp_regnum (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + /* Skip verify of ssp_regnum, invalid_p == 0. */ + if (gdbarch_debug >= 2) + gdb_printf (gdb_stdlog, "gdbarch_ssp_regnum called\n"); + return gdbarch->ssp_regnum; +} + +void +set_gdbarch_ssp_regnum (struct gdbarch *gdbarch, + int ssp_regnum) +{ + gdbarch->ssp_regnum = ssp_regnum; +} + int gdbarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int dwarf2_regnr) { @@ -5455,43 +5490,84 @@ set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarch->core_parse_exec_context = core_parse_exec_context; } +std::optional +gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->get_shadow_stack_pointer != NULL); + if (gdbarch_debug >= 2) + gdb_printf (gdb_stdlog, "gdbarch_get_shadow_stack_pointer called\n"); + return gdbarch->get_shadow_stack_pointer (gdbarch, regcache, shadow_stack_enabled); +} + +void +set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, + gdbarch_get_shadow_stack_pointer_ftype get_shadow_stack_pointer) +{ + gdbarch->get_shadow_stack_pointer = get_shadow_stack_pointer; +} + bool -gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch) +gdbarch_address_in_shadow_stack_memory_range_p (struct gdbarch *gdbarch) { gdb_assert (gdbarch != NULL); - return gdbarch->shadow_stack_push != NULL; + return gdbarch->address_in_shadow_stack_memory_range != NULL; +} + +bool +gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gdbarch, CORE_ADDR ADDR, std::pair *range) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->address_in_shadow_stack_memory_range != NULL); + if (gdbarch_debug >= 2) + gdb_printf (gdb_stdlog, "gdbarch_address_in_shadow_stack_memory_range called\n"); + return gdbarch->address_in_shadow_stack_memory_range (ADDR, range); } void -gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache) +set_gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gdbarch, + gdbarch_address_in_shadow_stack_memory_range_ftype address_in_shadow_stack_memory_range) +{ + gdbarch->address_in_shadow_stack_memory_range = address_in_shadow_stack_memory_range; +} + +bool +gdbarch_top_addr_empty_shadow_stack_p (struct gdbarch *gdbarch) { gdb_assert (gdbarch != NULL); - gdb_assert (gdbarch->shadow_stack_push != NULL); + return gdbarch->top_addr_empty_shadow_stack != NULL; +} + +bool +gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch, const CORE_ADDR addr, const std::pair range) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->top_addr_empty_shadow_stack != NULL); if (gdbarch_debug >= 2) - gdb_printf (gdb_stdlog, "gdbarch_shadow_stack_push called\n"); - gdbarch->shadow_stack_push (gdbarch, new_addr, regcache); + gdb_printf (gdb_stdlog, "gdbarch_top_addr_empty_shadow_stack called\n"); + return gdbarch->top_addr_empty_shadow_stack (gdbarch, addr, range); } void -set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, - gdbarch_shadow_stack_push_ftype shadow_stack_push) +set_gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch, + gdbarch_top_addr_empty_shadow_stack_ftype top_addr_empty_shadow_stack) { - gdbarch->shadow_stack_push = shadow_stack_push; + gdbarch->top_addr_empty_shadow_stack = top_addr_empty_shadow_stack; } -std::optional -gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled) +int +gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch) { gdb_assert (gdbarch != NULL); - gdb_assert (gdbarch->get_shadow_stack_pointer != NULL); + /* Skip verify of shadow_stack_element_size_aligned, invalid_p == 0. */ if (gdbarch_debug >= 2) - gdb_printf (gdb_stdlog, "gdbarch_get_shadow_stack_pointer called\n"); - return gdbarch->get_shadow_stack_pointer (gdbarch, regcache, shadow_stack_enabled); + gdb_printf (gdb_stdlog, "gdbarch_shadow_stack_element_size_aligned called\n"); + return gdbarch->shadow_stack_element_size_aligned; } void -set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, - gdbarch_get_shadow_stack_pointer_ftype get_shadow_stack_pointer) +set_gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch, + int shadow_stack_element_size_aligned) { - gdbarch->get_shadow_stack_pointer = get_shadow_stack_pointer; + gdbarch->shadow_stack_element_size_aligned = shadow_stack_element_size_aligned; } diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index 7ea9971362c..cbe970fd716 100644 --- a/gdb/gdbarch-gen.h +++ b/gdb/gdbarch-gen.h @@ -283,6 +283,12 @@ extern void set_gdbarch_ps_regnum (struct gdbarch *gdbarch, int ps_regnum); extern int gdbarch_fp0_regnum (struct gdbarch *gdbarch); extern void set_gdbarch_fp0_regnum (struct gdbarch *gdbarch, int fp0_regnum); +/* Register number for the shadow stack pointer. For inferior calls, the + gdbarch value ssp_regnum has to be provided. */ + +extern int gdbarch_ssp_regnum (struct gdbarch *gdbarch); +extern void set_gdbarch_ssp_regnum (struct gdbarch *gdbarch, int ssp_regnum); + /* Provide a default mapping from a DWARF2 register number to a gdb REGNUM. Return -1 for bad REGNUM. Note: Several targets get this wrong. */ @@ -1771,22 +1777,23 @@ extern void set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarc /* Some targets support special hardware-assisted control-flow protection technologies. For example, the Intel Control-Flow Enforcement Technology (Intel CET) on x86 provides a shadow stack and indirect branch tracking. - To enable shadow stack support for inferior calls the shadow_stack_push - gdbarch hook has to be provided. The get_shadow_stack_pointer gdbarch - hook has to be provided to enable displaced stepping. - - Push NEW_ADDR to the shadow stack and update the shadow stack pointer. */ - -extern bool gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch); - -typedef void (gdbarch_shadow_stack_push_ftype) (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache); -extern void gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache); -extern void set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, gdbarch_shadow_stack_push_ftype *shadow_stack_push); - -/* If possible, return the shadow stack pointer. If the shadow stack + For GDB shadow stack support the following methods or values must be + provided: + - get_shadow_stack_pointer: required for displaced stepping and inferior + function calls + - address_in_shadow_stack_memory_range: required for shadow stack pointer + unwinding and inferior function calls + - top_addr_empty_shadow_stack: required for shadow stack pointer unwinding + - ssp_regnum: required for inferior function calls. + + If the shadow stack alignment is not the predefault of 8 bytes, configure + the gdbarch value shadow_stack_element_size_aligned. + + If possible, return the shadow stack pointer. If the shadow stack feature is enabled then set SHADOW_STACK_ENABLED to true, otherwise set SHADOW_STACK_ENABLED to false. This hook has to be provided to enable - displaced stepping for shadow stack enabled programs. + displaced stepping and inferior function calls for shadow stack enabled + programs. On some architectures, the shadow stack pointer is available even if the feature is disabled. So dependent on the target, an implementation of this function may return a valid shadow stack pointer, but set @@ -1795,3 +1802,31 @@ extern void set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, gdbarch_shad typedef std::optional (gdbarch_get_shadow_stack_pointer_ftype) (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled); extern std::optional gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled); extern void set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer); + +/* Returns true if ADDR belongs to a shadow stack memory range. If this is + the case and RANGE is non-null, assign the shadow stack memory range to + RANGE [start_address, end_address). This hook has to be provided for + shadow stack pointer unwinding and inferior function calls. */ + +extern bool gdbarch_address_in_shadow_stack_memory_range_p (struct gdbarch *gdbarch); + +typedef bool (gdbarch_address_in_shadow_stack_memory_range_ftype) (CORE_ADDR ADDR, std::pair *range); +extern bool gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gdbarch, CORE_ADDR ADDR, std::pair *range); +extern void set_gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gdbarch, gdbarch_address_in_shadow_stack_memory_range_ftype *address_in_shadow_stack_memory_range); + +/* Return true if ADDR points to the top of an empty shadow stack, defined by + RANGE [start_address, end_address). This hook has to be provided to enable + unwinding of the shadow stack pointer. */ + +extern bool gdbarch_top_addr_empty_shadow_stack_p (struct gdbarch *gdbarch); + +typedef bool (gdbarch_top_addr_empty_shadow_stack_ftype) (struct gdbarch *gdbarch, const CORE_ADDR addr, const std::pair range); +extern bool gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch, const CORE_ADDR addr, const std::pair range); +extern void set_gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch, gdbarch_top_addr_empty_shadow_stack_ftype *top_addr_empty_shadow_stack); + +/* The number of bytes required to update the shadow stack pointer by one + element. In case the alignment is not the predefault (8 bytes), configure + this value. */ + +extern int gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch); +extern void set_gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch, int shadow_stack_element_size_aligned); diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py index f419d21655e..d44e59a523d 100644 --- a/gdb/gdbarch_components.py +++ b/gdb/gdbarch_components.py @@ -544,6 +544,17 @@ Value( invalid=False, ) +Value( + comment=""" +Register number for the shadow stack pointer. For inferior calls, the +gdbarch value ssp_regnum has to be provided. +""", + type="int", + name="ssp_regnum", + predefault="-1", + invalid=False, +) + Method( comment=""" Provide a default mapping from a DWARF2 register number to a gdb REGNUM. @@ -2811,24 +2822,23 @@ Method( Some targets support special hardware-assisted control-flow protection technologies. For example, the Intel Control-Flow Enforcement Technology (Intel CET) on x86 provides a shadow stack and indirect branch tracking. -To enable shadow stack support for inferior calls the shadow_stack_push -gdbarch hook has to be provided. The get_shadow_stack_pointer gdbarch -hook has to be provided to enable displaced stepping. - -Push NEW_ADDR to the shadow stack and update the shadow stack pointer. -""", - type="void", - name="shadow_stack_push", - params=[("CORE_ADDR", "new_addr"), ("regcache *", "regcache")], - predicate=True, -) +For GDB shadow stack support the following methods or values must be +provided: +- get_shadow_stack_pointer: required for displaced stepping and inferior + function calls +- address_in_shadow_stack_memory_range: required for shadow stack pointer + unwinding and inferior function calls +- top_addr_empty_shadow_stack: required for shadow stack pointer unwinding +- ssp_regnum: required for inferior function calls. + +If the shadow stack alignment is not the predefault of 8 bytes, configure +the gdbarch value shadow_stack_element_size_aligned. -Method( - comment=""" If possible, return the shadow stack pointer. If the shadow stack feature is enabled then set SHADOW_STACK_ENABLED to true, otherwise set SHADOW_STACK_ENABLED to false. This hook has to be provided to enable -displaced stepping for shadow stack enabled programs. +displaced stepping and inferior function calls for shadow stack enabled +programs. On some architectures, the shadow stack pointer is available even if the feature is disabled. So dependent on the target, an implementation of this function may return a valid shadow stack pointer, but set @@ -2840,3 +2850,46 @@ SHADOW_STACK_ENABLED to false. predefault="default_get_shadow_stack_pointer", invalid=False, ) + +Function( + comment=""" +Returns true if ADDR belongs to a shadow stack memory range. If this is +the case and RANGE is non-null, assign the shadow stack memory range to +RANGE [start_address, end_address). This hook has to be provided for +shadow stack pointer unwinding and inferior function calls. +""", + type="bool", + name="address_in_shadow_stack_memory_range", + params=[ + ("CORE_ADDR", "ADDR"), + ("std::pair *", "range") + ], + predicate=True, +) + +Method( + comment=""" +Return true if ADDR points to the top of an empty shadow stack, defined by +RANGE [start_address, end_address). This hook has to be provided to enable +unwinding of the shadow stack pointer. +""", + type="bool", + name="top_addr_empty_shadow_stack", + params=[ + ("const CORE_ADDR", "addr"), + ("const std::pair", "range") + ], + predicate=True, +) + +Value( + comment=""" +The number of bytes required to update the shadow stack pointer by one +element. In case the alignment is not the predefault (8 bytes), configure +this value. +""", + type="int", + name="shadow_stack_element_size_aligned", + predefault="8", + invalid=False, +) diff --git a/gdb/infcall.c b/gdb/infcall.c index dcbae679d07..e8fbc7f30d0 100644 --- a/gdb/infcall.c +++ b/gdb/infcall.c @@ -42,6 +42,7 @@ #include "thread-fsm.h" #include #include "gdbsupport/scope-exit.h" +#include "shadow-stack.h" #include /* True if we are debugging inferior calls. */ @@ -1464,8 +1465,7 @@ call_function_by_hand_dummy (struct value *function, /* Push the return address of the inferior (bp_addr) to the shadow stack and update the shadow stack pointer. As we don't execute a call instruction to call the function we need to handle this manually. */ - if (gdbarch_shadow_stack_push_p (gdbarch)) - gdbarch_shadow_stack_push (gdbarch, bp_addr, regcache); + shadow_stack_push (regcache, bp_addr); /* Set up a frame ID for the dummy frame so we can pass it to set_momentary_breakpoint. We need to give the breakpoint a frame diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index ccc7fa7cf9d..9d2b32b6481 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -3095,8 +3095,11 @@ linux_address_in_shadow_stack_mem_range if (it != smaps.end ()) { - range->first = it->start_address; - range->second = it->end_address; + if (range != nullptr) + { + range->first = it->start_address; + range->second = it->end_address; + } return true; } @@ -3146,6 +3149,8 @@ linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch, set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); set_gdbarch_core_parse_exec_context (gdbarch, linux_corefile_parse_exec_context); + set_gdbarch_address_in_shadow_stack_memory_range + (gdbarch, linux_address_in_shadow_stack_mem_range); } INIT_GDB_FILE (linux_tdep) diff --git a/gdb/shadow-stack.c b/gdb/shadow-stack.c new file mode 100644 index 00000000000..77d7c07d970 --- /dev/null +++ b/gdb/shadow-stack.c @@ -0,0 +1,166 @@ +/* Manage a shadow stack pointer for GDB, the GNU debugger. + + Copyright (C) 2024-2026 Free Software Foundation, Inc. + This file is part of GDB. + + 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 . */ + +#include "defs.h" +#include "arch-utils.h" +#include "gdbcore.h" +#include "extract-store-integer.h" +#include "frame.h" +#include "frame-unwind.h" +#include "shadow-stack.h" + +class regcache; + +enum class ssp_update_direction +{ + /* Update ssp towards the oldest (outermost) element of the shadow + stack. */ + outer = 0, + + /* Update ssp towards the most recent (innermost) element of the + shadow stack. */ + inner +}; + +/* Return a new shadow stack pointer which is incremented or decremented + by one element dependent on DIRECTION. */ + +static CORE_ADDR +update_shadow_stack_pointer (gdbarch *gdbarch, CORE_ADDR ssp, + const ssp_update_direction direction) +{ + bool increment = gdbarch_stack_grows_down (gdbarch) + ? direction == ssp_update_direction::outer + : direction == ssp_update_direction::inner; + + if (increment) + return ssp + gdbarch_shadow_stack_element_size_aligned (gdbarch); + else + return ssp - gdbarch_shadow_stack_element_size_aligned (gdbarch); +} + +/* See shadow-stack.h. */ + +void shadow_stack_push (regcache *regcache, const CORE_ADDR new_addr) +{ + gdbarch *gdbarch = regcache->arch (); + if (!gdbarch_address_in_shadow_stack_memory_range_p (gdbarch) + || gdbarch_ssp_regnum (gdbarch) == -1) + return; + + bool shadow_stack_enabled; + std::optional ssp + = gdbarch_get_shadow_stack_pointer (gdbarch, regcache, + shadow_stack_enabled); + if (!ssp.has_value () || !shadow_stack_enabled) + return; + + const CORE_ADDR new_ssp + = update_shadow_stack_pointer (gdbarch, *ssp, + ssp_update_direction::inner); + + /* If NEW_SSP does not point to shadow stack memory, we assume the + stack is full. */ + if (!gdbarch_address_in_shadow_stack_memory_range (gdbarch, + new_ssp, + nullptr)) + error (_("No space left on the shadow stack.")); + + /* On x86 there can be a shadow stack token at bit 63. For x32, the + address size is only 32 bit. Always write back the full element + size to include the shadow stack token. */ + const int element_size + = gdbarch_shadow_stack_element_size_aligned (gdbarch); + + const bfd_endian byte_order = gdbarch_byte_order (gdbarch); + + write_memory_unsigned_integer (new_ssp, element_size, byte_order, + (ULONGEST) new_addr); + + regcache_raw_write_unsigned (regcache, + gdbarch_ssp_regnum (gdbarch), + new_ssp); +} + +/* See shadow-stack.h. */ + +value * +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 (gdbarch_address_in_shadow_stack_memory_range_p (gdbarch) + && v->entirely_available () && !v->optimized_out ()) + { + const 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); + + /* Only if the current shadow stack pointer SSP points to shadow + stack memory a valid previous shadow stack pointer can be + calculated. */ + std::pair range; + if (gdbarch_address_in_shadow_stack_memory_range (gdbarch, ssp, &range)) + { + /* 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 unwinder without support for shadow stack + switches for now. */ + const CORE_ADDR new_ssp + = update_shadow_stack_pointer (gdbarch, ssp, + ssp_update_direction::outer); + + /* On x86, if NEW_SSP points to the end outside of RANGE + (NEW_SSP == RANGE.SECOND), it indicates that NEW_SSP is + valid, but the shadow stack is empty. In contrast, for + ARM's Guarded Control Stack, if NEW_SSP points to the end + of RANGE, it means that the shadow stack feature is + disabled. */ + bool is_top_addr_empty_shadow_stack + = gdbarch_top_addr_empty_shadow_stack_p (gdbarch) + && gdbarch_top_addr_empty_shadow_stack (gdbarch, new_ssp, range); + + /* Validate NEW_SSP. This may depend on both + IS_TOP_ADDR_EMPTY_SHADOW_STACK and the gdbarch hook (e.g., x86), + or on the hook only (e.g., ARM). */ + if (is_top_addr_empty_shadow_stack + || gdbarch_address_in_shadow_stack_memory_range (gdbarch, + new_ssp, + &range)) + 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; +} diff --git a/gdb/shadow-stack.h b/gdb/shadow-stack.h new file mode 100644 index 00000000000..5f8395ec047 --- /dev/null +++ b/gdb/shadow-stack.h @@ -0,0 +1,38 @@ +/* Definitions to manage a shadow stack pointer for GDB, the GNU debugger. + + Copyright (C) 2024-2026 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 . */ + +#ifndef GDB_SHADOW_STACK_H +#define GDB_SHADOW_STACK_H + +/* If shadow stack is enabled, push the address NEW_ADDR on the shadow + stack and update the shadow stack pointer accordingly. */ + +void shadow_stack_push (regcache *regcache, const CORE_ADDR new_addr); + +/* Unwind the previous shadow stack pointer of THIS_FRAME's shadow stack + pointer. REGNUM is the register number of the shadow stack pointer. + Return a value that is unavailable in case we cannot unwind the + previous shadow stack pointer. Otherwise, return a value containing + the previous shadow stack pointer. */ + +value *dwarf2_prev_ssp (const frame_info_ptr &this_frame, + void **this_cache, int regnum); + +#endif /* GDB_SHADOW_STACK_H */ -- 2.34.1 --===============1694012148564620400== Content-Type: multipart/alternative; boundary="===============6664073991897333596==" MIME-Version: 1.0 Content-Disposition: inline --===============6664073991897333596== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Intel Deutschland GmbH Registered Address: Dornacher Stra=C3=9Fe 1, 85622 Feldkirchen, Germany Tel: +49 89 991 430, www.intel.de Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell Chairperson of the Supervisory Board: Nicole Lau Registered Seat: Munich Commercial Register: Amtsgericht M=C3=BCnchen HRB 186928 --===============6664073991897333596== Content-Type: text/html; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable
Intel Deutschland GmbH
Registered Address: Dornacher Stra=C3= =9Fe 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.deManaging Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell<= div>Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: M= unich
Commercial Register: Amtsgericht M=C3=BCnchen HRB 186928 --===============6664073991897333596==-- --===============1694012148564620400==--