Committed as r16-5192-g0cd1f03939d and also mentioned in www docs in
commit 87741e98fd7b6ecc5b51aff9be2629e147d01724.
On Thu, Jul 10, 2025 at 04:16:44PM +0200, Stefan Schulze Frielinghaus wrote:
> So far only a per thread canary in the TLS block is supported. This
> patch adds support for a global canary, too. For this the new option
> -mstack-protector-guard={global,tls} is added which defaults to tls.
>
> The global canary is expected at symbol __stack_chk_guard which means
> for a function prologue instructions larl/l(g)fr + mvc are emitted and
> for an epilogue larl/l(g)fr + clc.
>
> Furthermore, option -mstack-protector-guard-record is added which is
> inspired by -mrecord-mcount and generates section __stack_protector_loc
> containing pointers to all instructions which load the address of the
> global guard. Thus, this option has only an effect in conjunction with
> -mstack-protector-guard=global. The intended use is for the Linux
> kernel in order to support run-time patching. In each task_struct of
> the kernel a canary is held which will be copied into the lowcore.
> Since the kernel supports migration of the lowcore, addresses are not
> necessarily constant. Therefore, the kernel expects that all
> instructions loading the address of the canary to be of format RIL or
> more precisely are either larl or lgrl and that the instructions
> addresses are recorded in section __stack_protector_loc. The kernel is
> then required to patch those instructions e.g. to llilf, prior first
> execution or whenever the lowcore moves.
>
> In total this means -mstack-protector-guard=global emits code suitable
> for user and kernel space.
>
> gcc/ChangeLog:
>
> * config/s390/s390-opts.h (enum stack_protector_guard): Define
> SP_TLS and SP_GLOBAL.
> * config/s390/s390.h (TARGET_SP_GLOBAL_GUARD): Define predicate.
> (TARGET_SP_TLS_GUARD): Define predicate.
> * config/s390/s390.md (stack_protect_global_guard_addr<mode>):
> New insn.
> (stack_protect_set): Also deal with a global guard.
> (stack_protect_test): Also deal with a global guard.
> * config/s390/s390.opt (-mstack-protector-guard={global,tls}):
> New option.
> (-mstack-protector-guard-record) New option.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.target/s390/stack-protector-guard-global-1.c: New test.
> * gcc.target/s390/stack-protector-guard-global-2.c: New test.
> * gcc.target/s390/stack-protector-guard-global-3.c: New test.
> * gcc.target/s390/stack-protector-guard-global-4.c: New test.
> ---
> gcc/config/s390/s390-opts.h | 8 ++
> gcc/config/s390/s390.h | 3 +
> gcc/config/s390/s390.md | 87 +++++++++++++++----
> gcc/config/s390/s390.opt | 18 ++++
> .../s390/stack-protector-guard-global-1.c | 27 ++++++
> .../s390/stack-protector-guard-global-2.c | 5 ++
> .../s390/stack-protector-guard-global-3.c | 6 ++
> .../s390/stack-protector-guard-global-4.c | 6 ++
> 8 files changed, 144 insertions(+), 16 deletions(-)
> create mode 100644
> gcc/testsuite/gcc.target/s390/stack-protector-guard-global-1.c
> create mode 100644
> gcc/testsuite/gcc.target/s390/stack-protector-guard-global-2.c
> create mode 100644
> gcc/testsuite/gcc.target/s390/stack-protector-guard-global-3.c
> create mode 100644
> gcc/testsuite/gcc.target/s390/stack-protector-guard-global-4.c
>
> diff --git a/gcc/config/s390/s390-opts.h b/gcc/config/s390/s390-opts.h
> index 9cacb2c29d1..29dd4a5f77f 100644
> --- a/gcc/config/s390/s390-opts.h
> +++ b/gcc/config/s390/s390-opts.h
> @@ -53,4 +53,12 @@ enum indirect_branch {
> indirect_branch_thunk_inline,
> indirect_branch_thunk_extern
> };
> +
> +
> +/* Where to get the canary for the stack protector. */
> +enum stack_protector_guard
> +{
> + SP_TLS, /* per-thread canary in TLS block */
> + SP_GLOBAL /* global canary */
> +};
> #endif
> diff --git a/gcc/config/s390/s390.h b/gcc/config/s390/s390.h
> index 8b04bc9a755..2631788df4c 100644
> --- a/gcc/config/s390/s390.h
> +++ b/gcc/config/s390/s390.h
> @@ -251,6 +251,9 @@ enum processor_flags
> && (s390_tune < PROCESSOR_2964_Z13 || (VAL) != const0_rtx)
> \
> && (!CONST_INT_P (LEN) || INTVAL ((LEN)) >
> TARGET_SETMEM_PREFETCH_DISTANCE))
>
> +#define TARGET_SP_GLOBAL_GUARD (s390_stack_protector_guard == SP_GLOBAL)
> +#define TARGET_SP_TLS_GUARD (s390_stack_protector_guard == SP_TLS)
> +
> /* Run-time target specification. */
>
> /* Defaults for option flags defined only on some subtargets. */
> diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md
> index 02bc149b0fb..2d3027e1574 100644
> --- a/gcc/config/s390/s390.md
> +++ b/gcc/config/s390/s390.md
> @@ -311,6 +311,7 @@
>
> ; Stack Protector
> UNSPECV_SP_GET_TP
> + UNSPECV_SP_GLOBAL_GUARD_ADDR
> ])
>
> ;;
> @@ -11930,6 +11931,36 @@
> ; Stack Protector Patterns
> ;
>
> +(define_insn "stack_protect_global_guard_addr<mode>"
> + [(set (match_operand:P 0 "register_operand" "=d")
> + (unspec_volatile:P [(const_int 0)] UNSPECV_SP_GLOBAL_GUARD_ADDR))]
> + ""
> +{
> + if (flag_s390_stack_protector_guard_record)
> + fprintf (asm_out_file, "1:\n");
> + if (flag_pic)
> + {
> + if (TARGET_Z10)
> + output_asm_insn ("l<g>rl\t%0,__stack_chk_guard@GOTENT", operands);
> + else
> + {
> + output_asm_insn ("larl\t%0,__stack_chk_guard@GOTENT", operands);
> + output_asm_insn ("l<g>\t%0,0(%0)", operands);
> + }
> + }
> + else
> + output_asm_insn ("larl\t%0,__stack_chk_guard", operands);
> + if (flag_s390_stack_protector_guard_record)
> + fprintf (asm_out_file, "\t.section
> __stack_protector_loc,\\"a\\",@progbits\n"
> + "\t.%s 1b\n"
> + "\t.previous\n", TARGET_64BIT ? "quad" : "long");
> + return "";
> +}
> + [(set (attr "mnemonic")
> + (cond [(match_test "flag_pic && TARGET_Z10") (const_string "l<g>rl")
> + (match_test "flag_pic && !TARGET_Z10") (const_string "*")]
> + (const_string "larl")))])
> +
> ; Insns stack_protect_get_tp{si,di} are similar to *get_tp_{31,64} but still
> ; distinct in the sense that they force recomputation of the thread pointer
> ; instead of potentially reloading it from stack.
> @@ -11958,16 +11989,28 @@
> (match_operand 1 "memory_operand" ""))]
> ""
> {
> -#ifdef TARGET_THREAD_SSP_OFFSET
> - rtx tp = gen_reg_rtx (Pmode);
> - if (TARGET_64BIT)
> - emit_insn (gen_stack_protect_get_tpdi (tp));
> + if (TARGET_SP_GLOBAL_GUARD)
> + {
> + rtx addr = gen_reg_rtx (Pmode);
> + if (TARGET_64BIT)
> + emit_insn (gen_stack_protect_global_guard_addrdi (addr));
> + else
> + emit_insn (gen_stack_protect_global_guard_addrsi (addr));
> + operands[1] = gen_rtx_MEM (Pmode, addr);
> + }
> else
> - emit_insn (gen_stack_protect_get_tpsi (tp));
> - operands[1]
> - = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, tp,
> - GEN_INT (TARGET_THREAD_SSP_OFFSET)));
> + {
> +#ifdef TARGET_THREAD_SSP_OFFSET
> + rtx tp = gen_reg_rtx (Pmode);
> + if (TARGET_64BIT)
> + emit_insn (gen_stack_protect_get_tpdi (tp));
> + else
> + emit_insn (gen_stack_protect_get_tpsi (tp));
> + operands[1]
> + = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, tp,
> + GEN_INT
> (TARGET_THREAD_SSP_OFFSET)));
> #endif
> + }
> if (TARGET_64BIT)
> emit_insn (gen_stack_protect_setdi (operands[0], operands[1]));
> else
> @@ -11991,16 +12034,28 @@
> ""
> {
> rtx cc_reg, test;
> -#ifdef TARGET_THREAD_SSP_OFFSET
> - rtx tp = gen_reg_rtx (Pmode);
> - if (TARGET_64BIT)
> - emit_insn (gen_stack_protect_get_tpdi (tp));
> + if (TARGET_SP_GLOBAL_GUARD)
> + {
> + rtx addr = gen_reg_rtx (Pmode);
> + if (TARGET_64BIT)
> + emit_insn (gen_stack_protect_global_guard_addrdi (addr));
> + else
> + emit_insn (gen_stack_protect_global_guard_addrsi (addr));
> + operands[1] = gen_rtx_MEM (Pmode, addr);
> + }
> else
> - emit_insn (gen_stack_protect_get_tpsi (tp));
> - operands[1]
> - = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, tp,
> - GEN_INT (TARGET_THREAD_SSP_OFFSET)));
> + {
> +#ifdef TARGET_THREAD_SSP_OFFSET
> + rtx tp = gen_reg_rtx (Pmode);
> + if (TARGET_64BIT)
> + emit_insn (gen_stack_protect_get_tpdi (tp));
> + else
> + emit_insn (gen_stack_protect_get_tpsi (tp));
> + operands[1]
> + = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, tp,
> + GEN_INT
> (TARGET_THREAD_SSP_OFFSET)));
> #endif
> + }
> if (TARGET_64BIT)
> emit_insn (gen_stack_protect_testdi (operands[0], operands[1]));
> else
> diff --git a/gcc/config/s390/s390.opt b/gcc/config/s390/s390.opt
> index 6753a9326be..a82992ef7ef 100644
> --- a/gcc/config/s390/s390.opt
> +++ b/gcc/config/s390/s390.opt
> @@ -196,6 +196,24 @@ mno-stack-guard
> Target RejectNegative Alias(mstack-guard=,0) Negative(mstack-guard=)
> Switches off the -mstack-guard= option.
>
> +mstack-protector-guard=
> +Target RejectNegative Joined Enum(stack_protector_guard)
> Var(s390_stack_protector_guard) Init(SP_TLS)
> +Use given stack-protector guard.
> +
> +Enum
> +Name(stack_protector_guard) Type(enum stack_protector_guard)
> +Valid arguments to -mstack-protector-guard=:
> +
> +EnumValue
> +Enum(stack_protector_guard) String(tls) Value(SP_TLS)
> +
> +EnumValue
> +Enum(stack_protector_guard) String(global) Value(SP_GLOBAL)
> +
> +mstack-protector-guard-record
> +Target Var(flag_s390_stack_protector_guard_record)
> +Generate section __stack_protector_loc containing pointers to all
> instructions which load the address of the global guard.
> +
> mstack-size=
> Target RejectNegative Joined UInteger Var(s390_stack_size) Save
> Emit extra code in the function prologue in order to trap if the stack size
> exceeds the given limit.
> diff --git a/gcc/testsuite/gcc.target/s390/stack-protector-guard-global-1.c
> b/gcc/testsuite/gcc.target/s390/stack-protector-guard-global-1.c
> new file mode 100644
> index 00000000000..f276645adcb
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/s390/stack-protector-guard-global-1.c
> @@ -0,0 +1,27 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fstack-protector-all -mstack-protector-guard=global
> -mstack-protector-guard-record" } */
> +/* { dg-final { scan-assembler-times
> {\n1:\n\tlarl\t%r[0-9]+,__stack_chk_guard\n} 4 } } */
> +
> +void test_0 (void) { }
> +
> +void test_1 (void)
> +{
> + __asm__ __volatile ("" :::
> + "r0",
> + "r1",
> + "r2",
> + "r3",
> + "r4",
> + "r5",
> + "r6",
> + "r7",
> + "r8",
> + "r9",
> + "r10",
> + "r11",
> +#ifndef __PIC__
> + "r12",
> +#endif
> + "r13",
> + "r14");
> +}
> diff --git a/gcc/testsuite/gcc.target/s390/stack-protector-guard-global-2.c
> b/gcc/testsuite/gcc.target/s390/stack-protector-guard-global-2.c
> new file mode 100644
> index 00000000000..7441a87adf7
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/s390/stack-protector-guard-global-2.c
> @@ -0,0 +1,5 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fstack-protector-all -mstack-protector-guard=global
> -mstack-protector-guard-record -fPIC" } */
> +/* { dg-final { scan-assembler-times
> {\n1:\n\t(larl|lg?rl)\t%r[0-9]+,__stack_chk_guard@GOTENT\n} 4 } } */
> +
> +#include "stack-protector-guard-global-1.c"
> diff --git a/gcc/testsuite/gcc.target/s390/stack-protector-guard-global-3.c
> b/gcc/testsuite/gcc.target/s390/stack-protector-guard-global-3.c
> new file mode 100644
> index 00000000000..4ef379aa8f7
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/s390/stack-protector-guard-global-3.c
> @@ -0,0 +1,6 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fstack-protector-all -mstack-protector-guard=global" }
> */
> +/* { dg-final { scan-assembler-times {\tlarl\t%r[0-9]+,__stack_chk_guard\n}
> 4 } } */
> +/* { dg-final { scan-assembler-not {\n1:\n} } } */
> +
> +#include "stack-protector-guard-global-1.c"
> diff --git a/gcc/testsuite/gcc.target/s390/stack-protector-guard-global-4.c
> b/gcc/testsuite/gcc.target/s390/stack-protector-guard-global-4.c
> new file mode 100644
> index 00000000000..d57fc79bd83
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/s390/stack-protector-guard-global-4.c
> @@ -0,0 +1,6 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fstack-protector-all -mstack-protector-guard=global
> -fPIC" } */
> +/* { dg-final { scan-assembler-times
> {\t(larl|lg?rl)\t%r[0-9]+,__stack_chk_guard@GOTENT\n} 4 } } */
> +/* { dg-final { scan-assembler-not {\n1:\n} } } */
> +
> +#include "stack-protector-guard-global-1.c"
> --
> 2.49.0
>