diff options
author | Monk Chiang <monk.chiang@sifive.com> | 2024-11-15 13:38:48 +0800 |
---|---|---|
committer | Monk Chiang <monk.chiang@sifive.com> | 2025-01-17 10:49:16 +0800 |
commit | dc76aa0e4d5398104b6b26f08b46524b97de5100 (patch) | |
tree | 7210fc03f9544ca682238548dd4768490651e16e /gcc | |
parent | 29da6a642402ac64002f5edeab268606b4637103 (diff) | |
download | gcc-dc76aa0e4d5398104b6b26f08b46524b97de5100.zip gcc-dc76aa0e4d5398104b6b26f08b46524b97de5100.tar.gz gcc-dc76aa0e4d5398104b6b26f08b46524b97de5100.tar.bz2 |
RISC-V: Add Zicfiss ISA extension.
This patch is implemented according to the RISC-V CFI specification.
It supports the generation of shadow stack instructions in the prologue,
epilogue, non-local gotos, and unwinding.
RISC-V CFI SPEC: https://github.com/riscv/riscv-cfi
gcc/ChangeLog:
* common/config/riscv/riscv-common.cc: Add ZICFISS ISA string.
* config/riscv/predicates.md: New predicate x1x5_operand.
* config/riscv/riscv.cc
(riscv_expand_prologue): Insert shadow stack instructions.
(riscv_expand_epilogue): Likewise.
(riscv_for_each_saved_reg): Assign t0 or ra register for
sspopchk instruction.
(need_shadow_stack_push_pop_p): New function. Omit shadow
stack operation on leaf function.
* config/riscv/riscv.h
(need_shadow_stack_push_pop_p): Define.
* config/riscv/riscv.md: Add shadow stack patterns.
(save_stack_nonlocal): Add shadow stack instructions for setjump.
(restore_stack_nonlocal): Add shadow stack instructions for longjump.
* config/riscv/riscv.opt (TARGET_ZICFISS): Define.
libgcc/ChangeLog:
* config/riscv/linux-unwind.h: Include shadow-stack-unwind.h.
* config/riscv/shadow-stack-unwind.h
(_Unwind_Frames_Extra): Define.
(_Unwind_Frames_Increment): Define.
gcc/testsuite/ChangeLog:
* gcc.target/riscv/ssp-1.c: New test.
* gcc.target/riscv/ssp-2.c: New test.
Co-Developed-by: Greg McGary <gkm@rivosinc.com>,
Kito Cheng <kito.cheng@gmail.com>
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/common/config/riscv/riscv-common.cc | 7 | ||||
-rw-r--r-- | gcc/config/riscv/predicates.md | 6 | ||||
-rw-r--r-- | gcc/config/riscv/riscv.cc | 58 | ||||
-rw-r--r-- | gcc/config/riscv/riscv.h | 1 | ||||
-rw-r--r-- | gcc/config/riscv/riscv.md | 125 | ||||
-rw-r--r-- | gcc/config/riscv/riscv.opt | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.target/riscv/ssp-1.c | 41 | ||||
-rw-r--r-- | gcc/testsuite/gcc.target/riscv/ssp-2.c | 10 |
8 files changed, 241 insertions, 9 deletions
diff --git a/gcc/common/config/riscv/riscv-common.cc b/gcc/common/config/riscv/riscv-common.cc index bfc8aa5..8e8b610 100644 --- a/gcc/common/config/riscv/riscv-common.cc +++ b/gcc/common/config/riscv/riscv-common.cc @@ -111,6 +111,9 @@ static const riscv_implied_info_t riscv_implied_info[] = {"zfinx", "zicsr"}, {"zdinx", "zicsr"}, + {"zicfiss", "zicsr"}, + {"zicfiss", "zimop"}, + {"zk", "zkn"}, {"zk", "zkr"}, {"zk", "zkt"}, @@ -325,6 +328,8 @@ static const struct riscv_ext_version riscv_ext_version_table[] = {"zicclsm", ISA_SPEC_CLASS_NONE, 1, 0}, {"ziccrse", ISA_SPEC_CLASS_NONE, 1, 0}, + {"zicfiss", ISA_SPEC_CLASS_NONE, 1, 0}, + {"zimop", ISA_SPEC_CLASS_NONE, 1, 0}, {"zcmop", ISA_SPEC_CLASS_NONE, 1, 0}, @@ -1647,6 +1652,8 @@ static const riscv_ext_flag_table_t riscv_ext_flag_table[] = RISCV_EXT_FLAG_ENTRY ("zicbop", x_riscv_zicmo_subext, MASK_ZICBOP), RISCV_EXT_FLAG_ENTRY ("zic64b", x_riscv_zicmo_subext, MASK_ZIC64B), + RISCV_EXT_FLAG_ENTRY ("zicfiss", x_riscv_zi_subext, MASK_ZICFISS), + RISCV_EXT_FLAG_ENTRY ("zimop", x_riscv_mop_subext, MASK_ZIMOP), RISCV_EXT_FLAG_ENTRY ("zcmop", x_riscv_mop_subext, MASK_ZCMOP), diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md index cda7502..1f67d30 100644 --- a/gcc/config/riscv/predicates.md +++ b/gcc/config/riscv/predicates.md @@ -679,3 +679,9 @@ return (riscv_symbolic_constant_p (op, &type) && type == SYMBOL_PCREL); }) + +;; Shadow stack operands only allow x1, x5 registers +(define_predicate "x1x5_operand" + (and (match_operand 0 "register_operand") + (match_test "REGNO (op) == RETURN_ADDR_REGNUM + || REGNO (op) == T0_REGNUM"))) diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index a03b35b..3fe6343 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -7496,6 +7496,9 @@ riscv_save_reg_p (unsigned int regno) if (regno == GP_REGNUM || regno == THREAD_POINTER_REGNUM) return false; + if (regno == RETURN_ADDR_REGNUM && TARGET_ZICFISS) + return true; + /* We must save every register used in this function. If this is not a leaf function, then we must save all temporary registers. */ if (df_regs_ever_live_p (regno) @@ -8049,7 +8052,7 @@ riscv_is_eh_return_data_register (unsigned int regno) static void riscv_for_each_saved_reg (poly_int64 sp_offset, riscv_save_restore_fn fn, - bool epilogue, bool maybe_eh_return) + bool epilogue, bool maybe_eh_return, bool sibcall_p) { HOST_WIDE_INT offset, first_fp_offset; unsigned int regno, num_masked_fp = 0; @@ -8135,7 +8138,14 @@ riscv_for_each_saved_reg (poly_int64 sp_offset, riscv_save_restore_fn fn, } } - riscv_save_restore_reg (word_mode, regno, offset, fn); + if (need_shadow_stack_push_pop_p () && epilogue && !sibcall_p + && !(maybe_eh_return && crtl->calls_eh_return) + && (regno == RETURN_ADDR_REGNUM) + && !cfun->machine->interrupt_handler_p) + riscv_save_restore_reg (word_mode, RISCV_PROLOGUE_TEMP_REGNUM, + offset, fn); + else + riscv_save_restore_reg (word_mode, regno, offset, fn); } /* This loop must iterate over the same space as its companion in @@ -8729,6 +8739,9 @@ riscv_expand_prologue (void) if (cfun->machine->naked_p) return; + if (need_shadow_stack_push_pop_p ()) + emit_insn (gen_sspush (Pmode, gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM))); + /* prefer multi-push to save-restore libcall. */ if (riscv_use_multi_push (frame)) { @@ -8772,7 +8785,7 @@ riscv_expand_prologue (void) = get_multi_push_fpr_mask (multi_push_additional / UNITS_PER_WORD); frame->fmask &= mask_fprs_push; riscv_for_each_saved_reg (remaining_size, riscv_save_reg, false, - false); + false, false); frame->fmask = fmask & ~mask_fprs_push; /* mask for the rest FPRs. */ } } @@ -8823,7 +8836,8 @@ riscv_expand_prologue (void) GEN_INT (-step1)); RTX_FRAME_RELATED_P (emit_insn (insn)) = 1; } - riscv_for_each_saved_reg (remaining_size, riscv_save_reg, false, false); + riscv_for_each_saved_reg (remaining_size, riscv_save_reg, + false, false, false); } /* Undo the above fib. */ @@ -8985,6 +8999,7 @@ riscv_expand_epilogue (int style) = use_multi_pop ? frame->multi_push_adj_base + frame->multi_push_adj_addi : 0; rtx ra = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM); + rtx t0 = gen_rtx_REG (Pmode, RISCV_PROLOGUE_TEMP_REGNUM); unsigned th_int_mask = 0; rtx insn; @@ -9194,7 +9209,8 @@ riscv_expand_epilogue (int style) riscv_for_each_saved_v_reg (step2, riscv_restore_reg, false); riscv_for_each_saved_reg (frame->total_size - step2 - libcall_size - multipop_size, - riscv_restore_reg, true, style == EXCEPTION_RETURN); + riscv_restore_reg, true, style == EXCEPTION_RETURN, + style == SIBCALL_RETURN); if (th_int_mask && TH_INT_INTERRUPT (cfun)) { @@ -9234,7 +9250,8 @@ riscv_expand_epilogue (int style) riscv_for_each_saved_reg (frame->total_size - libcall_size - multipop_size, riscv_restore_reg, true, - style == EXCEPTION_RETURN); + style == EXCEPTION_RETURN, false); + /* Undo the above fib. */ frame->mask = mask; frame->fmask = fmask; @@ -9259,6 +9276,17 @@ riscv_expand_epilogue (int style) emit_insn (gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx, EH_RETURN_STACKADJ_RTX)); + if (need_shadow_stack_push_pop_p () + && !((style == EXCEPTION_RETURN) && crtl->calls_eh_return)) + { + if (BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM) + && style != SIBCALL_RETURN + && !cfun->machine->interrupt_handler_p) + emit_insn (gen_sspopchk (Pmode, t0)); + else + emit_insn (gen_sspopchk (Pmode, ra)); + } + /* Return from interrupt. */ if (cfun->machine->interrupt_handler_p) { @@ -9276,7 +9304,15 @@ riscv_expand_epilogue (int style) emit_jump_insn (gen_riscv_uret ()); } else if (style != SIBCALL_RETURN) - emit_jump_insn (gen_simple_return_internal (ra)); + { + if (need_shadow_stack_push_pop_p () + && !((style == EXCEPTION_RETURN) && crtl->calls_eh_return) + && BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM) + && !cfun->machine->interrupt_handler_p) + emit_jump_insn (gen_simple_return_internal (t0)); + else + emit_jump_insn (gen_simple_return_internal (ra)); + } } /* Implement EPILOGUE_USES. */ @@ -9473,7 +9509,8 @@ bool riscv_can_use_return_insn (void) { return (reload_completed && known_eq (cfun->machine->frame.total_size, 0) - && ! cfun->machine->interrupt_handler_p); + && ! cfun->machine->interrupt_handler_p + && ! need_shadow_stack_push_pop_p ()); } /* Given that there exists at least one variable that is set (produced) @@ -13785,6 +13822,11 @@ expand_reversed_crc_using_clmul (scalar_mode crc_mode, scalar_mode data_mode, riscv_emit_move (operands[0], gen_lowpart (crc_mode, a0)); } +bool need_shadow_stack_push_pop_p () +{ + return TARGET_ZICFISS && riscv_save_return_addr_reg_p (); +} + /* Initialize the GCC target structure. */ #undef TARGET_ASM_ALIGNED_HI_OP #define TARGET_ASM_ALIGNED_HI_OP "\t.half\t" diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h index 4ff883a..93e88fe 100644 --- a/gcc/config/riscv/riscv.h +++ b/gcc/config/riscv/riscv.h @@ -1189,6 +1189,7 @@ extern poly_int64 riscv_v_adjust_nunits (enum machine_mode, int); extern poly_int64 riscv_v_adjust_nunits (machine_mode, bool, int, int); extern poly_int64 riscv_v_adjust_precision (enum machine_mode, int); extern poly_int64 riscv_v_adjust_bytesize (enum machine_mode, int); +extern bool need_shadow_stack_push_pop_p (); /* The number of bits and bytes in a RVV vector. */ #define BITS_PER_RISCV_VECTOR (poly_uint16 (riscv_vector_chunks * riscv_bytes_per_vector_chunk * 8)) #define BYTES_PER_RISCV_VECTOR (poly_uint16 (riscv_vector_chunks * riscv_bytes_per_vector_chunk)) diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md index fd98e0a..eeaa908 100644 --- a/gcc/config/riscv/riscv.md +++ b/gcc/config/riscv/riscv.md @@ -137,6 +137,12 @@ ;; Zihintpause unspec UNSPECV_PAUSE + ;; ZICFISS + UNSPECV_SSPUSH + UNSPECV_SSPOPCHK + UNSPECV_SSRDP + UNSPECV_SSP + ;; XTheadInt unspec UNSPECV_XTHEADINT_PUSH UNSPECV_XTHEADINT_POP @@ -4111,6 +4117,30 @@ (set_attr "length" "0")] ) +(define_expand "save_stack_nonlocal" + [(set (match_operand 0 "memory_operand") + (match_operand 1 "register_operand"))] + "" +{ + rtx stack_slot; + + if (need_shadow_stack_push_pop_p ()) + { + /* Copy shadow stack pointer to the first slot + and stack pointer to the second slot. */ + rtx ssp_slot = adjust_address (operands[0], word_mode, 0); + stack_slot = adjust_address (operands[0], Pmode, UNITS_PER_WORD); + + rtx reg_ssp = force_reg (word_mode, const0_rtx); + emit_insn (gen_ssrdp (word_mode, reg_ssp)); + emit_move_insn (ssp_slot, reg_ssp); + } + else + stack_slot = adjust_address (operands[0], Pmode, 0); + emit_move_insn (stack_slot, operands[1]); + DONE; +}) + ;; This fixes a failure with gcc.c-torture/execute/pr64242.c at -O2 for a ;; 32-bit target when using -mtune=sifive-7-series. The first sched pass ;; runs before register elimination, and we have a non-obvious dependency @@ -4121,7 +4151,70 @@ (match_operand 1 "memory_operand")] "" { - emit_move_insn (operands[0], operands[1]); + rtx stack_slot; + + if (need_shadow_stack_push_pop_p ()) + { + rtx t0 = gen_rtx_REG (Pmode, RISCV_PROLOGUE_TEMP_REGNUM); + /* Restore shadow stack pointer from the first slot + and stack pointer from the second slot. */ + rtx ssp_slot = adjust_address (operands[1], word_mode, 0); + stack_slot = adjust_address (operands[1], Pmode, UNITS_PER_WORD); + + /* Get the current shadow stack pointer. */ + rtx cur_ssp = force_reg (word_mode, const0_rtx); + emit_insn (gen_ssrdp (word_mode, cur_ssp)); + + /* Compare and jump over adjustment code. */ + rtx noadj_label = gen_label_rtx (); + emit_cmp_and_jump_insns (cur_ssp, const0_rtx, EQ, NULL_RTX, + word_mode, 1, noadj_label); + + rtx loop_label = gen_label_rtx (); + emit_label (loop_label); + LABEL_NUSES (loop_label) = 1; + + /* Check if current ssp less than jump buffer ssp, + so no loop is needed. */ + emit_cmp_and_jump_insns (ssp_slot, cur_ssp, LE, NULL_RTX, + ptr_mode, 1, noadj_label); + + /* Advance by a maximum of 4K at a time to avoid unwinding + past bounds of the shadow stack. */ + rtx reg_4096 = force_reg (word_mode, GEN_INT (4096)); + rtx cmp_ssp = gen_reg_rtx (word_mode); + cmp_ssp = expand_simple_binop (ptr_mode, MINUS, + ssp_slot, cur_ssp, + cmp_ssp, 1, OPTAB_DIRECT); + + /* Update curr_ssp from jump buffer ssp. */ + emit_move_insn (cur_ssp, ssp_slot); + emit_insn (gen_write_ssp (word_mode, cur_ssp)); + emit_jump_insn (gen_jump (loop_label)); + emit_barrier (); + + /* Adjust the ssp in a loop. */ + rtx cmp_4k_label = gen_label_rtx (); + emit_label (cmp_4k_label); + LABEL_NUSES (cmp_4k_label) = 1; + + /* Add 4k for curr_ssp. */ + cur_ssp = expand_simple_binop (ptr_mode, PLUS, + cur_ssp, reg_4096, + cur_ssp, 1, OPTAB_DIRECT); + emit_insn (gen_write_ssp (word_mode, cur_ssp)); + emit_insn (gen_sspush (Pmode, t0)); + emit_insn (gen_sspopchk (Pmode, t0)); + emit_jump_insn (gen_jump (loop_label)); + emit_barrier (); + + emit_label (noadj_label); + LABEL_NUSES (noadj_label) = 1; + } + else + stack_slot = adjust_address (operands[1], Pmode, 0); + + emit_move_insn (operands[0], stack_slot); /* Prevent the following hard fp restore from being moved before the move insn above which uses a copy of the soft fp reg. */ emit_clobber (gen_rtx_MEM (BLKmode, hard_frame_pointer_rtx)); @@ -4589,6 +4682,36 @@ }" [(set_attr "type" "arith")]) +;; Shadow stack + +(define_insn "@sspush<mode>" + [(unspec_volatile [(match_operand:P 0 "x1x5_operand" "r")] UNSPECV_SSPUSH)] + "TARGET_ZICFISS" + "sspush\t%0" + [(set_attr "type" "arith") + (set_attr "mode" "<MODE>")]) + +(define_insn "@sspopchk<mode>" + [(unspec_volatile [(match_operand:P 0 "x1x5_operand" "r")] UNSPECV_SSPOPCHK)] + "TARGET_ZICFISS" + "sspopchk\t%0" + [(set_attr "type" "arith") + (set_attr "mode" "<MODE>")]) + +(define_insn "@ssrdp<mode>" + [(set (match_operand:P 0 "register_operand" "=r") + (unspec_volatile [(const_int 0)] UNSPECV_SSRDP))] + "TARGET_ZICFISS" + "ssrdp\t%0" + [(set_attr "type" "arith") + (set_attr "mode" "<MODE>")]) + +(define_insn "@write_ssp<mode>" + [(unspec_volatile [(match_operand:P 0 "register_operand" "r")] UNSPECV_SSP)] + "TARGET_ZICFISS" + "csrw\tssp, %0" + [(set_attr "type" "arith") + (set_attr "mode" "<MODE>")]) (include "bitmanip.md") (include "crypto.md") diff --git a/gcc/config/riscv/riscv.opt b/gcc/config/riscv/riscv.opt index 88fec1c..4a43bfd 100644 --- a/gcc/config/riscv/riscv.opt +++ b/gcc/config/riscv/riscv.opt @@ -253,6 +253,8 @@ Mask(ZICCLSM) Var(riscv_zi_subext) Mask(ZICCRSE) Var(riscv_zi_subext) +Mask(ZICFISS) Var(riscv_zi_subext) + TargetVariable int riscv_za_subext diff --git a/gcc/testsuite/gcc.target/riscv/ssp-1.c b/gcc/testsuite/gcc.target/riscv/ssp-1.c new file mode 100644 index 0000000..abf47ec --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/ssp-1.c @@ -0,0 +1,41 @@ +/* { dg-do compile { target { riscv64*-*-* } } } */ +/* { dg-options "-O2 -march=rv64gc_zicfiss -mabi=lp64d" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" } } */ +struct ad { + void *ae; +}; +struct af { + union { + int *ai; + int *aj; + struct ad *ak; + } u; + struct { + struct { + long al : 1; + long am : 1; + long : 21; + } b; + long i; + } s; +}; +void fdes (struct af *, void *, long *); + +void foo (struct af *bv, long *bw) { + bw[0] = bw[1] = 0; + if (bv->s.b.al) + fdes (bv, bv->u.ak->ae, bw); + else if (bv->s.b.am) { + int **p = (int**)bv->u.aj; + for (; *p; ++p) + fdes (bv, *p, bw); + } else + fdes (bv, bv->u.ai, bw); +} + +/* { dg-final { scan-assembler-times "ld\tt0" 1 } } */ +/* { dg-final { scan-assembler-times "sspopchk\tt0" 1 } } */ +/* { dg-final { scan-assembler-times "jr\tt0" 1 } } */ +/* { dg-final { scan-assembler-times "ld\tra" 2 } } */ +/* { dg-final { scan-assembler-times "sspopchk\tra" 2 } } */ +/* { dg-final { scan-assembler-times "tail" 2 } } */ diff --git a/gcc/testsuite/gcc.target/riscv/ssp-2.c b/gcc/testsuite/gcc.target/riscv/ssp-2.c new file mode 100644 index 0000000..7c60983 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/ssp-2.c @@ -0,0 +1,10 @@ +/* { dg-do compile { target { riscv64*-*-* } } } */ +/* { dg-options "-O0 -march=rv64gc_zicfiss -mabi=lp64d" } */ + +void __attribute__ ((interrupt)) +foo (void) +{ +} +/* { dg-final { scan-assembler-times "sd\tra" 1 } } */ +/* { dg-final { scan-assembler-times "ld\tra" 1 } } */ +/* { dg-final { scan-assembler-times "sspopchk\tra" 1 } } */ |