Without target patterns, the generic stack protector expansion moves the
canary through ordinary pseudos, which the register allocator is free to
spill or to keep live across the whole function. Describe the set and
the test as single, unsplittable insns that clear their scratch registers
on the way out, so that the canary value exists in a register for only
the few instructions that need it. This matches what riscv, sparc,
aarch64 and others already do.
__stack_chk_guard's address is not a valid memory address on Alpha, and
gentarget-def calls gen_stack_protect_set directly without checking the
expander's predicates, so the expanders legitimize it themselves. Only
the address is placed in a pseudo, never the canary.
gcc/ChangeLog:
* config/alpha/alpha.md (UNSPEC_SSP_SET, UNSPEC_SSP_TEST): New
unspecs.
(stack_protect_set, stack_protect_setdi): New patterns.
(stack_protect_test, stack_protect_testdi): Likewise.
gcc/testsuite/ChangeLog:
* gcc.target/alpha/stack-protector-1.c: New test.
---
gcc/config/alpha/alpha.md | 69 +++++++++++++++++++
.../gcc.target/alpha/stack-protector-1.c | 25 +++++++
2 files changed, 94 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/alpha/stack-protector-1.c
diff --git ./gcc/config/alpha/alpha.md ./gcc/config/alpha/alpha.md
index 2ca9dd65835..a0235c61325 100644
--- ./gcc/config/alpha/alpha.md
+++ ./gcc/config/alpha/alpha.md
@@ -58,6 +58,10 @@
UNSPEC_ATOMIC
UNSPEC_CMPXCHG
UNSPEC_XCHG
+
+ ;; Stack protector
+ UNSPEC_SSP_SET
+ UNSPEC_SSP_TEST
])
;; UNSPEC_VOLATILE:
@@ -3680,6 +3684,71 @@
"call_pal 0x81"
[(set_attr "type" "callpal")])
+;; Stack protector. The canary is read from __stack_chk_guard, whose
+;; address is not a valid memory address on its own, so legitimize it
+;; here; only the address, never the canary value itself, is allowed to
+;; live in a pseudo.
+
+(define_expand "stack_protect_set"
+ [(match_operand:DI 0 "memory_operand")
+ (match_operand:DI 1 "memory_operand")]
+ ""
+{
+ if (!memory_operand (operands[1], DImode))
+ operands[1] = replace_equiv_address (operands[1],
+ copy_addr_to_reg (XEXP (operands[1],
+ 0)));
+ emit_insn (gen_stack_protect_setdi (operands[0], operands[1]));
+ DONE;
+})
+
+;; DO NOT SPLIT THIS PATTERN. It is important for security reasons that
+;; the canary value does not live beyond the life of this sequence.
+(define_insn "stack_protect_setdi"
+ [(set (match_operand:DI 0 "memory_operand" "=m")
+ (unspec:DI [(match_operand:DI 1 "memory_operand" "m")]
+ UNSPEC_SSP_SET))
+ (set (match_scratch:DI 2 "=&r") (const_int 0))]
+ ""
+ "ldq %2,%1\;stq %2,%0\;bis $31,$31,%2"
+ [(set_attr "type" "multi")
+ (set_attr "length" "12")])
+
+(define_expand "stack_protect_test"
+ [(match_operand:DI 0 "memory_operand")
+ (match_operand:DI 1 "memory_operand")
+ (match_operand 2)]
+ ""
+{
+ rtx res, ops[4];
+
+ if (!memory_operand (operands[1], DImode))
+ operands[1] = replace_equiv_address (operands[1],
+ copy_addr_to_reg (XEXP (operands[1],
+ 0)));
+ res = gen_reg_rtx (DImode);
+ emit_insn (gen_stack_protect_testdi (res, operands[0], operands[1]));
+
+ ops[0] = gen_rtx_EQ (VOIDmode, res, const0_rtx);
+ ops[1] = res;
+ ops[2] = const0_rtx;
+ ops[3] = operands[2];
+ alpha_emit_conditional_branch (ops, DImode);
+ DONE;
+})
+
+;; DO NOT SPLIT THIS PATTERN, as above.
+(define_insn "stack_protect_testdi"
+ [(set (match_operand:DI 0 "register_operand" "=&r")
+ (unspec:DI [(match_operand:DI 1 "memory_operand" "m")
+ (match_operand:DI 2 "memory_operand" "m")]
+ UNSPEC_SSP_TEST))
+ (clobber (match_scratch:DI 3 "=&r"))]
+ ""
+ "ldq %0,%1\;ldq %3,%2\;xor %0,%3,%0\;bis $31,$31,%3"
+ [(set_attr "type" "multi")
+ (set_attr "length" "16")])
+
;; For userland, we load the thread pointer from the TCB.
;; For the kernel, we load the per-cpu private value.
diff --git ./gcc/testsuite/gcc.target/alpha/stack-protector-1.c
./gcc/testsuite/gcc.target/alpha/stack-protector-1.c
new file mode 100644
index 00000000000..9c7ab0c439f
--- /dev/null
+++ ./gcc/testsuite/gcc.target/alpha/stack-protector-1.c
@@ -0,0 +1,25 @@
+/* Check that -fstack-protector is accepted and that the canary is set from
+ __stack_chk_guard and tested before returning. The scratch registers used
+ to carry the canary must be cleared afterwards, so that its value is not
+ left recoverable in a register or in a spill slot. */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-protector-all" } */
+
+extern void g (char *);
+
+int
+f (void)
+{
+ char buf[64];
+ g (buf);
+ return 0;
+}
+
+/* { dg-final { scan-assembler "__stack_chk_guard" } } */
+/* { dg-final { scan-assembler "__stack_chk_fail" } } */
+/* { dg-final { scan-assembler
"\\sxor\\s\\\$\[0-9\]+,\\\$\[0-9\]+,\\\$\[0-9\]+\\s" } } */
+/* One scratch cleared by stack_protect_setdi, one by stack_protect_testdi.
+ The pattern deliberately does not match "bis $31,$31,$31", which is a nop
+ the scheduler may insert anywhere. */
+/* { dg-final { scan-assembler-times
"\\sbis\\s\\\$31,\\\$31,\\\$(\[0-9\]|1\[0-9\]|2\[0-9\]|30)\\n" 2 } } */
--
2.54.0