//===---*- mode: c++; indent-tabs-mode: nil; c-basic-offset: 2 -*---------===// //===-- CodeGen/AsmPrinter/MonoException.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 code to emit Mono specific exception handling tables. // It is based on code in DwarfException.cpp and MCDwarf.cpp. // //===----------------------------------------------------------------------===// #include "MonoException.h" #include "llvm/IR/Module.h" #include "llvm/IR/Constants.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/TargetFrameLowering.h" #include "llvm/CodeGen/TargetLoweringObjectFile.h" #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.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/MC/MCSymbolELF.h" #include "llvm/IR/DataLayout.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" using namespace llvm; // // The EH tables emitted by this class enable the following functionality: // - obtaining the GNU EH information for a given method // - obtaining the DWARF CFI unwind info for a given method // - obtaining the address of the table itself from user // code, ie. it is in the text segment pointed to by // a symbol. // cl::opt MonoEHFrameSymbol("mono-eh-frame-symbol", cl::NotHidden, cl::desc("Symbol name for the mono eh frame")); // Emit a CFI instruction in DWARF format static void emitCFIInstruction(MCStreamer &Streamer, const MCCFIInstruction &Instr, int &CFAOffset, int DataAlignmentFactor) { // Same as MCDwarf::EmitCFIInstruction () // FIXME: Unify int dataAlignmentFactor = DataAlignmentFactor; bool VerboseAsm = Streamer.isVerboseAsm(); switch (Instr.getOperation()) { case MCCFIInstruction::OpWindowSave: { Streamer.EmitIntValue(dwarf::DW_CFA_GNU_window_save, 1); return; } case MCCFIInstruction::OpUndefined: { unsigned Reg = Instr.getRegister(); if (VerboseAsm) { Streamer.AddComment("DW_CFA_undefined"); Streamer.AddComment(Twine("Reg ") + Twine(Reg)); } Streamer.EmitIntValue(dwarf::DW_CFA_undefined, 1); Streamer.EmitULEB128IntValue(Reg); return; } case MCCFIInstruction::OpAdjustCfaOffset: case MCCFIInstruction::OpDefCfaOffset: { const bool IsRelative = Instr.getOperation() == MCCFIInstruction::OpAdjustCfaOffset; if (VerboseAsm) Streamer.AddComment("DW_CFA_def_cfa_offset"); Streamer.EmitIntValue(dwarf::DW_CFA_def_cfa_offset, 1); if (IsRelative) { CFAOffset += Instr.getOffset(); } else { // The backends pass in a negative value, // then createDefCfaOffset () negates it CFAOffset = Instr.getOffset(); assert(CFAOffset >= 0); } if (VerboseAsm) Streamer.AddComment(Twine("Offset " + Twine(CFAOffset))); Streamer.EmitULEB128IntValue(CFAOffset); return; } case MCCFIInstruction::OpDefCfa: { if (VerboseAsm) Streamer.AddComment("DW_CFA_def_cfa"); Streamer.EmitIntValue(dwarf::DW_CFA_def_cfa, 1); if (VerboseAsm) Streamer.AddComment(Twine("Reg ") + Twine(Instr.getRegister())); Streamer.EmitULEB128IntValue(Instr.getRegister()); // The backends pass in a negative value, // then createDefCfaOffset () negates it CFAOffset = Instr.getOffset(); if (CFAOffset < 0) { outs () << CFAOffset << "\n"; LLVM_BUILTIN_TRAP; } assert(CFAOffset >= 0); if (VerboseAsm) Streamer.AddComment(Twine("Offset " + Twine(CFAOffset))); Streamer.EmitULEB128IntValue(CFAOffset); return; } case MCCFIInstruction::OpDefCfaRegister: { if (VerboseAsm) Streamer.AddComment("DW_CFA_def_cfa_register"); Streamer.EmitIntValue(dwarf::DW_CFA_def_cfa_register, 1); if (VerboseAsm) Streamer.AddComment(Twine("Reg ") + Twine(Instr.getRegister())); Streamer.EmitULEB128IntValue(Instr.getRegister()); return; } case MCCFIInstruction::OpOffset: case MCCFIInstruction::OpRelOffset: { const bool IsRelative = Instr.getOperation() == MCCFIInstruction::OpRelOffset; unsigned Reg = Instr.getRegister(); int Offset = Instr.getOffset(); if (IsRelative) Offset -= CFAOffset; Offset = Offset / dataAlignmentFactor; if (Offset < 0) { if (VerboseAsm) Streamer.AddComment("DW_CFA_offset_extended_sf"); Streamer.EmitIntValue(dwarf::DW_CFA_offset_extended_sf, 1); if (VerboseAsm) Streamer.AddComment(Twine("Reg ") + Twine(Reg)); Streamer.EmitULEB128IntValue(Reg); if (VerboseAsm) Streamer.AddComment(Twine("Offset ") + Twine(Offset)); Streamer.EmitSLEB128IntValue(Offset); } else if (Reg < 64) { if (VerboseAsm) Streamer.AddComment(Twine("DW_CFA_offset + Reg(") + Twine(Reg) + ")"); Streamer.EmitIntValue(dwarf::DW_CFA_offset + Reg, 1); if (VerboseAsm) Streamer.AddComment(Twine("Offset ") + Twine(Offset)); Streamer.EmitULEB128IntValue(Offset); } else { if (VerboseAsm) Streamer.AddComment("DW_CFA_offset_extended"); Streamer.EmitIntValue(dwarf::DW_CFA_offset_extended, 1); if (VerboseAsm) Streamer.AddComment(Twine("Reg ") + Twine(Reg)); Streamer.EmitULEB128IntValue(Reg); if (VerboseAsm) Streamer.AddComment(Twine("Offset ") + Twine(Offset)); Streamer.EmitULEB128IntValue(Offset); } return; } case MCCFIInstruction::OpRememberState: if (VerboseAsm) Streamer.AddComment("DW_CFA_remember_state"); Streamer.EmitIntValue(dwarf::DW_CFA_remember_state, 1); return; case MCCFIInstruction::OpRestoreState: if (VerboseAsm) Streamer.AddComment("DW_CFA_restore_state"); Streamer.EmitIntValue(dwarf::DW_CFA_restore_state, 1); return; case MCCFIInstruction::OpSameValue: { unsigned Reg = Instr.getRegister(); if (VerboseAsm) Streamer.AddComment("DW_CFA_same_value"); Streamer.EmitIntValue(dwarf::DW_CFA_same_value, 1); if (VerboseAsm) Streamer.AddComment(Twine("Reg ") + Twine(Reg)); Streamer.EmitULEB128IntValue(Reg); return; } case MCCFIInstruction::OpRestore: { unsigned Reg = Instr.getRegister(); if (VerboseAsm) { Streamer.AddComment("DW_CFA_restore"); Streamer.AddComment(Twine("Reg ") + Twine(Reg)); } Streamer.EmitIntValue(dwarf::DW_CFA_restore | Reg, 1); return; } case MCCFIInstruction::OpEscape: if (VerboseAsm) Streamer.AddComment("Escape bytes"); Streamer.EmitBytes(Instr.getValues()); return; case MCCFIInstruction::OpRegister: case MCCFIInstruction::OpGnuArgsSize: llvm_unreachable("Unhandled case in switch"); return; } llvm_unreachable("Unhandled case in switch"); } // Emit a list of CFI instructions static void emitCFIInstructions(MCStreamer &streamer, const std::vector &Instrs, MCSymbol *BaseLabel, const std::vector *Labels, int &CFAOffset, int DataAlignmentFactor) { for (unsigned i = 0, N = Instrs.size(); i < N; ++i) { const MCCFIInstruction &Instr = Instrs[i]; MCSymbol *Label = Labels ? ((*Labels)[i]) : NULL; // Advance row if new location. if (BaseLabel && Label) { MCSymbol *ThisSym = Label; if (ThisSym != BaseLabel) { streamer.AddComment ("cfa_advance"); streamer.EmitDwarfAdvanceFrameAddr(BaseLabel, ThisSym); BaseLabel = ThisSym; } } emitCFIInstruction(streamer, Instr, CFAOffset, DataAlignmentFactor); } } MonoException::MonoException(AsmPrinter *A, bool disableGNUEH) : EHStreamer(A) { RI = nullptr; DisableGNUEH = disableGNUEH; } MonoException::~MonoException() { } void MonoException::beginFunction(const MachineFunction *MF) { EmitFnStart(); EHLabels.clear(); } void MonoException::PrepareMonoLSDA(EHInfo *info) { const MachineFunction *MF = Asm->MF; const std::vector &TypeInfos = MF->getTypeInfos(); const std::vector &PadInfos = MF->getLandingPads(); // Sort the landing pads in order of their type ids. This is used to fold // duplicate actions. SmallVector LandingPads; LandingPads.reserve(PadInfos.size()); for (unsigned i = 0, N = PadInfos.size(); i != N; ++i) LandingPads.push_back(&PadInfos[i]); std::sort(LandingPads.begin(), LandingPads.end(), [](const LandingPadInfo *L, const LandingPadInfo *R) { return L->TypeIds < R->TypeIds; }); // Invokes and nounwind calls have entries in PadMap (due to being bracketed // by try-range labels when lowered). Ordinary calls do not, so appropriate // try-ranges for them need be deduced when using DWARF exception handling. RangeMapType PadMap; for (unsigned i = 0, N = LandingPads.size(); i != N; ++i) { const LandingPadInfo *LandingPad = LandingPads[i]; for (unsigned j = 0, E = LandingPad->BeginLabels.size(); j != E; ++j) { MCSymbol *BeginLabel = LandingPad->BeginLabels[j]; assert(!PadMap.count(BeginLabel) && "Duplicate landing pad labels!"); PadRange P = { i, j }; PadMap[BeginLabel] = P; } } // Compute the call-site table. SmallVector CallSites; MCSymbol *LastLabel = 0; for (MachineFunction::const_iterator I = MF->begin(), E = MF->end(); I != E; ++I) { for (MachineBasicBlock::const_iterator MI = I->begin(), E = I->end(); MI != E; ++MI) { if (!MI->isLabel()) { continue; } MCSymbol *BeginLabel = MI->getOperand(0).getMCSymbol(); assert(BeginLabel && "Invalid label!"); RangeMapType::iterator L = PadMap.find(BeginLabel); if (L == PadMap.end()) continue; PadRange P = L->second; const LandingPadInfo *LandingPad = LandingPads[P.PadIndex]; assert(BeginLabel == LandingPad->BeginLabels[P.RangeIndex] && "Inconsistent landing pad map!"); // Mono emits one landing pad for each CLR exception clause, // and the type info contains the clause index assert (LandingPad->TypeIds.size() == 1); assert (LandingPad->LandingPadLabel); LastLabel = LandingPad->EndLabels[P.RangeIndex]; MonoCallSiteEntry Site = {BeginLabel, LastLabel, LandingPad->LandingPadLabel, LandingPad->TypeIds [0]}; assert(Site.BeginLabel && Site.EndLabel && Site.PadLabel && "Invalid landing pad!"); // FIXME: This doesn't work because it includes ranges outside clauses #if 0 // Try to merge with the previous call-site. if (CallSites.size()) { MonoCallSiteEntry &Prev = CallSites.back(); if (Site.PadLabel == Prev.PadLabel && Site.TypeID == Prev.TypeID) { // Extend the range of the previous entry. Prev.EndLabel = Site.EndLabel; continue; } } #endif // Otherwise, create a new call-site. CallSites.push_back(Site); } } info->CallSites.insert(info->CallSites.begin(), CallSites.begin(), CallSites.end()); info->TypeInfos = TypeInfos; info->PadInfos = PadInfos; int ThisSlot = Asm->MF->getMonoThisSlot(); if (ThisSlot != -1) { unsigned FrameReg; info->ThisOffset = Asm->MF->getTarget ().getSubtargetImpl (Asm->MF->getFunction())->getFrameLowering ()->getFrameIndexReference (*Asm->MF, ThisSlot, FrameReg); info->FrameReg = Asm->MF->getTarget ().getSubtargetImpl (Asm->MF->getFunction())->getRegisterInfo ()->getDwarfRegNum (FrameReg, true); } else { info->FrameReg = -1; } } void MonoException::EmitFnStart(void) { if (DisableGNUEH && Asm->MAI->getExceptionHandlingType() == ExceptionHandling::ARM) static_cast(Asm->OutStreamer->getTargetStreamer())->emitFnStart(); } void MonoException::EmitFnEnd(void) { if (DisableGNUEH && Asm->MAI->getExceptionHandlingType() == ExceptionHandling::ARM) static_cast(Asm->OutStreamer->getTargetStreamer())->emitFnEnd(); } void MonoException::endFunction(const MachineFunction *MF) { // // Compute a mapping from method names to their AOT method index // if (FuncIndexes.size () == 0) { const Module *m = MMI->getModule (); NamedMDNode *indexes = m->getNamedMetadata ("mono.function_indexes"); if (indexes) { for (unsigned int i = 0; i < indexes->getNumOperands (); ++i) { MDNode *n = indexes->getOperand (i); MDString *s = cast(n->getOperand (0)); auto *idx = mdconst::dyn_extract(n->getOperand (1)); FuncIndexes.insert (std::make_pair(s->getString (), (int)idx->getLimitedValue () + 1)); } } } // Remember the register info RI = MF->getSubtarget().getRegisterInfo(); MachineFunction *NonConstMF = const_cast(MF); NonConstMF->tidyLandingPads(); int monoMethodIdx = FuncIndexes.lookup (Asm->MF->getFunction ().getName ()) - 1; if (monoMethodIdx == -1) { EmitFnEnd (); return; } //outs () << "D: " << Asm->MF->getFunction()->getName() << " " << monoMethodIdx << "\n"; // Save information for use by endModule () EHInfo info; info.FunctionNumber = Asm->getFunctionNumber(); info.BeginSym = Asm->getFunctionBegin (); info.EndSym = Asm->getFunctionEnd (); info.EHLabels = EHLabels; info.MonoMethodIdx = monoMethodIdx; info.HasLandingPads = !MF->getLandingPads().empty(); info.Instructions = MF->getFrameInstructions(); assert (info.Instructions.size () == info.EHLabels.size()); if (DisableGNUEH) /* ARMAsmPrinter generates references to this */ Asm->OutStreamer->EmitLabel(Asm->getCurExceptionSym()); PrepareMonoLSDA(&info); Frames.push_back(info); EHLabels.clear(); EmitFnEnd (); } /// EmitMonoLSDA - Mono's version of EmitExceptionTable /// /// The code below is a modified/simplified version of DwarfException::EmitExceptionTable() /// We emit the information inline instead of into a separate section. /// void MonoException::EmitMonoLSDA(const EHInfo *info) { // Load saved information from EHFrameInfo const std::vector &TypeInfos = info->TypeInfos; const std::vector &PadInfos = info->PadInfos; const std::vector CallSites = info->CallSites; int FrameReg = info->FrameReg; int ThisOffset = info->ThisOffset; // Sort the landing pads in order of their type ids. This is used to fold // duplicate actions. SmallVector LandingPads; LandingPads.reserve(PadInfos.size()); for (unsigned i = 0, N = PadInfos.size(); i != N; ++i) LandingPads.push_back(&PadInfos[i]); std::sort(LandingPads.begin(), LandingPads.end(), [](const LandingPadInfo *L, const LandingPadInfo *R) { return L->TypeIds < R->TypeIds; }); // The type_info itself is emitted int TTypeEncoding = dwarf::DW_EH_PE_udata4; // Emit the LSDA. // Keep this in sync with JITDwarfEmitter::EmitExceptionTable () Asm->EmitULEB128(0x4d4fef4f, "MONO Magic"); Asm->EmitULEB128(1, "Version"); // Emit the LSDA header. if (FrameReg != -1) { Asm->EmitEncodingByte(dwarf::DW_EH_PE_udata4, "This encoding"); // Emit 'this' location Asm->OutStreamer->AddComment("bregx"); Asm->EmitInt8((int)dwarf::DW_OP_bregx); Asm->EmitULEB128(FrameReg, "Base reg"); Asm->EmitSLEB128(ThisOffset, "Offset"); } else { Asm->EmitEncodingByte(dwarf::DW_EH_PE_omit, "@This encoding"); } Asm->EmitULEB128 (CallSites.size (), "Number of call sites"); Asm->EmitAlignment(2); for (std::vector::const_iterator I = CallSites.begin(), E = CallSites.end(); I != E; ++I) { const MonoCallSiteEntry &S = *I; MCSymbol *EHFuncBeginSym = info->BeginSym; MCSymbol *BeginLabel = S.BeginLabel; if (BeginLabel == 0) BeginLabel = EHFuncBeginSym; MCSymbol *EndLabel = S.EndLabel; if (EndLabel == 0) EndLabel = info->EndSym; Asm->OutStreamer->AddComment("Region start"); Asm->EmitLabelDifference(BeginLabel, EHFuncBeginSym, 4); Asm->OutStreamer->AddComment("Region length"); Asm->EmitLabelDifference(EndLabel, BeginLabel, 4); Asm->OutStreamer->AddComment("Landing pad"); if (!S.PadLabel) Asm->OutStreamer->EmitIntValue(0, 4); else Asm->EmitLabelDifference(S.PadLabel, EHFuncBeginSym, 4); unsigned int TypeID = S.TypeID; assert (TypeID > 0 && TypeID <= TypeInfos.size ()); const GlobalVariable *GV = dyn_cast(TypeInfos[TypeID - 1]); assert (GV); // // Mono typeinfos are simple constant integers. Emit the constant itself. // assert(GV); const ConstantInt *ci = dyn_cast(GV->getInitializer()); Asm->OutStreamer->AddComment("TypeInfo"); Asm->OutStreamer->EmitIntValue(ci->getZExtValue(),Asm->GetSizeOfEncodedValue(TTypeEncoding)); } } void MonoException::endModule() { const TargetLoweringObjectFile &tlof = Asm->getObjFileLowering(); auto &streamer = *Asm->OutStreamer; // Size and sign of stack growth. int stackGrowth = -Asm->getDataLayout().getPointerSize(); int dataAlignmentFactor = stackGrowth; // Emit the EH table // Can't use rodata as the symbols we reference are in the text segment streamer.SwitchSection(tlof.getTextSection()); MCSymbol *tableSymbol = Asm->OutContext.getOrCreateSymbol(Twine(MonoEHFrameSymbol)); MCSymbol *tableEndSym = Asm->createTempSymbol ("mono_eh_frame_end"); // Symbol Asm->EmitAlignment(4); streamer.EmitLabel(tableSymbol); streamer.EmitSymbolAttribute(tableSymbol, MCSA_ELF_TypeObject); if (Asm->MAI->hasDotTypeDotSizeDirective()) { const MCExpr *SizeExp = MCBinaryExpr::createSub( MCSymbolRefExpr::create(tableEndSym, Asm->OutContext), MCSymbolRefExpr::create(tableSymbol, Asm->OutContext), Asm->OutContext); streamer.emitELFSize(cast(tableSymbol), SizeExp); } // Header streamer.AddComment("version"); streamer.EmitIntValue(3, 1); streamer.AddComment ("func addr encoding"); // Unused streamer.EmitIntValue (0, 1); // Search table Asm->EmitAlignment(2); streamer.AddComment("fde_count"); streamer.EmitIntValue (Frames.size(), 4); MCSymbol *lastBegin = nullptr; MCSymbol *lastEnd = nullptr; for (std::vector::iterator I = Frames.begin(), E = Frames.end(); I != E; ++I) { EHInfo &info = *I; info.FDESym = Asm->createTempSymbol ("mono_fde"); streamer.AddComment("mono method idx"); streamer.EmitIntValue (info.MonoMethodIdx, 4); Asm->EmitLabelDifference(info.FDESym, tableSymbol, 4); lastBegin = info.BeginSym; lastEnd = info.EndSym; } // Emit a last entry to simplify binary searches and to enable the computation of // the size of the last function/FDE entry if (Frames.size() == 0) { streamer.EmitIntValue (-1, 4); Asm->EmitLabelDifference(tableSymbol, tableSymbol, 4); } else { // Emit the size of the last function, since it cannot be computed using the next table entry Asm->EmitLabelDifference(lastEnd, lastBegin, 4); Asm->EmitLabelDifference(tableEndSym, tableSymbol, 4); } // CIE // This comes right after the search table Asm->EmitULEB128(1, "CIE Code Alignment Factor"); Asm->EmitSLEB128(stackGrowth, "CIE Data Alignment Factor"); streamer.AddComment("CIE Return Address Column"); // RI can be null if there are no methods if (RI) Asm->EmitInt8(RI->getDwarfRegNum(RI->getRARegister(), true)); Asm->EmitEncodingByte(dwarf::DW_EH_PE_omit, "Personality"); int cfaOffset = 0; // Initial CIE program emitCFIInstructions(streamer, streamer.getContext().getAsmInfo()->getInitialFrameState(), NULL, NULL, cfaOffset, stackGrowth); streamer.AddComment("End of CIE program"); streamer.EmitIntValue(dwarf::DW_CFA_nop, 1); int cieCfaOffset = cfaOffset; // FDEs streamer.AddBlankLine(); for (std::vector::iterator I = Frames.begin(), E = Frames.end(); I != E; ++I) { const EHInfo &info = *I; streamer.EmitLabel(info.FDESym); // Emit augmentation if (info.HasLandingPads || info.FrameReg != -1) { // Need an extra has_augmentation field as the augmentation size is always encoded // in 4 bytes Asm->EmitULEB128(1, "Has augmentation"); MCSymbol *fdeBeginSym = Asm->OutContext.createTempSymbol("mono_fde_aug_begin", info.FunctionNumber); MCSymbol *fdeEndSym = Asm->OutContext.createTempSymbol("mono_fde_aug_end", info.FunctionNumber); streamer.AddComment("Augmentation size"); Asm->EmitLabelDifference(fdeEndSym, fdeBeginSym, 4); streamer.EmitLabel(fdeBeginSym); EmitMonoLSDA (&info); streamer.EmitLabel(fdeEndSym); } else { Asm->EmitULEB128(0, "Has augmentation"); } // Emit unwind info cfaOffset = cieCfaOffset; emitCFIInstructions(streamer, info.Instructions, info.BeginSym, &info.EHLabels, cfaOffset, dataAlignmentFactor); streamer.AddBlankLine(); } streamer.EmitLabel(tableEndSym); Asm->EmitAlignment(3); } void MonoException::beginInstruction(const MachineInstr *MI) { if (MI->getOpcode() == TargetOpcode::CFI_INSTRUCTION) { unsigned CFIIndex = MI->getOperand(0).getCFIIndex(); //outs () << "D: " << CFIIndex << " " << EHLabels.size() << "\n"; /* Emit a label and save the label-cfi index association */ if (CFIIndex != EHLabels.size()) assert (0); MCSymbol *Label = Asm->OutContext.createTempSymbol(); Asm->OutStreamer->EmitLabel(Label); EHLabels.push_back(Label); } }