diff --git a/clang/include/clang/Basic/BuiltinsPPC.def b/clang/include/clang/Basic/BuiltinsPPC.def --- a/clang/include/clang/Basic/BuiltinsPPC.def +++ b/clang/include/clang/Basic/BuiltinsPPC.def @@ -165,6 +165,8 @@ BUILTIN(__builtin_ppc_get_timebase, "ULLi", "n") +BUILTIN(__builtin_ppc_kill_canary, "v", "") + // This is just a placeholder, the types and attributes are wrong. BUILTIN(__builtin_altivec_vaddcuw, "V4UiV4UiV4Ui", "") diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp --- a/clang/lib/Basic/Targets/PPC.cpp +++ b/clang/lib/Basic/Targets/PPC.cpp @@ -122,6 +122,7 @@ Builder.defineMacro("__fetch_and_orlp", "__builtin_ppc_fetch_and_orlp"); Builder.defineMacro("__fetch_and_swap", "__builtin_ppc_fetch_and_swap"); Builder.defineMacro("__fetch_and_swaplp", "__builtin_ppc_fetch_and_swaplp"); + Builder.defineMacro("__kill_canary", "__builtin_ppc_kill_canary"); Builder.defineMacro("__ldarx", "__builtin_ppc_ldarx"); Builder.defineMacro("__lwarx", "__builtin_ppc_lwarx"); Builder.defineMacro("__lharx", "__builtin_ppc_lharx"); diff --git a/clang/test/CodeGen/PowerPC/builtins-ppc-stackprotect.c b/clang/test/CodeGen/PowerPC/builtins-ppc-stackprotect.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/PowerPC/builtins-ppc-stackprotect.c @@ -0,0 +1,23 @@ +// REQUIRES: powerpc-registered-target +// RUN: %clang_cc1 -O2 -triple powerpc64-unknown-linux-gnu \ +// RUN: -emit-llvm %s -o - -target-cpu pwr7 | \ +// RUN: FileCheck %s +// RUN: %clang_cc1 -O2 -triple powerpc64le-unknown-linux-gnu \ +// RUN: -emit-llvm %s -o - -target-cpu pwr8 | \ +// RUN: FileCheck %s +// RUN: %clang_cc1 -O2 -triple powerpc-unknown-aix \ +// RUN: -emit-llvm %s -o - -target-cpu pwr7 | \ +// RUN: FileCheck %s +// RUN: %clang_cc1 -O2 -triple powerpc64-unknown-aix \ +// RUN: -emit-llvm %s -o - -target-cpu pwr7 | \ +// RUN: FileCheck %s + +void test_builtin_ppc_kill_canary() { + // CHECK: call void @llvm.ppc.kill.canary() + __builtin_ppc_kill_canary(); +} + +void test_kill_canary() { + // CHECK: call void @llvm.ppc.kill.canary() + __kill_canary(); +} diff --git a/llvm/include/llvm/IR/IntrinsicsPowerPC.td b/llvm/include/llvm/IR/IntrinsicsPowerPC.td --- a/llvm/include/llvm/IR/IntrinsicsPowerPC.td +++ b/llvm/include/llvm/IR/IntrinsicsPowerPC.td @@ -217,6 +217,11 @@ : Intrinsic<[llvm_float_ty], [llvm_float_ty, llvm_float_ty, llvm_float_ty, llvm_vararg_ty], [IntrNoMem]>; + def int_ppc_kill_canary + : ClangBuiltin<"__builtin_ppc_kill_canary">, + Intrinsic<[], + [], + [IntrWriteMem, IntrHasSideEffects]>; } let TargetPrefix = "ppc" in { // All PPC intrinsics start with "llvm.ppc.". diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp --- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp +++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp @@ -11143,6 +11143,72 @@ } break; } + case Intrinsic::ppc_kill_canary: { + MachineFunction &MF = DAG.getMachineFunction(); + const Module *M = MF.getMMI().getModule(); + + /* If SafeStack or !StackProtector, kill_canary not supported */ + if (MF.getFunction().hasFnAttribute(Attribute::SafeStack) || + !MF.getFunction().hasStackProtectorFnAttr()) { + DAG.ReplaceAllUsesOfValueWith( + SDValue(Op.getNode(), 0), + Op->getOperand(0)); // prepare node for deletion + break; + } + + EVT VT = DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout()); + + SDValue Load; + SDValue Store; + + const uint64_t XORWord = + 0xFFFFFFFFFFFFFFFF; // XORing with 0b111...111 will never + // result in the original word + + if (useLoadStackGuardNode()) { // linux uses LOAD_STACK_GUARD node instead + // of having a canary word global value + MachineSDNode *LSG = + DAG.getMachineNode(PPC::LOAD_STACK_GUARD, DL, VT, Op->getOperand(0)); + Load = SDValue(LSG, 0); + + /* frame index used to determine stack guard location if + * LOAD_STACK_GUARD is used */ + MachineFrameInfo &MFI = MF.getFrameInfo(); + int SPI = MFI.getStackProtectorIndex(); // should return -1 + PPCFunctionInfo *FuncInfo = MF.getInfo(); + SDValue FIN = DAG.getFrameIndex(FuncInfo->getVarArgsFrameIndex(), VT); + + // XOR canary word and store back + Store = DAG.getStore( + Op->getOperand(0), DL, + DAG.getNode(ISD::XOR, DL, VT, Load, DAG.getConstant(XORWord, DL, VT)), + DAG.getNode( // add frame index, stack protector index, return node + // result + ISD::ADD, DL, VT, FIN, DAG.getConstant(SPI, DL, VT)), + MachinePointerInfo()); + + } else if (Value *GV = + getSDagStackGuard(*M)) { // on aix, load from global value + VT = DAG.getTargetLoweringInfo().getValueType(DAG.getDataLayout(), + GV->getType(), true); + SDValue CanaryLoc = + DAG.getGlobalAddress(dyn_cast(GV), DL, VT); + + // Load from global value + Load = DAG.getLoad(VT, DL, Op->getOperand(0), CanaryLoc, + MachinePointerInfo()); + + // XOR canary word and store back + Store = DAG.getStore( + Op->getOperand(0), DL, + DAG.getNode(ISD::XOR, DL, VT, Load, DAG.getConstant(XORWord, DL, VT)), + CanaryLoc, MachinePointerInfo()); + } else { + llvm_unreachable("Unhandled stack guard case"); + } + + return Store; + } default: break; } diff --git a/llvm/test/CodeGen/PowerPC/kill-canary-intrinsic.ll b/llvm/test/CodeGen/PowerPC/kill-canary-intrinsic.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/kill-canary-intrinsic.ll @@ -0,0 +1,77 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -verify-machineinstrs -mtriple=powerpc64-unknown-aix \ +; RUN: --ppc-asm-full-reg-names < %s | FileCheck %s -check-prefix=AIX +; RUN: llc -verify-machineinstrs -mtriple=powerpc64-unknown-linux \ +; RUN: --ppc-asm-full-reg-names < %s | FileCheck %s -check-prefix=LINUX + +declare void @llvm.ppc.kill.canary() +define dso_local void @test_kill_canary() { +; AIX-LABEL: test_kill_canary: +; AIX: # %bb.0: # %entry +; AIX-NEXT: blr +; +; LINUX-LABEL: test_kill_canary: +; LINUX: # %bb.0: # %entry +; LINUX-NEXT: blr +entry: + call void @llvm.ppc.kill.canary() + ret void +} + +attributes #0 = { sspreq } +; Function Attrs: sspreq +define dso_local void @test_kill_canary_ssp() #0 { +; AIX-LABEL: test_kill_canary_ssp: +; AIX: # %bb.0: # %entry +; AIX-NEXT: mflr r0 +; AIX-NEXT: std r0, 16(r1) +; AIX-NEXT: stdu r1, -128(r1) +; AIX-NEXT: ld r3, L..C0(r2) # @__ssp_canary_word +; AIX-NEXT: ld r4, 0(r3) +; AIX-NEXT: std r4, 120(r1) +; AIX-NEXT: ld r4, 0(r3) +; AIX-NEXT: xori r4, r4, 65535 +; AIX-NEXT: xoris r4, r4, 65535 +; AIX-NEXT: std r4, 0(r3) +; AIX-NEXT: ld r3, 0(r3) +; AIX-NEXT: ld r4, 120(r1) +; AIX-NEXT: cmpld r3, r4 +; AIX-NEXT: bne cr0, L..BB1_2 +; AIX-NEXT: # %bb.1: # %entry +; AIX-NEXT: addi r1, r1, 128 +; AIX-NEXT: ld r0, 16(r1) +; AIX-NEXT: mtlr r0 +; AIX-NEXT: blr +; AIX-NEXT: L..BB1_2: # %entry +; AIX-NEXT: bl .__stack_chk_fail[PR] +; AIX-NEXT: nop +; +; LINUX-LABEL: test_kill_canary_ssp: +; LINUX: # %bb.0: # %entry +; LINUX-NEXT: mflr r0 +; LINUX-NEXT: std r0, 16(r1) +; LINUX-NEXT: stdu r1, -128(r1) +; LINUX-NEXT: .cfi_def_cfa_offset 128 +; LINUX-NEXT: .cfi_offset lr, 16 +; LINUX-NEXT: ld r3, -28688(r13) +; LINUX-NEXT: std r3, 120(r1) +; LINUX-NEXT: ld r3, -28688(r13) +; LINUX-NEXT: xori r3, r3, 65535 +; LINUX-NEXT: xoris r3, r3, 65535 +; LINUX-NEXT: std r3, 120(r1) +; LINUX-NEXT: ld r3, 120(r1) +; LINUX-NEXT: ld r4, -28688(r13) +; LINUX-NEXT: cmpld r4, r3 +; LINUX-NEXT: bne cr0, .LBB1_2 +; LINUX-NEXT: # %bb.1: # %entry +; LINUX-NEXT: addi r1, r1, 128 +; LINUX-NEXT: ld r0, 16(r1) +; LINUX-NEXT: mtlr r0 +; LINUX-NEXT: blr +; LINUX-NEXT: .LBB1_2: # %entry +; LINUX-NEXT: bl __stack_chk_fail +; LINUX-NEXT: nop +entry: + call void @llvm.ppc.kill.canary() + ret void +}