[clang] [llvm] [SystemZ][z/OS] Add support of stack guard on z/OS (PR #206045)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 26 05:23:37 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-driver
Author: sujianIBM
<details>
<summary>Changes</summary>
This PR
- Adds support of stack guard for XPLINK64 on z/OS.
- Disables value `global` for option `-mstack-protector-guard=` on z/OS since it is not supported.
---
Full diff: https://github.com/llvm/llvm-project/pull/206045.diff
8 Files Affected:
- (modified) clang/lib/Driver/ToolChains/Clang.cpp (+6)
- (modified) clang/test/Driver/stack-protector-guard.c (+8)
- (modified) llvm/include/llvm/IR/RuntimeLibcalls.td (+2-1)
- (modified) llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp (+19)
- (modified) llvm/lib/Target/SystemZ/SystemZAsmPrinter.h (+2)
- (modified) llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp (+13-4)
- (modified) llvm/lib/Target/SystemZ/SystemZInstrInfo.td (+3)
- (added) llvm/test/CodeGen/SystemZ/zos-stack-protector.ll (+190)
``````````diff
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 418d540895681..b3bd5ca3463c2 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -3524,6 +3524,12 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC,
!EffectiveTriple.isSystemZ())
D.Diag(diag::err_drv_unsupported_opt_for_target)
<< A->getAsString(Args) << TripleStr;
+ // z/OS only supports the tls mode.
+ if (EffectiveTriple.isOSzOS() && GuardValue != "tls") {
+ D.Diag(diag::err_drv_invalid_value_with_suggestion)
+ << A->getOption().getName() << GuardValue << "tls";
+ return;
+ }
if ((EffectiveTriple.isX86() || EffectiveTriple.isARM() ||
EffectiveTriple.isThumb() || EffectiveTriple.isSystemZ()) &&
GuardValue != "tls" && GuardValue != "global") {
diff --git a/clang/test/Driver/stack-protector-guard.c b/clang/test/Driver/stack-protector-guard.c
index 46e09d6581867..971b042dd4f2e 100644
--- a/clang/test/Driver/stack-protector-guard.c
+++ b/clang/test/Driver/stack-protector-guard.c
@@ -178,3 +178,11 @@
// RUN: -mstack-protector-guard-record %s 2>&1 | \
// RUN: FileCheck -check-prefix=INVALID_TLS_RECORD_SYSTEMZ %s
// INVALID_TLS_RECORD_SYSTEMZ: error: invalid argument '-mstack-protector-guard-record' only allowed with '-mstack-protector-guard=global'
+
+// RUN: %clang -### -target s390x-ibm-zos -mstack-protector-guard=tls %s 2>&1 | \
+// RUN: FileCheck -check-prefix=CHECK_TLS_ZOS %s
+// CHECK_TLS_ZOS: "-cc1" {{.*}}"-mstack-protector-guard=tls"
+
+// RUN: not %clang -### -target s390x-ibm-zos -mstack-protector-guard=global %s 2>&1 | \
+// RUN: FileCheck -check-prefix=INVALID_GLOBAL_ZOS %s
+// INVALID_GLOBAL_ZOS: invalid value 'global' in 'mstack-protector-guard=', expected one of: tls
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.td b/llvm/include/llvm/IR/RuntimeLibcalls.td
index 37bad559f49e7..ff23a813ca737 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.td
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.td
@@ -3541,7 +3541,8 @@ def isZOS : RuntimeLibcallPredicate<"TT.isOSzOS()">;
def SystemZZOSSystemLibrary
: SystemRuntimeLibrary<
isSystemZZOS, (add DefaultLibcallImpls64,
- LibcallImpls<(add ZOSRuntimeLibcalls), isZOS>)>;
+ LibcallImpls<(add ZOSRuntimeLibcalls), isZOS>,
+ DefaultStackProtector)>;
//===----------------------------------------------------------------------===//
// WebAssembly Runtime Libcalls
diff --git a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
index 5c755db5f7f5f..c181e2048becd 100644
--- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
@@ -780,6 +780,9 @@ void SystemZAsmPrinter::emitInstruction(const MachineInstr *MI) {
case SystemZ::LOAD_GLOBAL_STACKGUARD_ADDR:
lowerLOAD_GLOBAL_STACKGUARD_ADDR(*MI, Lower);
return;
+ case SystemZ::LOAD_LIBRARY_ANCHOR_AREA_ADDR:
+ lowerLOAD_LIBRARY_ANCHOR_AREA_ADDR(*MI, Lower);
+ return;
default:
Lower.lower(MI, LoweredMI);
@@ -1092,6 +1095,22 @@ void SystemZAsmPrinter::lowerLOAD_GLOBAL_STACKGUARD_ADDR(
}
}
+void SystemZAsmPrinter::lowerLOAD_LIBRARY_ANCHOR_AREA_ADDR(
+ const MachineInstr &MI, SystemZMCInstLower &Lower) {
+ Register AddrReg = MI.getOperand(0).getReg();
+ const SystemZSubtarget &STI = MF->getSubtarget<SystemZSubtarget>();
+
+ assert(STI.isTargetzOS() &&
+ "LOAD_LIBRARY_ANCHOR_AREA_ADDR is only for XPLINK64 on z/OS");
+
+ enum { OFFSET_PSALAA = 0x4B8 };
+ EmitToStreamer(*OutStreamer, MCInstBuilder(SystemZ::LLGT)
+ .addReg(AddrReg)
+ .addReg(0)
+ .addImm(OFFSET_PSALAA)
+ .addReg(0));
+}
+
// The *alignment* of 128-bit vector types is different between the software
// and hardware vector ABIs. If the there is an externally visible use of a
// vector type in the module it should be annotated with an attribute.
diff --git a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h
index f3703b783f7ec..e383b29b199e5 100644
--- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h
+++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h
@@ -174,6 +174,8 @@ class LLVM_LIBRARY_VISIBILITY SystemZAsmPrinter : public AsmPrinter {
SystemZMCInstLower &Lower);
void lowerLOAD_GLOBAL_STACKGUARD_ADDR(const MachineInstr &MI,
SystemZMCInstLower &Lower);
+ void lowerLOAD_LIBRARY_ANCHOR_AREA_ADDR(const MachineInstr &MI,
+ SystemZMCInstLower &Lower);
void emitAttributes(Module &M);
};
} // end namespace llvm
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp b/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp
index d5bdb8ef3b595..2662bb905d96d 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp
@@ -1813,10 +1813,16 @@ void SystemZInstrInfo::expandStackGuardPseudo(MachineInstr &MI,
// Emit an appropriate pseudo for the guard type, which loads the address of
// said guard into the scratch register AddrReg.
if (GuardType.empty() || (GuardType == "tls")) {
- // Emit a load of the TLS block's address
- BuildMI(MBB, MI, DL, get(SystemZ::LOAD_TLS_BLOCK_ADDR), AddrReg);
- // Record the appropriate stack guard offset (40 in the tls case).
- Offset = 40;
+ if (STI.isTargetzOS()) {
+ enum { OFFSET_CEELAA_STACK_GUARD = 0x98 };
+ BuildMI(MBB, MI, DL, get(SystemZ::LOAD_LIBRARY_ANCHOR_AREA_ADDR), AddrReg);
+ Offset = OFFSET_CEELAA_STACK_GUARD;
+ } else {
+ // Emit a load of the TLS block's address
+ BuildMI(MBB, MI, DL, get(SystemZ::LOAD_TLS_BLOCK_ADDR), AddrReg);
+ // Record the appropriate stack guard offset (40 in the tls case).
+ Offset = 40;
+ }
} else if (GuardType == "global") {
// Emit a load of the global stack guard's address
BuildMI(MBB, MI, DL, get(SystemZ::LOAD_GLOBAL_STACKGUARD_ADDR), AddrReg);
@@ -1861,6 +1867,9 @@ unsigned SystemZInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const {
if (MI.getOpcode() == SystemZ::LOAD_GLOBAL_STACKGUARD_ADDR)
// Both larl and lgrl are 6 bytes long.
return 6;
+ if (MI.getOpcode() == SystemZ::LOAD_LIBRARY_ANCHOR_AREA_ADDR)
+ // llgt(6)
+ return 6;
return MI.getDesc().getSize();
}
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index 269fa6ffda0b9..8cd135aaa8b1c 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -530,6 +530,9 @@ let hasNoSchedulingInfo = 1, hasSideEffects = 1, mayLoad = 1 in {
// Load the address of a global variable holding the stack guard.
def LOAD_GLOBAL_STACKGUARD_ADDR : Pseudo<(outs ADDR64:$grdaddr),
(ins), []>;
+ // Load the address of Library Anchor Area (LAA) on z/OS
+ def LOAD_LIBRARY_ANCHOR_AREA_ADDR : Pseudo<(outs ADDR64:$grdaddr),
+ (ins), []>;
let mayStore = 1 in {
// Move the stack guard to the stack.
diff --git a/llvm/test/CodeGen/SystemZ/zos-stack-protector.ll b/llvm/test/CodeGen/SystemZ/zos-stack-protector.ll
new file mode 100644
index 0000000000000..c56360bbfa4f9
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/zos-stack-protector.ll
@@ -0,0 +1,190 @@
+; Test the stack protector under XPLINK on z/OS
+;
+; RUN: llc < %s -mtriple=s390x-ibm-zos -mcpu=z13 | FileCheck --check-prefixes=CHECK %s
+
+; Test stack protector for non-XPLEAF.
+
+; Small stack frame.
+; CHECK-LABEL: func0
+; CHECK: * DSA Size [[#%#x,DSA_SIZE:]]
+; CHECK: aghi 4,-[[#%u,mul(div(DSA_SIZE,32),32)]]
+; CHECK: llgt [[REG1:[0-9]+]],1208
+; CHECK: mvc [[#%u,2040+mul(div(DSA_SIZE,32),32)]](8,4),152([[REG1]])
+; ...
+; CHECK: llgt [[REG3:[0-9]+]],1208
+; CHECK: clc [[#%u,2040+mul(div(DSA_SIZE,32),32)]](8,4),152([[REG3]])
+; CHECK: jlh [[FAIL_LABEL:L#BB[0-9_]+]]
+; success block
+; CHECK: aghi 4,[[#%u,mul(div(DSA_SIZE,32),32)]]
+; CHECK: b 2(7)
+; failure block
+; CHECK: [[FAIL_LABEL]] DS 0H
+; invoke __stack_chk_fail
+; CHECK: lg 6,[[#CHK_FAIL_OFF:]]({{[0-9]+}})
+; CHECK: lg 5,[[#CHK_FAIL_OFF-8]]({{[0-9]+}})
+; CHECK: basr 7,6
+; CHECK-NEXT: bcr 0,0
+
+define void @func0() sspreq {
+ call i64 (i64) @fun(i64 10)
+ ret void
+}
+
+; Large stack frame.
+; Larger than 1M in XPLINK64.
+; CHECK-LABEL: func1
+; CHECK: * DSA Size [[#%#x,DSA_SIZE:]]
+; CHECK: stmg 6,{{[0-9]+}},2064(4)
+; CHECK: llilh [[REG_CANARY_OFF_HIGH:[0-9]+]],[[#%u,CANARY_OFF_HIGH:div(DSA_SIZE,65536)]]
+; CHECK: la [[REG_CANARY_OFF_HIGH]],0([[REG_CANARY_OFF_HIGH]],4)
+; CHECK: llgt [[REG1:[0-9]+]],1208
+; CHECK: mvc [[#%u,2040+mul(div(DSA_SIZE,32),32)-mul(CANARY_OFF_HIGH,65536)]](8,[[REG_CANARY_OFF_HIGH]]),152([[REG1]])
+; ...
+; CHECK: llilh [[REG_CANARY_OFF_HIGH_2:[0-9]+]],[[#%u,CANARY_OFF_HIGH_2:div(DSA_SIZE,65536)]]
+; CHECK: la [[REG_CANARY_OFF_HIGH_2]],0([[REG_CANARY_OFF_HIGH_2]],4)
+; CHECK: llgt [[REG3:[0-9]+]],1208
+; CHECK: clc [[#%u,2040+mul(div(DSA_SIZE,32),32)-mul(CANARY_OFF_HIGH_2,65536)]](8,[[REG_CANARY_OFF_HIGH_2]]),152([[REG3]])
+; CHECK: jlh [[FAIL_LABEL:L#BB[0-9_]+]]
+; success block
+; CHECK: agfi 4,[[#%u,mul(div(DSA_SIZE,32),32)]]
+; CHECK: b 2(7)
+; failure block
+; CHECK: [[FAIL_LABEL]] DS 0H
+; invoke __stack_chk_fail
+; CHECK: lg 6,[[#CHK_FAIL_OFF:]]({{[0-9]+}})
+; CHECK: lg 5,[[#CHK_FAIL_OFF-8]]({{[0-9]+}})
+; CHECK: basr 7,6
+; CHECK-NEXT: bcr 0,0
+
+define void @func1() sspreq {
+ %arr = alloca [131072 x i64], align 8
+ call i64 (ptr) @fun1(ptr %arr)
+ ret void
+}
+
+; Test converting XPLeaf functions to non-leaf functions if they need stack protection
+
+; TODO: Currently any function that needs to store data on the stack is
+; converted to a non-leaf function, so XPLeaf functions never write to the
+; stack, and thus there is no way for them to cause stack corruption.
+;
+; Eventually we'll start taking advantage of the 2048 bytes of space between R4
+; and the caller's stack frame to eliminate the need to convert some functions
+; that would have been XPLeaf functions. At which point, it will be possible for
+; an XPLeaf function to corrupt the stack.
+;
+; Since stack protection protects against corruption of the caller's stack
+; frame and not corruption of the callee's stack frame, it doesn't matter that
+; XPLeaf functions don't have a stack frame of their own - that's not what we'd
+; be protecting anyways.
+;
+; Thus, we'll have to choose what to do with functions that need stack protection
+; but could remain as XPLeaf functions by using those 2048 bytes of space.
+; We have 3 options:
+; 1. Convert them to non-leaf functions and continue protecting them as before.
+; 2. Keep them as XPLeaf functions, but give up on stack protecting them
+; 3. Keep them as XPLeaf functions, and try to stack protect them without making
+; any function calls in the failure case. This would probably involve
+; delaying the invocation of __stack_chk_fail/__CEL4SFCR until we return to
+; the caller.
+;
+; If we choose option 1, leave this test as is and remove this TODO.
+; If we choose option 2, make sure we don't try to stack protect these functions.
+; If we choose option 3, this test needs to be replaced, but what we replace it
+; with will depend on how we implement the failure case.
+
+; Based on func3_64 in call-zos-03.ll
+; CHECK-LABEL: func2_64
+; CHECK: * Entry Flags
+; CHECK-NEXT: * Bit 1: 0 = Non-leaf function
+define i64 @func2_64(i64 %arg0) sspreq {
+ %out = add i64 %arg0, 55
+ ret i64 %out
+}
+
+; Based on func6 in zos-prologue-epilog.ll. R15 is callee-saved, so needs to be
+; spilled to the stack before we use it. As a result this currently gets
+; converted to a non-leaf function (even without sspreq).
+; CHECK-LABEL: func3
+; CHECK: * Entry Flags
+; CHECK-NEXT: * Bit 1: 0 = Non-leaf function
+define void @func3() local_unnamed_addr sspreq #0 {
+entry:
+ tail call void asm sideeffect " lhi 15,1\0A", "~{r15}"()
+ ret void
+}
+
+; Test stack protector for function that uses alloca()
+
+; CHECK-LABEL: func4
+; CHECK: * DSA Size [[#%#x,DSA_SIZE:]]
+; CHECK: * Entry Flags
+; CHECK-NEXT: * Bit 1: 0 = Non-leaf function
+; CHECK-NEXT: * Bit 2: 1 = Uses alloca
+; CHECK: stmg 4,[[SPILLHI:[0-9]+]],[[#%u,2048-mul(div(DSA_SIZE,32),32)]](4)
+; CHECK: aghi 4,-[[#%u,mul(div(DSA_SIZE,32),32)]]
+; CHECK: lgr [[ALLOCAREG:[0-9]+]],4
+; CHECK: llgt [[REG1:[0-9]+]],1208
+; CHECK: mvc [[#%u,2040+mul(div(DSA_SIZE,32),32)]](8,[[ALLOCAREG]]),152([[REG1]])
+; ...
+; CHECK: llgt [[REG3:[0-9]+]],1208
+; CHECK: clc [[#%u,2040+mul(div(DSA_SIZE,32),32)]](8,[[ALLOCAREG]]),152([[REG3]])
+; CHECK: jlh [[FAIL_LABEL:L#BB[0-9_]+]]
+; success block
+; CHECK: lmg 4,[[SPILLHI]],2048(4)
+; CHECK: b 2(7)
+; failure block
+; CHECK: [[FAIL_LABEL]] DS 0H
+; invoke __stack_chk_fail
+; CHECK: lg 6,[[#CHK_FAIL_OFF:]]({{[0-9]+}})
+; CHECK: lg 5,[[#CHK_FAIL_OFF-8]]({{[0-9]+}})
+; CHECK: basr 7,6
+; CHECK-NEXT: bcr 0,0
+
+define i64 @func4(i64 %n) sspreq {
+ %vla = alloca i64, i64 %n, align 8
+ %call = call i64 @fun2(i64 %n, ptr nonnull %vla, ptr nonnull %vla)
+ ret i64 %call
+}
+
+; Test stack protector off.
+
+; CHECK-LABEL: func5
+define void @func5() {
+ call i64 (i64) @fun(i64 10)
+ ret void
+}
+
+declare i64 @fun(i64 %arg0)
+declare i64 @fun1(ptr %ptr)
+declare i64 @fun2(i64 %n, ptr %arr0, ptr %arr1)
+
+; CHECK-LABEL: L#PPA1_func0_0 DS 0H
+; CHECK: * PPA1 Flags 2
+; CHECK-NOT: * PPA1 Flags 3
+; CHECK: * Bit 3: 1 = STACKPROTECT is enabled
+
+; CHECK-LABEL: L#PPA1_func1_0 DS 0H
+; CHECK: * PPA1 Flags 2
+; CHECK-NOT: * PPA1 Flags 3
+; CHECK: * Bit 3: 1 = STACKPROTECT is enabled
+
+; CHECK-LABEL: L#PPA1_func2_64_0 DS 0H
+; CHECK: * PPA1 Flags 2
+; CHECK-NOT: * PPA1 Flags 3
+; CHECK: * Bit 3: 1 = STACKPROTECT is enabled
+
+; CHECK-LABEL: L#PPA1_func3_0 DS 0H
+; CHECK: * PPA1 Flags 2
+; CHECK-NOT: * PPA1 Flags 3
+; CHECK: * Bit 3: 1 = STACKPROTECT is enabled
+
+; CHECK-LABEL: L#PPA1_func4_0 DS 0H
+; CHECK: * PPA1 Flags 2
+; CHECK-NOT: * PPA1 Flags 3
+; CHECK: * Bit 3: 1 = STACKPROTECT is enabled
+
+; CHECK-LABEL: L#PPA1_func5_0 DS 0H
+; CHECK: * PPA1 Flags 2
+; CHECK-NOT: * PPA1 Flags 3
+; CHECK: * Bit 3: 0 = STACKPROTECT is not enabled
``````````
</details>
https://github.com/llvm/llvm-project/pull/206045
More information about the cfe-commits
mailing list