Index: include/llvm/CodeGen/WinEHFuncInfo.h =================================================================== --- include/llvm/CodeGen/WinEHFuncInfo.h +++ include/llvm/CodeGen/WinEHFuncInfo.h @@ -109,6 +109,7 @@ int EHRegNodeFrameIndex = INT_MAX; int EHRegNodeEndOffset = INT_MAX; + int EHGuardFrameIndex = INT_MAX; int SEHSetFrameOffset = INT_MAX; WinEHFuncInfo(); Index: include/llvm/IR/IntrinsicsX86.td =================================================================== --- include/llvm/IR/IntrinsicsX86.td +++ include/llvm/IR/IntrinsicsX86.td @@ -25,6 +25,9 @@ // Marks the EH registration node created in LLVM IR prior to code generation. def int_x86_seh_ehregnode : Intrinsic<[], [llvm_ptr_ty], []>; + // Marks the EH guard slot node created in LLVM IR prior to code generation. + def int_x86_seh_ehguard : Intrinsic<[], [llvm_ptr_ty], []>; + // Given a pointer to the end of an EH registration object, returns the true // parent frame address that can be used with llvm.localrecover. def int_x86_seh_recoverfp : Intrinsic<[llvm_ptr_ty], Index: lib/CodeGen/AsmPrinter/WinException.cpp =================================================================== --- lib/CodeGen/AsmPrinter/WinException.cpp +++ lib/CodeGen/AsmPrinter/WinException.cpp @@ -944,15 +944,41 @@ // ScopeTableEntry ScopeRecord[]; // }; // - // Only the EHCookieOffset field appears to vary, and it appears to be the - // offset from the final saved SP value to the retaddr. + // Offsets are %ebp relative. + // + // The GS cookie is present only if the function needs stack protection. + // GSCookieOffset = -2 means that GS cookie is not used. + // + // The EH cookie is always present. + // + // Check is done the following way: + // (ebp+CookieXOROffset) ^ [ebp+CookieOffset] == _security_cookie + + // Retrieve the Guard Stack slot. + int GSCookieOffset = -2; + const MachineFrameInfo *MFI = MF->getFrameInfo(); + if (MFI->hasStackProtectorIndex()) { + unsigned UnusedReg; + const TargetFrameLowering *TFI = MF->getSubtarget().getFrameLowering(); + int SSPIdx = MFI->getStackProtectorIndex(); + GSCookieOffset = TFI->getFrameIndexReference(*MF, SSPIdx, UnusedReg); + } + + // Retrieve the EH Guard slot. + int EHCookieOffset = 9999; + if (FuncInfo.EHGuardFrameIndex != INT_MAX) { + unsigned UnusedReg; + const TargetFrameLowering *TFI = MF->getSubtarget().getFrameLowering(); + int EHGuardIdx = FuncInfo.EHGuardFrameIndex; + EHCookieOffset = TFI->getFrameIndexReference(*MF, EHGuardIdx, UnusedReg); + } + AddComment("GSCookieOffset"); - OS.EmitIntValue(-2, 4); + OS.EmitIntValue(GSCookieOffset, 4); AddComment("GSCookieXOROffset"); OS.EmitIntValue(0, 4); - // FIXME: Calculate. AddComment("EHCookieOffset"); - OS.EmitIntValue(9999, 4); + OS.EmitIntValue(EHCookieOffset, 4); AddComment("EHCookieXOROffset"); OS.EmitIntValue(0, 4); BaseState = -2; Index: lib/Target/X86/X86ISelLowering.cpp =================================================================== --- lib/Target/X86/X86ISelLowering.cpp +++ lib/Target/X86/X86ISelLowering.cpp @@ -18170,6 +18170,24 @@ return Chain; } +static SDValue MarkEHGuard(SDValue Op, SelectionDAG &DAG) { + MachineFunction &MF = DAG.getMachineFunction(); + SDValue Chain = Op.getOperand(0); + SDValue EHGuard = Op.getOperand(2); + WinEHFuncInfo *EHInfo = MF.getWinEHFuncInfo(); + if (!EHInfo) + report_fatal_error("EHGuard only live in functions using WinEH"); + + // Cast the operand to an alloca, and remember the frame index. + auto *FINode = dyn_cast(EHGuard); + if (!FINode) + report_fatal_error("llvm.x86.seh.ehguard expects a static alloca"); + EHInfo->EHGuardFrameIndex = FINode->getIndex(); + + // Return the chain operand without making any DAG nodes. + return Chain; +} + static SDValue LowerINTRINSIC_W_CHAIN(SDValue Op, const X86Subtarget &Subtarget, SelectionDAG &DAG) { unsigned IntNo = cast(Op.getOperand(1))->getZExtValue(); @@ -18178,6 +18196,8 @@ if (!IntrData) { if (IntNo == llvm::Intrinsic::x86_seh_ehregnode) return MarkEHRegistrationNode(Op, DAG); + if (IntNo == llvm::Intrinsic::x86_seh_ehguard) + return MarkEHGuard(Op, DAG); if (IntNo == llvm::Intrinsic::x86_flags_read_u32 || IntNo == llvm::Intrinsic::x86_flags_read_u64 || IntNo == llvm::Intrinsic::x86_flags_write_u32 || Index: lib/Target/X86/X86WinEHState.cpp =================================================================== --- lib/Target/X86/X86WinEHState.cpp +++ lib/Target/X86/X86WinEHState.cpp @@ -106,6 +106,9 @@ /// fs:00 chain and the current state. AllocaInst *RegNode = nullptr; + // The allocation containing the EH security guard. + AllocaInst *EHGuardNode = nullptr; + /// The index of the state field of RegNode. int StateFieldIndex = ~0U; @@ -195,6 +198,9 @@ PersonalityFn = nullptr; Personality = EHPersonality::Unknown; UseStackGuard = false; + RegNode = nullptr; + EHGuardNode = nullptr; + return true; } @@ -274,6 +280,9 @@ IRBuilder<> Builder(&F->getEntryBlock(), F->getEntryBlock().begin()); Type *Int8PtrType = Builder.getInt8PtrTy(); + Type *Int32Ty = Builder.getInt32Ty(); + Type *VoidTy = Builder.getVoidTy(); + if (Personality == EHPersonality::MSVC_CXX) { RegNodeTy = getCXXEHRegistrationType(); RegNode = Builder.CreateAlloca(RegNodeTy); @@ -292,37 +301,53 @@ CxxLongjmpUnwind = TheModule->getOrInsertFunction( "__CxxLongjmpUnwind", - FunctionType::get(Type::getVoidTy(TheModule->getContext()), Int8PtrType, - /*isVarArg=*/false)); + FunctionType::get(VoidTy, Int8PtrType, /*isVarArg=*/false)); cast(CxxLongjmpUnwind->stripPointerCasts()) ->setCallingConv(CallingConv::X86_StdCall); } else if (Personality == EHPersonality::MSVC_X86SEH) { // If _except_handler4 is in use, some additional guard checks and prologue // stuff is required. + StringRef PersonalityName = PersonalityFn->getName(); + UseStackGuard = (PersonalityName == "_except_handler4"); + + // Allocate local structures. RegNodeTy = getSEHRegistrationType(); RegNode = Builder.CreateAlloca(RegNodeTy); + if (UseStackGuard) + EHGuardNode = Builder.CreateAlloca(Int32Ty); + // SavedESP = llvm.stacksave() Value *SP = Builder.CreateCall( Intrinsic::getDeclaration(TheModule, Intrinsic::stacksave), {}); Builder.CreateStore(SP, Builder.CreateStructGEP(RegNodeTy, RegNode, 0)); // TryLevel = -2 / -1 StateFieldIndex = 4; - StringRef PersonalityName = PersonalityFn->getName(); - UseStackGuard = (PersonalityName == "_except_handler4"); ParentBaseState = UseStackGuard ? -2 : -1; insertStateNumberStore(&*Builder.GetInsertPoint(), ParentBaseState); // ScopeTable = llvm.x86.seh.lsda(F) Value *LSDA = emitEHLSDA(Builder, F); - Type *Int32Ty = Type::getInt32Ty(TheModule->getContext()); LSDA = Builder.CreatePtrToInt(LSDA, Int32Ty); // If using _except_handler4, xor the address of the table with // __security_cookie. if (UseStackGuard) { Cookie = TheModule->getOrInsertGlobal("__security_cookie", Int32Ty); - Value *Val = Builder.CreateLoad(Int32Ty, Cookie); + Value *Val = Builder.CreateLoad(Int32Ty, Cookie, "cookie"); LSDA = Builder.CreateXor(LSDA, Val); } Builder.CreateStore(LSDA, Builder.CreateStructGEP(RegNodeTy, RegNode, 3)); + + // If using _except_handler4, the EHGuard contains: FramePtr xor Cookie. + if (UseStackGuard) { + Value *Val = Builder.CreateLoad(Int32Ty, Cookie); + Value *FrameAddr = Builder.CreateCall( + Intrinsic::getDeclaration(TheModule, Intrinsic::frameaddress), + Builder.getInt32(0), "frameaddr"); + Value *FrameAddrI32 = Builder.CreatePtrToInt(FrameAddr, Int32Ty); + FrameAddrI32 = Builder.CreateXor(FrameAddrI32, Val); + Builder.CreateStore(FrameAddrI32, EHGuardNode); + } + + // Register the exception handler. Link = Builder.CreateStructGEP(RegNodeTy, RegNode, 2); linkExceptionRegistration(Builder, PersonalityFn); @@ -608,12 +633,21 @@ void WinEHStatePass::addStateStores(Function &F, WinEHFuncInfo &FuncInfo) { // Mark the registration node. The backend needs to know which alloca it is so // that it can recover the original frame pointer. - IRBuilder<> Builder(RegNode->getParent(), std::next(RegNode->getIterator())); + IRBuilder<> Builder(RegNode->getNextNode()); Value *RegNodeI8 = Builder.CreateBitCast(RegNode, Builder.getInt8PtrTy()); Builder.CreateCall( Intrinsic::getDeclaration(TheModule, Intrinsic::x86_seh_ehregnode), {RegNodeI8}); + if (EHGuardNode) { + IRBuilder<> Builder(EHGuardNode->getNextNode()); + Value *EHGuardNodeI8 = + Builder.CreateBitCast(EHGuardNode, Builder.getInt8PtrTy()); + Builder.CreateCall( + Intrinsic::getDeclaration(TheModule, Intrinsic::x86_seh_ehguard), + {EHGuardNodeI8}); + } + // Calculate state numbers. if (isAsynchronousEHPersonality(Personality)) calculateSEHStateNumbers(&F, FuncInfo); Index: test/CodeGen/X86/win32-eh.ll =================================================================== --- test/CodeGen/X86/win32-eh.ll +++ test/CodeGen/X86/win32-eh.ll @@ -88,12 +88,58 @@ ; CHECK-LABEL: L__ehtable$use_except_handler4: ; CHECK-NEXT: .long -2 ; CHECK-NEXT: .long 0 -; CHECK-NEXT: .long 9999 +; CHECK-NEXT: .long -40 ; CHECK-NEXT: .long 0 ; CHECK-NEXT: .long -2 ; CHECK-NEXT: .long _catchall_filt ; CHECK-NEXT: .long LBB2_2 +define void @use_except_handler4_ssp() sspstrong personality i32 (...)* @_except_handler4 { +entry: + invoke void @may_throw_or_crash() + to label %cont unwind label %lpad +cont: + ret void +lpad: + %cs = catchswitch within none [label %catch] unwind to caller +catch: + %p = catchpad within %cs [i8* bitcast (i32 ()* @catchall_filt to i8*)] + catchret from %p to label %cont +} + +; CHECK-LABEL: _use_except_handler4_ssp: +; CHECK: pushl %ebp +; CHECK: movl %esp, %ebp +; CHECK: subl ${{[0-9]+}}, %esp +; CHECK: movl %ebp, %[[ehguard:[^ ,]*]] +; CHECK: movl %esp, -36(%ebp) +; CHECK: movl $-2, -16(%ebp) +; CHECK: movl $L__ehtable$use_except_handler4_ssp, %[[lsda:[^ ,]*]] +; CHECK: xorl ___security_cookie, %[[lsda]] +; CHECK: movl %[[lsda]], -20(%ebp) +; CHECK: xorl ___security_cookie, %[[ehguard]] +; CHECK: movl %[[ehguard]], -40(%ebp) +; CHECK: leal -28(%ebp), %[[node:[^ ,]*]] +; CHECK: movl $__except_handler4, -24(%ebp) +; CHECK: movl %fs:0, %[[next:[^ ,]*]] +; CHECK: movl %[[next]], -28(%ebp) +; CHECK: movl %[[node]], %fs:0 +; CHECK: calll _may_throw_or_crash +; CHECK: movl -28(%ebp), %[[next:[^ ,]*]] +; CHECK: movl %[[next]], %fs:0 +; CHECK: retl +; CHECK: [[catch:[^ ,]*]]: # %catch{{$}} + +; CHECK: .section .xdata,"dr" +; CHECK-LABEL: L__ehtable$use_except_handler4_ssp: +; CHECK-NEXT: .long -2 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long -40 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long -2 +; CHECK-NEXT: .long _catchall_filt +; CHECK-NEXT: .long [[catch]] + define void @use_CxxFrameHandler3() personality i32 (...)* @__CxxFrameHandler3 { invoke void @may_throw_or_crash() to label %cont unwind label %catchall