https://github.com/sujianIBM created https://github.com/llvm/llvm-project/pull/206045
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. >From a4732561e975ffa1e82081fe593f7c3f6adfbbe5 Mon Sep 17 00:00:00 2001 From: Jian Su <[email protected]> Date: Thu, 25 Jun 2026 19:34:58 +0000 Subject: [PATCH 1/2] Add support for stack guard on z/OS. --- llvm/include/llvm/IR/RuntimeLibcalls.td | 3 +- llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp | 19 ++ llvm/lib/Target/SystemZ/SystemZAsmPrinter.h | 2 + llvm/lib/Target/SystemZ/SystemZInstrInfo.cpp | 17 +- llvm/lib/Target/SystemZ/SystemZInstrInfo.td | 3 + .../CodeGen/SystemZ/zos-stack-protector.ll | 190 ++++++++++++++++++ 6 files changed, 229 insertions(+), 5 deletions(-) create mode 100644 llvm/test/CodeGen/SystemZ/zos-stack-protector.ll 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 >From 03eca124e2b7b0c899f4ca01a8d662b9ef8b585a Mon Sep 17 00:00:00 2001 From: Jian Su <[email protected]> Date: Thu, 25 Jun 2026 20:06:52 +0000 Subject: [PATCH 2/2] Disable value global for option -mstack-protector-guard= on z/OS. --- clang/lib/Driver/ToolChains/Clang.cpp | 6 ++++++ clang/test/Driver/stack-protector-guard.c | 8 ++++++++ 2 files changed, 14 insertions(+) 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
