//===-- CodeGen/AsmPrinter/Win64Exception.cpp - Dwarf Exception Impl ------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file contains support for writing Win64 exception info into asm files. // //===----------------------------------------------------------------------===// #include "Win64Exception.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/WinEHFuncInfo.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Mangler.h" #include "llvm/IR/Module.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/Dwarf.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Target/TargetFrameLowering.h" #include "llvm/Target/TargetLoweringObjectFile.h" #include "llvm/Target/TargetOptions.h" #include "llvm/Target/TargetRegisterInfo.h" using namespace llvm; Win64Exception::Win64Exception(AsmPrinter *A) : EHStreamer(A), shouldEmitPersonality(false), shouldEmitLSDA(false), shouldEmitMoves(false) {} Win64Exception::~Win64Exception() {} /// endModule - Emit all exception information that should come after the /// content. void Win64Exception::endModule() { } void Win64Exception::beginFunction(const MachineFunction *MF) { shouldEmitMoves = shouldEmitPersonality = shouldEmitLSDA = false; // If any landing pads survive, we need an EH table. bool hasLandingPads = !MMI->getLandingPads().empty(); shouldEmitMoves = Asm->needsSEHMoves(); const TargetLoweringObjectFile &TLOF = Asm->getObjFileLowering(); unsigned PerEncoding = TLOF.getPersonalityEncoding(); const Function *Per = MF->getMMI().getPersonality(); shouldEmitPersonality = hasLandingPads && PerEncoding != dwarf::DW_EH_PE_omit && Per; unsigned LSDAEncoding = TLOF.getLSDAEncoding(); shouldEmitLSDA = shouldEmitPersonality && LSDAEncoding != dwarf::DW_EH_PE_omit; // If this was an outlined handler, we need to define the label corresponding // to the offset of the parent frame relative to the stack pointer after the // prologue. const Function *F = MF->getFunction(); const Function *ParentF = MMI->getWinEHParent(F); if (F != ParentF) { WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(ParentF); auto I = FuncInfo.CatchHandlerParentFrameObjOffset.find(F); if (I != FuncInfo.CatchHandlerParentFrameObjOffset.end()) { MCSymbol *HandlerTypeParentFrameOffset = Asm->OutContext.getOrCreateParentFrameOffsetSymbol( GlobalValue::getRealLinkageName(F->getName())); // Emit a symbol assignment. Asm->OutStreamer.EmitAssignment( HandlerTypeParentFrameOffset, MCConstantExpr::Create(I->second, Asm->OutContext)); } } if (!shouldEmitPersonality && !shouldEmitMoves) return; Asm->OutStreamer.EmitWinCFIStartProc(Asm->CurrentFnSym); if (!shouldEmitPersonality) return; const MCSymbol *PersHandlerSym = TLOF.getCFIPersonalitySymbol(Per, *Asm->Mang, Asm->TM, MMI); Asm->OutStreamer.EmitWinEHHandler(PersHandlerSym, true, true); } /// endFunction - Gather and emit post-function exception information. /// void Win64Exception::endFunction(const MachineFunction *MF) { if (!shouldEmitPersonality && !shouldEmitMoves) return; EHPersonality Per = MMI->getPersonalityType(); // Get rid of any dead landing pads if we're not using a Windows EH scheme. In // Windows EH schemes, the landing pad is not actually reachable. It only // exists so that we can emit the right table data. if (!isMSVCEHPersonality(Per)) MMI->TidyLandingPads(); if (shouldEmitPersonality) { Asm->OutStreamer.PushSection(); // Emit an UNWIND_INFO struct describing the prologue. Asm->OutStreamer.EmitWinEHHandlerData(); // Emit the tables appropriate to the personality function in use. If we // don't recognize the personality, assume it uses an Itanium-style LSDA. if (Per == EHPersonality::MSVC_Win64SEH) emitCSpecificHandlerTable(); else if (Per == EHPersonality::MSVC_CXX) emitCXXFrameHandler3Table(MF); else emitExceptionTable(); Asm->OutStreamer.PopSection(); } Asm->OutStreamer.EmitWinCFIEndProc(); } const MCExpr *Win64Exception::createImageRel32(const MCSymbol *Value) { if (!Value) return MCConstantExpr::Create(0, Asm->OutContext); return MCSymbolRefExpr::Create(Value, MCSymbolRefExpr::VK_COFF_IMGREL32, Asm->OutContext); } const MCExpr *Win64Exception::createImageRel32(const GlobalValue *GV) { if (!GV) return MCConstantExpr::Create(0, Asm->OutContext); return createImageRel32(Asm->getSymbol(GV)); } /// Emit the language-specific data that __C_specific_handler expects. This /// handler lives in the x64 Microsoft C runtime and allows catching or cleaning /// up after faults with __try, __except, and __finally. The typeinfo values /// are not really RTTI data, but pointers to filter functions that return an /// integer (1, 0, or -1) indicating how to handle the exception. For __finally /// blocks and other cleanups, the landing pad label is zero, and the filter /// function is actually a cleanup handler with the same prototype. A catch-all /// entry is modeled with a null filter function field and a non-zero landing /// pad label. /// /// Possible filter function return values: /// EXCEPTION_EXECUTE_HANDLER (1): /// Jump to the landing pad label after cleanups. /// EXCEPTION_CONTINUE_SEARCH (0): /// Continue searching this table or continue unwinding. /// EXCEPTION_CONTINUE_EXECUTION (-1): /// Resume execution at the trapping PC. /// /// Inferred table structure: /// struct Table { /// int NumEntries; /// struct Entry { /// imagerel32 LabelStart; /// imagerel32 LabelEnd; /// imagerel32 FilterOrFinally; // One means catch-all. /// imagerel32 LabelLPad; // Zero means __finally. /// } Entries[NumEntries]; /// }; void Win64Exception::emitCSpecificHandlerTable() { const std::vector &PadInfos = MMI->getLandingPads(); // Simplifying assumptions for first implementation: // - Cleanups are not implemented. // - Filters are not implemented. // The Itanium LSDA table sorts similar landing pads together to simplify the // actions table, but we don't need that. SmallVector LandingPads; LandingPads.reserve(PadInfos.size()); for (const auto &LP : PadInfos) LandingPads.push_back(&LP); // Compute label ranges for call sites as we would for the Itanium LSDA, but // use an all zero action table because we aren't using these actions. SmallVector FirstActions; FirstActions.resize(LandingPads.size()); SmallVector CallSites; computeCallSiteTable(CallSites, LandingPads, FirstActions); MCSymbol *EHFuncBeginSym = Asm->getFunctionBegin(); MCSymbol *EHFuncEndSym = Asm->getFunctionEnd(); // Emit the number of table entries. unsigned NumEntries = 0; for (const CallSiteEntry &CSE : CallSites) { if (!CSE.LPad) continue; // Ignore gaps. for (int Selector : CSE.LPad->TypeIds) { // Ignore C++ filter clauses in SEH. // FIXME: Implement cleanup clauses. if (isCatchEHSelector(Selector)) ++NumEntries; } } Asm->OutStreamer.EmitIntValue(NumEntries, 4); // Emit the four-label records for each call site entry. The table has to be // sorted in layout order, and the call sites should already be sorted. for (const CallSiteEntry &CSE : CallSites) { // Ignore gaps. Unlike the Itanium model, unwinding through a frame without // an EH table entry will propagate the exception rather than terminating // the program. if (!CSE.LPad) continue; const LandingPadInfo *LPad = CSE.LPad; // Compute the label range. We may reuse the function begin and end labels // rather than forming new ones. const MCExpr *Begin = createImageRel32(CSE.BeginLabel ? CSE.BeginLabel : EHFuncBeginSym); const MCExpr *End; if (CSE.EndLabel) { // The interval is half-open, so we have to add one to include the return // address of the last invoke in the range. End = MCBinaryExpr::CreateAdd(createImageRel32(CSE.EndLabel), MCConstantExpr::Create(1, Asm->OutContext), Asm->OutContext); } else { End = createImageRel32(EHFuncEndSym); } // These aren't really type info globals, they are actually pointers to // filter functions ordered by selector. The zero selector is used for // cleanups, so slot zero corresponds to selector 1. const std::vector &SelectorToFilter = MMI->getTypeInfos(); // Do a parallel iteration across typeids and clause labels, skipping filter // clauses. size_t NextClauseLabel = 0; for (size_t I = 0, E = LPad->TypeIds.size(); I < E; ++I) { // AddLandingPadInfo stores the clauses in reverse, but there is a FIXME // to change that. int Selector = LPad->TypeIds[E - I - 1]; // Ignore C++ filter clauses in SEH. // FIXME: Implement cleanup clauses. if (!isCatchEHSelector(Selector)) continue; Asm->OutStreamer.EmitValue(Begin, 4); Asm->OutStreamer.EmitValue(End, 4); if (isCatchEHSelector(Selector)) { assert(unsigned(Selector - 1) < SelectorToFilter.size()); const GlobalValue *TI = SelectorToFilter[Selector - 1]; if (TI) // Emit the filter function pointer. Asm->OutStreamer.EmitValue(createImageRel32(Asm->getSymbol(TI)), 4); else // Otherwise, this is a "catch i8* null", or catch all. Asm->OutStreamer.EmitIntValue(1, 4); } MCSymbol *ClauseLabel = LPad->ClauseLabels[NextClauseLabel++]; Asm->OutStreamer.EmitValue(createImageRel32(ClauseLabel), 4); } } } void Win64Exception::emitCXXFrameHandler3Table(const MachineFunction *MF) { const Function *F = MF->getFunction(); const Function *ParentF = MMI->getWinEHParent(F); auto &OS = Asm->OutStreamer; WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(ParentF); StringRef ParentLinkageName = GlobalValue::getRealLinkageName(ParentF->getName()); MCSymbol *FuncInfoXData = Asm->OutContext.GetOrCreateSymbol(Twine("$cppxdata$", ParentLinkageName)); OS.EmitValue(createImageRel32(FuncInfoXData), 4); // The Itanium LSDA table sorts similar landing pads together to simplify the // actions table, but we don't need that. SmallVector LandingPads; const std::vector &PadInfos = MMI->getLandingPads(); LandingPads.reserve(PadInfos.size()); for (const auto &LP : PadInfos) LandingPads.push_back(&LP); RangeMapType PadMap; computePadMap(LandingPads, PadMap); // The end label of the previous invoke or nounwind try-range. MCSymbol *LastLabel = Asm->getFunctionBegin(); // Whether there is a potentially throwing instruction (currently this means // an ordinary call) between the end of the previous try-range and now. bool SawPotentiallyThrowing = false; int LastEHState = -2; // The parent function and the catch handlers contribute to the 'ip2state' // table. for (const auto &MBB : *MF) { for (const auto &MI : MBB) { if (!MI.isEHLabel()) { if (MI.isCall()) SawPotentiallyThrowing |= !callToNoUnwindFunction(&MI); continue; } // End of the previous try-range? MCSymbol *BeginLabel = MI.getOperand(0).getMCSymbol(); if (BeginLabel == LastLabel) SawPotentiallyThrowing = false; // Beginning of a new try-range? RangeMapType::const_iterator L = PadMap.find(BeginLabel); if (L == PadMap.end()) // Nope, it was just some random label. continue; const PadRange &P = L->second; const LandingPadInfo *LandingPad = LandingPads[P.PadIndex]; assert(BeginLabel == LandingPad->BeginLabels[P.RangeIndex] && "Inconsistent landing pad map!"); if (SawPotentiallyThrowing) { FuncInfo.IPToStateList.push_back(std::make_pair(LastLabel, -1)); SawPotentiallyThrowing = false; LastEHState = -1; } if (LandingPad->WinEHState != LastEHState) FuncInfo.IPToStateList.push_back( std::make_pair(BeginLabel, LandingPad->WinEHState)); LastEHState = LandingPad->WinEHState; LastLabel = LandingPad->EndLabels[P.RangeIndex]; } } // Defer emission until we've visited the parent function and all the catch // handlers. Cleanups don't contribute to the ip2state table yet, so don't // count them. if (ParentF != F && !FuncInfo.CatchHandlerMaxState.count(F)) return; ++FuncInfo.NumIPToStateFuncsVisited; if (FuncInfo.NumIPToStateFuncsVisited != FuncInfo.CatchHandlerMaxState.size()) return; MCSymbol *UnwindMapXData = nullptr; MCSymbol *TryBlockMapXData = nullptr; MCSymbol *IPToStateXData = nullptr; if (!FuncInfo.UnwindMap.empty()) UnwindMapXData = Asm->OutContext.GetOrCreateSymbol( Twine("$stateUnwindMap$", ParentLinkageName)); if (!FuncInfo.TryBlockMap.empty()) TryBlockMapXData = Asm->OutContext.GetOrCreateSymbol( Twine("$tryMap$", ParentLinkageName)); if (!FuncInfo.IPToStateList.empty()) IPToStateXData = Asm->OutContext.GetOrCreateSymbol( Twine("$ip2state$", ParentLinkageName)); // FuncInfo { // uint32_t MagicNumber // int32_t MaxState; // UnwindMapEntry *UnwindMap; // uint32_t NumTryBlocks; // TryBlockMapEntry *TryBlockMap; // uint32_t IPMapEntries; // IPToStateMapEntry *IPToStateMap; // uint32_t UnwindHelp; // (x64/ARM only) // ESTypeList *ESTypeList; // int32_t EHFlags; // } // EHFlags & 1 -> Synchronous exceptions only, no async exceptions. // EHFlags & 2 -> ??? // EHFlags & 4 -> The function is noexcept(true), unwinding can't continue. OS.EmitLabel(FuncInfoXData); OS.EmitIntValue(0x19930522, 4); // MagicNumber OS.EmitIntValue(FuncInfo.UnwindMap.size(), 4); // MaxState OS.EmitValue(createImageRel32(UnwindMapXData), 4); // UnwindMap OS.EmitIntValue(FuncInfo.TryBlockMap.size(), 4); // NumTryBlocks OS.EmitValue(createImageRel32(TryBlockMapXData), 4); // TryBlockMap OS.EmitIntValue(FuncInfo.IPToStateList.size(), 4); // IPMapEntries OS.EmitValue(createImageRel32(IPToStateXData), 4); // IPToStateMap OS.EmitIntValue(FuncInfo.UnwindHelpFrameOffset, 4); // UnwindHelp OS.EmitIntValue(0, 4); // ESTypeList OS.EmitIntValue(1, 4); // EHFlags // UnwindMapEntry { // int32_t ToState; // void (*Action)(); // }; if (UnwindMapXData) { OS.EmitLabel(UnwindMapXData); for (const WinEHUnwindMapEntry &UME : FuncInfo.UnwindMap) { OS.EmitIntValue(UME.ToState, 4); // ToState OS.EmitValue(createImageRel32(UME.Cleanup), 4); // Action } } // TryBlockMap { // int32_t TryLow; // int32_t TryHigh; // int32_t CatchHigh; // int32_t NumCatches; // HandlerType *HandlerArray; // }; if (TryBlockMapXData) { OS.EmitLabel(TryBlockMapXData); SmallVector HandlerMaps; for (size_t I = 0, E = FuncInfo.TryBlockMap.size(); I != E; ++I) { WinEHTryBlockMapEntry &TBME = FuncInfo.TryBlockMap[I]; MCSymbol *HandlerMapXData = nullptr; if (!TBME.HandlerArray.empty()) HandlerMapXData = Asm->OutContext.GetOrCreateSymbol(Twine("$handlerMap$") .concat(Twine(I)) .concat("$") .concat(ParentLinkageName)); HandlerMaps.push_back(HandlerMapXData); int CatchHigh = -1; for (WinEHHandlerType &HT : TBME.HandlerArray) CatchHigh = std::max(CatchHigh, FuncInfo.CatchHandlerMaxState[HT.Handler]); assert(TBME.TryLow <= TBME.TryHigh); assert(CatchHigh > TBME.TryHigh); OS.EmitIntValue(TBME.TryLow, 4); // TryLow OS.EmitIntValue(TBME.TryHigh, 4); // TryHigh OS.EmitIntValue(CatchHigh, 4); // CatchHigh OS.EmitIntValue(TBME.HandlerArray.size(), 4); // NumCatches OS.EmitValue(createImageRel32(HandlerMapXData), 4); // HandlerArray } for (size_t I = 0, E = FuncInfo.TryBlockMap.size(); I != E; ++I) { WinEHTryBlockMapEntry &TBME = FuncInfo.TryBlockMap[I]; MCSymbol *HandlerMapXData = HandlerMaps[I]; if (!HandlerMapXData) continue; // HandlerType { // int32_t Adjectives; // TypeDescriptor *Type; // int32_t CatchObjOffset; // void (*Handler)(); // int32_t ParentFrameOffset; // x64 only // }; OS.EmitLabel(HandlerMapXData); for (const WinEHHandlerType &HT : TBME.HandlerArray) { MCSymbol *ParentFrameOffset = Asm->OutContext.getOrCreateParentFrameOffsetSymbol( GlobalValue::getRealLinkageName(HT.Handler->getName())); const MCSymbolRefExpr *ParentFrameOffsetRef = MCSymbolRefExpr::Create( ParentFrameOffset, MCSymbolRefExpr::VK_None, Asm->OutContext); // Get the frame escape label with the offset of the catch object. If // the index is -1, then there is no catch object, and we should emit an // offset of zero, indicating that no copy will occur. const MCExpr *FrameAllocOffsetRef = nullptr; if (HT.CatchObjRecoverIdx >= 0) { MCSymbol *FrameAllocOffset = Asm->OutContext.getOrCreateFrameAllocSymbol( GlobalValue::getRealLinkageName(ParentF->getName()), HT.CatchObjRecoverIdx); FrameAllocOffsetRef = MCSymbolRefExpr::Create( FrameAllocOffset, MCSymbolRefExpr::VK_None, Asm->OutContext); } else { FrameAllocOffsetRef = MCConstantExpr::Create(0, Asm->OutContext); } OS.EmitIntValue(HT.Adjectives, 4); // Adjectives OS.EmitValue(createImageRel32(HT.TypeDescriptor), 4); // Type OS.EmitValue(FrameAllocOffsetRef, 4); // CatchObjOffset OS.EmitValue(createImageRel32(HT.Handler), 4); // Handler OS.EmitValue(ParentFrameOffsetRef, 4); // ParentFrameOffset } } } // IPToStateMapEntry { // void *IP; // int32_t State; // }; if (IPToStateXData) { OS.EmitLabel(IPToStateXData); for (auto &IPStatePair : FuncInfo.IPToStateList) { OS.EmitValue(createImageRel32(IPStatePair.first), 4); // IP OS.EmitIntValue(IPStatePair.second, 4); // State } } }