https://gcc.gnu.org/g:db8f7949a6b074d7bdd1bf29801dd11eb967973f
commit r17-1557-gdb8f7949a6b074d7bdd1bf29801dd11eb967973f Author: Monk Chiang <[email protected]> Date: Tue Jun 9 18:34:44 2026 -0700 RISC-V: Add Zicfilp LPAD protection for setjmp and indirect_return Add call-site LPAD insertion for two cases: 1. setjmp / __attribute__((returns_twice)) calls, which may return a second time via longjmp. 2. A new "indirect_return" attribute for functions that may return to an unexpected address. Detection uses riscv_call_needs_lpad_p() at expand time. When needed, call_internal_cfi / call_value_internal_cfi emit .p2align 2, .option push/norelax/norvc, the call, .option pop, and lpad 0 as a single insn. This prevents the assembler (c.jal) or linker (jal relaxation) from shifting the return address off the lpad. Indirect calls to returns_twice or indirect_return functions are not covered. Changes in v4: - Revert the v2 change, make indirect_return a type attribute. Changes in v3: - Fix coding style in riscv.cc Changes in v2: - Change indirect_return from a type attribute to a decl attribute, consistent with returns_twice. - Add length attributes to call_internal_cfi and call_value_internal_cfi to reflect the multi-instruction sequence. - Copy the explaining comment to call_value_internal_cfi. gcc/ChangeLog: * config/riscv/riscv-protos.h (riscv_call_needs_lpad_p): Declare. * config/riscv/riscv.cc (riscv_gnu_attributes): Register new indirect_return attribute for function declarations. (riscv_call_needs_lpad_p): New function. * config/riscv/riscv.md (call_internal_cfi): New insn pattern with length attribute. (call_value_internal_cfi): Likewise for call-with-return-value, with comment and length attribute. (define_expand "call"): Emit call_internal_cfi when riscv_call_needs_lpad_p returns true. (define_expand "call_value"): Likewise. gcc/testsuite/ChangeLog: * gcc.target/riscv/zicfilp-indirect-return.c: New test. * gcc.target/riscv/zicfilp-setjmp.c: New test. Diff: --- gcc/config/riscv/riscv-protos.h | 1 + gcc/config/riscv/riscv.cc | 27 ++++++++ gcc/config/riscv/riscv.md | 80 ++++++++++++++++++++-- .../gcc.target/riscv/zicfilp-indirect-return.c | 30 ++++++++ gcc/testsuite/gcc.target/riscv/zicfilp-setjmp.c | 26 +++++++ 5 files changed, 160 insertions(+), 4 deletions(-) diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h index fa360157570a..f429ca86a8fd 100644 --- a/gcc/config/riscv/riscv-protos.h +++ b/gcc/config/riscv/riscv-protos.h @@ -156,6 +156,7 @@ extern rtx riscv_emit_binary (enum rtx_code code, rtx dest, rtx x, rtx y); #endif extern bool riscv_expand_conditional_move (rtx, rtx, rtx, rtx); extern rtx riscv_legitimize_call_address (rtx); +extern bool riscv_call_needs_lpad_p (rtx); extern bool riscv_expand_zilsd_misaligned_move (rtx, rtx); extern bool riscv_zilsd_valid_mem_p (rtx, machine_mode); extern void riscv_set_return_address (rtx, rtx); diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index ab50549d4e0b..b1bdbf1a9ccb 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -1030,6 +1030,10 @@ static const attribute_spec riscv_gnu_attributes[] = standard vector calling convention variant. Syntax: __attribute__((norelax)). */ {"norelax", 0, 0, true, false, false, false, NULL, NULL}, + /* Marks functions that may return indirectly. With Zicfilp, the compiler + inserts a landing-pad after calls to such functions. Syntax: + __attribute__ ((indirect_return)). */ + {"indirect_return", 0, 0, false, true, true, true, NULL, NULL}, }; static const scoped_attribute_specs riscv_gnu_attribute_table = @@ -8076,6 +8080,29 @@ riscv_legitimize_call_address (rtx addr) return addr; } +/* Return true if a Zicfilp landing-pad must follow a call to ADDR. + Indirect calls to returns_twice or indirect_return functions are not + covered. */ + +bool +riscv_call_needs_lpad_p (rtx addr) +{ + if (!is_zicfilp_p () || GET_CODE (addr) != SYMBOL_REF) + return false; + + tree decl = SYMBOL_REF_DECL (addr); + if (!decl || TREE_CODE (decl) != FUNCTION_DECL) + return false; + + /* Use setjmp_call_p to cover both ECF_RETURNS_TWICE builtins (e.g. the + C-library setjmp) and explicit __attribute__ ((returns_twice)). */ + if (setjmp_call_p (decl)) + return true; + + tree fntype = TREE_TYPE (decl); + return lookup_attribute ("indirect_return", TYPE_ATTRIBUTES (fntype)) != NULL; +} + /* Print symbolic operand OP, which is part of a HIGH or LO_SUM in context CONTEXT. HI_RELOC indicates a high-part reloc. */ diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md index 2055e5c4a9dc..5231610b0540 100644 --- a/gcc/config/riscv/riscv.md +++ b/gcc/config/riscv/riscv.md @@ -4190,8 +4190,12 @@ (use (match_operand 2 ""))])] "" { - rtx target = riscv_legitimize_call_address (XEXP (operands[0], 0)); - emit_call_insn (gen_call_internal (target, operands[1])); + rtx addr = XEXP (operands[0], 0); + rtx target = riscv_legitimize_call_address (addr); + if (riscv_call_needs_lpad_p (addr)) + emit_call_insn (gen_call_internal_cfi (target, operands[1])); + else + emit_call_insn (gen_call_internal (target, operands[1])); DONE; }) @@ -4206,6 +4210,37 @@ call\t%0@plt" [(set_attr "type" "call")]) +;; Zicfilp-protected call: .option push/pop guards prevent c.jal compression +;; and jal linker relaxation from moving the return address off the lpad. +;; .p2align 2 ensures the lpad is 4-byte aligned. +(define_insn "call_internal_cfi" + [(call (mem:SI (match_operand 0 "call_insn_operand" "l,S,U")) + (match_operand 1 "" "")) + (clobber (reg:SI RETURN_ADDR_REGNUM))] + "TARGET_ZICFILP" + { + output_asm_insn (".p2align\t2", operands); + output_asm_insn (".option push", operands); + output_asm_insn (".option norelax", operands); + output_asm_insn (".option norvc", operands); + switch (which_alternative) + { + case 0: + output_asm_insn ("jalr\t%0", operands); + break; + case 1: + output_asm_insn ("call\t%0", operands); + break; + default: + output_asm_insn ("call\t%0@plt", operands); + break; + } + output_asm_insn (".option pop", operands); + return "lpad\t0"; + } + [(set_attr "type" "call") + (set_attr "length" "8,12,12")]) + (define_expand "call_value" [(parallel [(set (match_operand 0 "") (call (match_operand 1 "") @@ -4213,8 +4248,13 @@ (use (match_operand 3 ""))])] "" { - rtx target = riscv_legitimize_call_address (XEXP (operands[1], 0)); - emit_call_insn (gen_call_value_internal (operands[0], target, operands[2])); + rtx addr = XEXP (operands[1], 0); + rtx target = riscv_legitimize_call_address (addr); + if (riscv_call_needs_lpad_p (addr)) + emit_call_insn (gen_call_value_internal_cfi (operands[0], target, + operands[2])); + else + emit_call_insn (gen_call_value_internal (operands[0], target, operands[2])); DONE; }) @@ -4230,6 +4270,38 @@ call\t%1@plt" [(set_attr "type" "call")]) +;; Zicfilp-protected call: .option push/pop guards prevent c.jal compression +;; and jal linker relaxation from moving the return address off the lpad. +;; .p2align 2 ensures the lpad is 4-byte aligned. +(define_insn "call_value_internal_cfi" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand 1 "call_insn_operand" "l,S,U")) + (match_operand 2 "" ""))) + (clobber (reg:SI RETURN_ADDR_REGNUM))] + "TARGET_ZICFILP" + { + output_asm_insn (".p2align\t2", operands); + output_asm_insn (".option push", operands); + output_asm_insn (".option norelax", operands); + output_asm_insn (".option norvc", operands); + switch (which_alternative) + { + case 0: + output_asm_insn ("jalr\t%1", operands); + break; + case 1: + output_asm_insn ("call\t%1", operands); + break; + default: + output_asm_insn ("call\t%1@plt", operands); + break; + } + output_asm_insn (".option pop", operands); + return "lpad\t0"; + } + [(set_attr "type" "call") + (set_attr "length" "8,12,12")]) + ;; Call subroutine returning any type. (define_expand "untyped_call" diff --git a/gcc/testsuite/gcc.target/riscv/zicfilp-indirect-return.c b/gcc/testsuite/gcc.target/riscv/zicfilp-indirect-return.c new file mode 100644 index 000000000000..230d865d1268 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/zicfilp-indirect-return.c @@ -0,0 +1,30 @@ +/* Test RISC-V Zicfilp indirect_return attribute functionality. */ +/* { dg-do compile { target { riscv64*-*-* } } } */ +/* { dg-options "-O2 -march=rv64gc_zicfilp -mabi=lp64d -fcf-protection=none -fcf-protection=branch -fno-inline" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-g" "-flto" } } */ +/* { dg-final { check-function-bodies "**" "" {\.p2align|\.option|call|lpad} } } */ + +/* Function marked with indirect_return attribute. */ +int __attribute__((indirect_return, noinline)) indirect_func (int x) +{ + return x * 2 + 1; +} + +/* +** test_indirect_call: +** ... +** .p2align 2 +** .option push +** .option norelax +** .option norvc +** call indirect_func.* +** .option pop +** lpad \d+ +** ... +*/ +int test_indirect_call (int x) +{ + /* Use the result to prevent tail call optimization. */ + int result = indirect_func (x); + return result + 1; +} diff --git a/gcc/testsuite/gcc.target/riscv/zicfilp-setjmp.c b/gcc/testsuite/gcc.target/riscv/zicfilp-setjmp.c new file mode 100644 index 000000000000..9d90c14db63c --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/zicfilp-setjmp.c @@ -0,0 +1,26 @@ +/* Test RISC-V Zicfilp setjmp call protection functionality. */ +/* { dg-do compile { target { riscv64*-*-* } } } */ +/* { dg-options "-O2 -march=rv64gc_zicfilp -mabi=lp64d -fcf-protection=none -fcf-protection=branch" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-g" "-flto" } } */ +/* { dg-final { check-function-bodies "**" "" {\.p2align|\.option|call|lpad} } } */ + +#include <setjmp.h> + +jmp_buf test_env; + +/* +** test_setjmp_call: +** ... +** .p2align 2 +** .option push +** .option norelax +** .option norvc +** call _?setjmp.* +** .option pop +** lpad \d+ +** ... +*/ +int test_setjmp_call (void) +{ + return setjmp (test_env); +}
