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 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.
---
gcc/config/riscv/riscv-protos.h | 1 +
gcc/config/riscv/riscv.cc | 26 ++++++
gcc/config/riscv/riscv.md | 80 ++++++++++++++++++-
.../riscv/zicfilp-indirect-return.c | 30 +++++++
.../gcc.target/riscv/zicfilp-setjmp.c | 26 ++++++
5 files changed, 159 insertions(+), 4 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/riscv/zicfilp-indirect-return.c
create mode 100644 gcc/testsuite/gcc.target/riscv/zicfilp-setjmp.c
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index fa360157570..f429ca86a8f 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 ab50549d4e0..645796a681f 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, true, false, false, false, NULL, NULL},
};
static const scoped_attribute_specs riscv_gnu_attribute_table =
@@ -8076,6 +8080,28 @@ 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;
+
+ return lookup_attribute ("indirect_return", DECL_ATTRIBUTES (decl)) != 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 2055e5c4a9d..5231610b054 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 00000000000..230d865d126
--- /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 00000000000..9d90c14db63
--- /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);
+}
--
2.54.0