@@ -11,6 +11,10 @@ endif
# of some assembler macros.
ASFLAGS-.os += $(pic-ccflag)
+ifeq ($(subdir),setjmp)
+gen-as-const-headers += jmp_buf-ssp.sym
+endif
+
ifeq (no,$(riscv-r-align))
ASFLAGS-.os += -Wa,-mno-relax
ASFLAGS-.o += -Wa,-mno-relax
@@ -18,10 +18,12 @@
#include <sysdep.h>
#include <sys/asm.h>
+#include <jmp_buf-ssp.h>
+#include <tcb-offsets.h>
ENTRY (__longjmp)
LPAD
- REG_L ra, 0*SZREG(a0)
+ REG_L t1, 0*SZREG(a0)
REG_L s0, 1*SZREG(a0)
REG_L s1, 2*SZREG(a0)
REG_L s2, 3*SZREG(a0)
@@ -51,8 +53,59 @@ ENTRY (__longjmp)
FREG_L fs11,14*SZREG+11*SZFREG(a0)
#endif
+#ifdef __riscv_shadow_stack
+ /* skip unwinding if ss is not enabled */
+ ssrdp ra
+ beqz ra, .Lfin
+ REG_L t0, SSP_OFFSET(a0)
+ REG_L a0, SSP_BASE_OFFSET(a0)
+ REG_L t2, TLS_SSP_BASE_OFFSET(tp)
+ bne a0, t2, .Ldifferent_stack
+.Lunwind:
+ bleu t0, ra, .Lfin
+ /* Increase ssp by at most a page size to ensure always run into a
+ guard page before accidentally point to another legal shadow stack
+ page */
+ /* ra = (t0 - ra >= 4096) ? ra + 4096 : t0 */
+ lui a0, 1
+ add ra, ra, a0
+ bleu ra, t0, 1f
+ mv ra, t0
+1:
+ csrw ssp, ra
+ /* Test if the location pointed by ssp is legal */
+ sspush x5
+ sspopchk x5
+ j .Lunwind
+.Ldifferent_stack:
+ /* Create restore token */
+ sspush ra
+ mv a4, t0
+
+.Lfind_rstor_token:
+ /* Probe and validate target restore token */
+ ssamoswap.d a3, x0, (a4)
+ addi a2, a4, 8
+ beq a3, a2, .Lswitch_stack
+ /* Restore the shadow stack and try the next slot */
+ ssamoswap.d x0, a3, (a4)
+ addi a4, a4, -8
+ j .Lfind_rstor_token
+
+.Lswitch_stack:
+ /* Switch stack: update ssp and base */
+ csrw ssp, t0
+ REG_S a0, TLS_SSP_BASE_OFFSET(tp)
+.Lfin:
+#endif
seqz a0, a1
add a0, a0, a1 # a0 = (a1 == 0) ? 1 : a1
+#ifdef __riscv_landing_pad
+ /* Use indirect branch if CFI is enabled */
+ jr t1
+#else
+ mv ra, t1
ret
+#endif
END (__longjmp)
@@ -18,6 +18,8 @@
#include <sysdep.h>
#include <sys/asm.h>
+#include <jmp_buf-ssp.h>
+#include <tcb-offsets.h>
ENTRY (_setjmp)
LPAD
@@ -61,6 +63,26 @@ ENTRY (__sigsetjmp)
FREG_S fs11,14*SZREG+11*SZFREG(a0)
#endif
+#ifdef __riscv_shadow_stack
+ /* Skip if shadow stack is not enabled */
+ ssrdp t0
+ beqz t0, .Lfin
+
+ /* Read ssp_base from TLS */
+ REG_L t2, TLS_SSP_BASE_OFFSET(tp)
+ bnez t2, .Lbase_saved
+
+ /* if not found, use current ssp as the marker */
+ mv t2, t0
+ REG_S t2, TLS_SSP_BASE_OFFSET(tp)
+
+.Lbase_saved:
+ /* Save caller's ssp and base marker to jmp_buf */
+ REG_S t0, SSP_OFFSET(a0)
+ REG_S t2, SSP_BASE_OFFSET(a0)
+.Lfin:
+#endif
+
#if !IS_IN (libc) && IS_IN (rtld)
/* In ld.so we never save the signal mask. */
li a0, 0
new file mode 100644
@@ -0,0 +1,7 @@
+#include <setjmpP.h>
+#include <stddef.h>
+#undef __saved_mask
+
+--
+SSP_OFFSET offsetof(struct __jmp_buf_tag, __saved_mask.__saved.__ssp)
+SSP_BASE_OFFSET offsetof(struct __jmp_buf_tag, __saved_mask.__saved.__ssp_base)
new file mode 100644
@@ -0,0 +1,78 @@
+/* Internal header file for <setjmp.h>. Linux/risc-v version.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _SETJMPP_H
+#define _SETJMPP_H 1
+
+#include <bits/types/__sigset_t.h>
+#include <libc-pointer-arith.h>
+#include <sigsetops.h>
+
+/* Number of bits per long. */
+#define _JUMP_BUF_SIGSET_BITS_PER_WORD (8 * sizeof (unsigned long int))
+/* This holds the number of signals, 512 should be sufficient for future.
+ expansion */
+#define _JUMP_BUF_SIGSET_NSIG 512
+/* Number of longs to hold all signals. */
+#define _JUMP_BUF_SIGSET_NWORDS \
+ (ALIGN_UP (_JUMP_BUF_SIGSET_NSIG, _JUMP_BUF_SIGSET_BITS_PER_WORD) \
+ / _JUMP_BUF_SIGSET_BITS_PER_WORD)
+
+typedef struct
+ {
+ unsigned long int __val[_JUMP_BUF_SIGSET_NWORDS];
+ } __jmp_buf_sigset_t;
+
+typedef union
+ {
+ __sigset_t __saved_mask_compat;
+ struct
+ {
+ __jmp_buf_sigset_t __saved_mask;
+ /* Used for shadow stack pointer. NB: Shadow stack pointer
+ must have the same alignment as __saved_mask. Otherwise
+ offset of __saved_mask will be changed. */
+ unsigned long int __ssp;
+ unsigned long int __ssp_base;
+ } __saved;
+ } __jmpbuf_arch_t;
+
+/* <setjmp/setjmp.h> has
+
+ NB: We use setjmp in thread cancellation and this saves the shadow
+ stack register, but __libc_unwind_longjmp doesn't restore the shadow
+ stack register since cancellation never returns after longjmp. */
+#undef __sigset_t
+#define __sigset_t __jmpbuf_arch_t
+#include <setjmp.h>
+#undef __saved_mask
+#define __saved_mask __saved_mask.__saved.__saved_mask
+
+#include <signal.h>
+
+typedef struct
+ {
+ unsigned long int __val[__NSIG_WORDS];
+ } __sigprocmask_sigset_t;
+
+extern jmp_buf ___buf;
+extern __typeof (___buf[0].__saved_mask) ___saved_mask;
+_Static_assert (sizeof (___saved_mask) >= sizeof (__sigprocmask_sigset_t),
+ "size of ___saved_mask < size of __sigprocmask_sigset_t");
+
+#endif /* setjmpP.h */