diff options
author | Jan Kotas <jkotas@microsoft.com> | 2017-11-09 03:20:39 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-09 03:20:39 +0300 |
commit | 162cead08753e6440d7e90ec4fb453a1a651785a (patch) | |
tree | dbb0478e51bd5b376386e6d0a38df7037fe51526 /src/Native/ObjWriter/objwriter.cpp | |
parent | 2bc2edc1e2b1b0e835366668b53ccfc48ece0949 (diff) | |
parent | 0566d28cd25fb5a8b708b94dd9ba7960a75e3b07 (diff) |
Merge pull request #4894 from dotnet/master
Merge master to nmirror
Diffstat (limited to 'src/Native/ObjWriter/objwriter.cpp')
-rw-r--r-- | src/Native/ObjWriter/objwriter.cpp | 806 |
1 files changed, 806 insertions, 0 deletions
diff --git a/src/Native/ObjWriter/objwriter.cpp b/src/Native/ObjWriter/objwriter.cpp new file mode 100644 index 000000000..5f22998a2 --- /dev/null +++ b/src/Native/ObjWriter/objwriter.cpp @@ -0,0 +1,806 @@ +//===---- objwriter.cpp --------------------------------*- C++ -*-===// +// +// object writer +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. +// See LICENSE file in the project root for full license information. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Implementation of object writer API for JIT/AOT +/// +//===----------------------------------------------------------------------===// + +#include "objwriter.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/Line.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDwarf.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCParser/AsmLexer.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSectionCOFF.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCObjectStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compression.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/Win64EH.h" +#include "llvm/Target/TargetMachine.h" + +using namespace llvm; +using namespace llvm::codeview; + +bool error(const Twine &Error) { + errs() << Twine("error: ") + Error + "\n"; + return false; +} + +void ObjectWriter::InitTripleName() { + TripleName = sys::getDefaultTargetTriple(); +} + +Triple ObjectWriter::GetTriple() { + Triple TheTriple(TripleName); + + if (TheTriple.getOS() == Triple::OSType::Darwin) { + TheTriple = Triple( + TheTriple.getArchName(), TheTriple.getVendorName(), "darwin", + TheTriple + .getEnvironmentName()); // it is workaround for llvm bug + // https://bugs.llvm.org//show_bug.cgi?id=24927. + } + return TheTriple; +} + +bool ObjectWriter::Init(llvm::StringRef ObjectFilePath) { + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + // Initialize targets + InitializeNativeTarget(); + InitializeNativeTargetAsmPrinter(); + + TargetMOptions = InitMCTargetOptionsFromFlags(); + + InitTripleName(); + Triple TheTriple = GetTriple(); + + // Get the target specific parser. + std::string TargetError; + const Target *TheTarget = + TargetRegistry::lookupTarget(TripleName, TargetError); + if (!TheTarget) { + return error("Unable to create target for " + ObjectFilePath + ": " + + TargetError); + } + + std::error_code EC; + OS.reset(new raw_fd_ostream(ObjectFilePath, EC, sys::fs::F_None)); + if (EC) + return error("Unable to create file for " + ObjectFilePath + ": " + + EC.message()); + + RegisterInfo.reset(TheTarget->createMCRegInfo(TripleName)); + if (!RegisterInfo) + return error("Unable to create target register info!"); + + AsmInfo.reset(TheTarget->createMCAsmInfo(*RegisterInfo, TripleName)); + if (!AsmInfo) + return error("Unable to create target asm info!"); + + ObjFileInfo.reset(new MCObjectFileInfo); + OutContext.reset( + new MCContext(AsmInfo.get(), RegisterInfo.get(), ObjFileInfo.get())); + ObjFileInfo->InitMCObjectFileInfo(TheTriple, false, CodeModel::Default, + *OutContext); + + InstrInfo.reset(TheTarget->createMCInstrInfo()); + if (!InstrInfo) + return error("no instr info info for target " + TripleName); + + std::string FeaturesStr; + std::string MCPU; + SubtargetInfo.reset( + TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); + if (!SubtargetInfo) + return error("no subtarget info for target " + TripleName); + + CodeEmitter = + TheTarget->createMCCodeEmitter(*InstrInfo, *RegisterInfo, *OutContext); + if (!CodeEmitter) + return error("no code emitter for target " + TripleName); + + AsmBackend = TheTarget->createMCAsmBackend(*RegisterInfo, TripleName, MCPU, + TargetMOptions); + if (!AsmBackend) + return error("no asm backend for target " + TripleName); + + Streamer = (MCObjectStreamer *)TheTarget->createMCObjectStreamer( + TheTriple, *OutContext, *AsmBackend, *OS, CodeEmitter, *SubtargetInfo, + RelaxAll, + /*IncrementalLinkerCompatible*/ true, + /*DWARFMustBeAtTheEnd*/ false); + if (!Streamer) + return error("no object streamer for target " + TripleName); + Assembler = &Streamer->getAssembler(); + + TMachine.reset(TheTarget->createTargetMachine(TripleName, MCPU, FeaturesStr, + TargetOptions(), None)); + if (!TMachine) + return error("no target machine for target " + TripleName); + + AssemblerPrinter.reset(TheTarget->createAsmPrinter( + *TMachine, std::unique_ptr<MCStreamer>(Streamer))); + if (!AssemblerPrinter) + return error("no asm printer for target " + TripleName); + + FrameOpened = false; + FuncId = 1; + + SetCodeSectionAttribute("text", CustomSectionAttributes_Executable, nullptr); + + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + TypeBuilder.SetStreamer(Streamer); + unsigned TargetPointerSize = AssemblerPrinter->getPointerSize(); + TypeBuilder.SetTargetPointerSize(TargetPointerSize); + } + + return true; +} + +void ObjectWriter::Finish() { Streamer->Finish(); } + +void ObjectWriter::SwitchSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + MCSection *Section = GetSection(SectionName, attributes, ComdatName); + Streamer->SwitchSection(Section); + if (Sections.count(Section) == 0) { + Sections.insert(Section); + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsMachO) { + assert(!Section->getBeginSymbol()); + // Output a DWARF linker-local symbol. + // This symbol is used as a base for other symbols in a section. + MCSymbol *SectionStartSym = OutContext->createTempSymbol(); + Streamer->EmitLabel(SectionStartSym); + Section->setBeginSymbol(SectionStartSym); + } + } +} + +MCSection *ObjectWriter::GetSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + MCSection *Section = nullptr; + + if (strcmp(SectionName, "text") == 0) { + Section = ObjFileInfo->getTextSection(); + } else if (strcmp(SectionName, "data") == 0) { + Section = ObjFileInfo->getDataSection(); + } else if (strcmp(SectionName, "rdata") == 0) { + Section = ObjFileInfo->getReadOnlySection(); + } else if (strcmp(SectionName, "xdata") == 0) { + Section = ObjFileInfo->getXDataSection(); + } else { + Section = GetSpecificSection(SectionName, attributes, ComdatName); + } + assert(Section); + return Section; +} + +MCSection *ObjectWriter::GetSpecificSection(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + Triple TheTriple(TripleName); + MCSection *Section = nullptr; + SectionKind Kind = (attributes & CustomSectionAttributes_Executable) + ? SectionKind::getText() + : (attributes & CustomSectionAttributes_Writeable) + ? SectionKind::getData() + : SectionKind::getReadOnly(); + switch (TheTriple.getObjectFormat()) { + case Triple::MachO: { + unsigned typeAndAttributes = 0; + if (attributes & CustomSectionAttributes_MachO_Init_Func_Pointers) { + typeAndAttributes |= MachO::SectionType::S_MOD_INIT_FUNC_POINTERS; + } + Section = OutContext->getMachOSection( + (attributes & CustomSectionAttributes_Executable) ? "__TEXT" : "__DATA", + SectionName, typeAndAttributes, Kind); + break; + } + case Triple::COFF: { + unsigned Characteristics = COFF::IMAGE_SCN_MEM_READ; + + if (attributes & CustomSectionAttributes_Executable) { + Characteristics |= COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE; + } else if (attributes & CustomSectionAttributes_Writeable) { + Characteristics |= + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_WRITE; + } else { + Characteristics |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; + } + + if (ComdatName != nullptr) { + Section = OutContext->getCOFFSection( + SectionName, Characteristics | COFF::IMAGE_SCN_LNK_COMDAT, Kind, + ComdatName, COFF::COMDATType::IMAGE_COMDAT_SELECT_ANY); + } else { + Section = OutContext->getCOFFSection(SectionName, Characteristics, Kind); + } + break; + } + case Triple::ELF: { + unsigned Flags = ELF::SHF_ALLOC; + if (ComdatName != nullptr) { + MCSymbolELF *GroupSym = + cast<MCSymbolELF>(OutContext->getOrCreateSymbol(ComdatName)); + OutContext->createELFGroupSection(GroupSym); + Flags |= ELF::SHF_GROUP; + } + if (attributes & CustomSectionAttributes_Executable) { + Flags |= ELF::SHF_EXECINSTR; + } else if (attributes & CustomSectionAttributes_Writeable) { + Flags |= ELF::SHF_WRITE; + } + Section = + OutContext->getELFSection(SectionName, ELF::SHT_PROGBITS, Flags, 0, + ComdatName != nullptr ? ComdatName : ""); + break; + } + default: + error("Unknown output format for target " + TripleName); + break; + } + return Section; +} + +void ObjectWriter::SetCodeSectionAttribute(const char *SectionName, + CustomSectionAttributes attributes, + const char *ComdatName) { + MCSection *Section = GetSection(SectionName, attributes, ComdatName); + + assert(!Section->hasInstructions()); + Section->setHasInstructions(true); + if (ObjFileInfo->getObjectFileType() != ObjFileInfo->IsCOFF) { + OutContext->addGenDwarfSection(Section); + } +} + +void ObjectWriter::EmitAlignment(int ByteAlignment) { + Streamer->EmitValueToAlignment(ByteAlignment, 0x90 /* Nop */); +} + +void ObjectWriter::EmitBlob(int BlobSize, const char *Blob) { + Streamer->EmitBytes(StringRef(Blob, BlobSize)); +} + +void ObjectWriter::EmitIntValue(uint64_t Value, unsigned Size) { + Streamer->EmitIntValue(Value, Size); +} + +void ObjectWriter::EmitSymbolDef(const char *SymbolName) { + MCSymbol *Sym = OutContext->getOrCreateSymbol(Twine(SymbolName)); + Streamer->EmitSymbolAttribute(Sym, MCSA_Global); + Streamer->EmitLabel(Sym); +} + +const MCSymbolRefExpr * +ObjectWriter::GetSymbolRefExpr(const char *SymbolName, + MCSymbolRefExpr::VariantKind Kind) { + // Create symbol reference + MCSymbol *T = OutContext->getOrCreateSymbol(SymbolName); + Assembler->registerSymbol(*T); + return MCSymbolRefExpr::create(T, Kind, *OutContext); +} + +int ObjectWriter::EmitSymbolRef(const char *SymbolName, + RelocType RelocationType, int Delta) { + bool IsPCRelative = false; + int Size = 0; + MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None; + + // Convert RelocationType to MCSymbolRefExpr + switch (RelocationType) { + case RelocType::IMAGE_REL_BASED_ABSOLUTE: + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF); + Kind = MCSymbolRefExpr::VK_COFF_IMGREL32; + Size = 4; + break; + case RelocType::IMAGE_REL_BASED_HIGHLOW: + Size = 4; + break; + case RelocType::IMAGE_REL_BASED_DIR64: + Size = 8; + break; + case RelocType::IMAGE_REL_BASED_REL32: + Size = 4; + IsPCRelative = true; + break; + default: + assert(false && "NYI RelocationType!"); + } + + const MCExpr *TargetExpr = GetSymbolRefExpr(SymbolName, Kind); + + if (IsPCRelative) { + // If the fixup is pc-relative, we need to bias the value to be relative to + // the start of the field, not the end of the field + TargetExpr = MCBinaryExpr::createSub( + TargetExpr, MCConstantExpr::create(Size, *OutContext), *OutContext); + } + + if (Delta != 0) { + TargetExpr = MCBinaryExpr::createAdd( + TargetExpr, MCConstantExpr::create(Delta, *OutContext), *OutContext); + } + Streamer->EmitValueImpl(TargetExpr, Size, SMLoc(), IsPCRelative); + return Size; +} + +void ObjectWriter::EmitWinFrameInfo(const char *FunctionName, int StartOffset, + int EndOffset, const char *BlobSymbolName) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF); + + // .pdata emission + MCSection *Section = ObjFileInfo->getPDataSection(); + + // If the function was emitted to a Comdat section, create an associative + // section to place the frame info in. This is due to the Windows linker + // requirement that a function and its unwind info come from the same + // object file. + MCSymbol *Fn = OutContext->getOrCreateSymbol(Twine(FunctionName)); + const MCSectionCOFF *FunctionSection = cast<MCSectionCOFF>(&Fn->getSection()); + if (FunctionSection->getCharacteristics() & COFF::IMAGE_SCN_LNK_COMDAT) { + Section = OutContext->getAssociativeCOFFSection( + cast<MCSectionCOFF>(Section), FunctionSection->getCOMDATSymbol()); + } + + Streamer->SwitchSection(Section); + Streamer->EmitValueToAlignment(4); + + const MCExpr *BaseRefRel = + GetSymbolRefExpr(FunctionName, MCSymbolRefExpr::VK_COFF_IMGREL32); + + // start Offset + const MCExpr *StartOfs = MCConstantExpr::create(StartOffset, *OutContext); + Streamer->EmitValue( + MCBinaryExpr::createAdd(BaseRefRel, StartOfs, *OutContext), 4); + + // end Offset + const MCExpr *EndOfs = MCConstantExpr::create(EndOffset, *OutContext); + Streamer->EmitValue(MCBinaryExpr::createAdd(BaseRefRel, EndOfs, *OutContext), + 4); + + // frame symbol reference + Streamer->EmitValue( + GetSymbolRefExpr(BlobSymbolName, MCSymbolRefExpr::VK_COFF_IMGREL32), 4); +} + +void ObjectWriter::EmitCFIStart(int Offset) { + assert(!FrameOpened && "frame should be closed before CFIStart"); + Streamer->EmitCFIStartProc(false); + FrameOpened = true; +} + +void ObjectWriter::EmitCFIEnd(int Offset) { + assert(FrameOpened && "frame should be opened before CFIEnd"); + Streamer->EmitCFIEndProc(); + FrameOpened = false; +} + +void ObjectWriter::EmitCFILsda(const char *LsdaBlobSymbolName) { + assert(FrameOpened && "frame should be opened before CFILsda"); + + // Create symbol reference + MCSymbol *T = OutContext->getOrCreateSymbol(LsdaBlobSymbolName); + Assembler->registerSymbol(*T); + Streamer->EmitCFILsda(T, llvm::dwarf::Constants::DW_EH_PE_pcrel | + llvm::dwarf::Constants::DW_EH_PE_sdata4); +} + +void ObjectWriter::EmitCFICode(int Offset, const char *Blob) { + assert(FrameOpened && "frame should be opened before CFICode"); + + const CFI_CODE *CfiCode = (const CFI_CODE *)Blob; + switch (CfiCode->CfiOpCode) { + case CFI_ADJUST_CFA_OFFSET: + assert(CfiCode->DwarfReg == DWARF_REG_ILLEGAL && + "Unexpected Register Value for OpAdjustCfaOffset"); + Streamer->EmitCFIAdjustCfaOffset(CfiCode->Offset); + break; + case CFI_REL_OFFSET: + Streamer->EmitCFIRelOffset(CfiCode->DwarfReg, CfiCode->Offset); + break; + case CFI_DEF_CFA_REGISTER: + assert(CfiCode->Offset == 0 && + "Unexpected Offset Value for OpDefCfaRegister"); + Streamer->EmitCFIDefCfaRegister(CfiCode->DwarfReg); + break; + default: + assert(false && "Unrecognized CFI"); + break; + } +} + +void ObjectWriter::EmitLabelDiff(const MCSymbol *From, const MCSymbol *To, + unsigned int Size) { + MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None; + const MCExpr *FromRef = MCSymbolRefExpr::create(From, Variant, *OutContext), + *ToRef = MCSymbolRefExpr::create(To, Variant, *OutContext); + const MCExpr *AddrDelta = + MCBinaryExpr::create(MCBinaryExpr::Sub, ToRef, FromRef, *OutContext); + Streamer->EmitValue(AddrDelta, Size); +} + +void ObjectWriter::EmitSymRecord(int Size, SymbolRecordKind SymbolKind) { + RecordPrefix Rec; + Rec.RecordLen = ulittle16_t(Size + sizeof(ulittle16_t)); + Rec.RecordKind = ulittle16_t((uint16_t)SymbolKind); + Streamer->EmitBytes(StringRef((char *)&Rec, sizeof(Rec))); +} + +void ObjectWriter::EmitCOFFSecRel32Value(MCExpr const *Value) { + MCDataFragment *DF = Streamer->getOrCreateDataFragment(); + MCFixup Fixup = MCFixup::create(DF->getContents().size(), Value, FK_SecRel_4); + DF->getFixups().push_back(Fixup); + DF->getContents().resize(DF->getContents().size() + 4, 0); +} + +void ObjectWriter::EmitVarDefRange(const MCSymbol *Fn, + const LocalVariableAddrRange &Range) { + const MCSymbolRefExpr *BaseSym = MCSymbolRefExpr::create(Fn, *OutContext); + const MCExpr *Offset = MCConstantExpr::create(Range.OffsetStart, *OutContext); + const MCExpr *Expr = MCBinaryExpr::createAdd(BaseSym, Offset, *OutContext); + EmitCOFFSecRel32Value(Expr); + Streamer->EmitCOFFSectionIndex(Fn); + Streamer->EmitIntValue(Range.Range, 2); +} + +void ObjectWriter::EmitCVDebugVarInfo(const MCSymbol *Fn, + const DebugVarInfo LocInfos[], + int NumVarInfos) { + for (int I = 0; I < NumVarInfos; I++) { + // Emit an S_LOCAL record + DebugVarInfo Var = LocInfos[I]; + TypeIndex Type = TypeIndex(Var.TypeIndex); + LocalSymFlags Flags = LocalSymFlags::None; + unsigned SizeofSym = sizeof(Type) + sizeof(Flags); + unsigned NameLength = Var.Name.length() + 1; + EmitSymRecord(SizeofSym + NameLength, SymbolRecordKind::LocalSym); + if (Var.IsParam) { + Flags |= LocalSymFlags::IsParameter; + } + Streamer->EmitBytes(StringRef((char *)&Type, sizeof(Type))); + Streamer->EmitIntValue(static_cast<uint16_t>(Flags), sizeof(Flags)); + Streamer->EmitBytes(StringRef(Var.Name.c_str(), NameLength)); + + for (const auto &Range : Var.Ranges) { + // Emit a range record + switch (Range.loc.vlType) { + case ICorDebugInfo::VLT_REG: + case ICorDebugInfo::VLT_REG_FP: { + + // Currently only support integer registers. + // TODO: support xmm registers + if (Range.loc.vlReg.vlrReg >= + sizeof(cvRegMapAmd64) / sizeof(cvRegMapAmd64[0])) { + break; + } + SymbolRecordKind SymbolKind = SymbolRecordKind::DefRangeRegisterSym; + unsigned SizeofDefRangeRegisterSym = sizeof(DefRangeRegisterSym::Hdr) + + sizeof(DefRangeRegisterSym::Range); + EmitSymRecord(SizeofDefRangeRegisterSym, SymbolKind); + + DefRangeRegisterSym DefRangeRegisterSymbol(SymbolKind); + DefRangeRegisterSymbol.Range.OffsetStart = Range.startOffset; + DefRangeRegisterSymbol.Range.Range = + Range.endOffset - Range.startOffset; + DefRangeRegisterSymbol.Range.ISectStart = 0; + DefRangeRegisterSymbol.Hdr.Register = + cvRegMapAmd64[Range.loc.vlReg.vlrReg]; + unsigned Length = sizeof(DefRangeRegisterSymbol.Hdr); + Streamer->EmitBytes( + StringRef((char *)&DefRangeRegisterSymbol.Hdr, Length)); + EmitVarDefRange(Fn, DefRangeRegisterSymbol.Range); + break; + } + + case ICorDebugInfo::VLT_STK: { + + // TODO: support REGNUM_AMBIENT_SP + if (Range.loc.vlStk.vlsBaseReg >= + sizeof(cvRegMapAmd64) / sizeof(cvRegMapAmd64[0])) { + break; + } + + assert(Range.loc.vlStk.vlsBaseReg < + sizeof(cvRegMapAmd64) / sizeof(cvRegMapAmd64[0]) && + "Register number should be in the range of [REGNUM_RAX, " + "REGNUM_R15]."); + + SymbolRecordKind SymbolKind = SymbolRecordKind::DefRangeRegisterRelSym; + unsigned SizeofDefRangeRegisterRelSym = + sizeof(DefRangeRegisterRelSym::Hdr) + + sizeof(DefRangeRegisterRelSym::Range); + EmitSymRecord(SizeofDefRangeRegisterRelSym, SymbolKind); + + DefRangeRegisterRelSym DefRangeRegisterRelSymbol(SymbolKind); + DefRangeRegisterRelSymbol.Range.OffsetStart = Range.startOffset; + DefRangeRegisterRelSymbol.Range.Range = + Range.endOffset - Range.startOffset; + DefRangeRegisterRelSymbol.Range.ISectStart = 0; + DefRangeRegisterRelSymbol.Hdr.Register = + cvRegMapAmd64[Range.loc.vlStk.vlsBaseReg]; + DefRangeRegisterRelSymbol.Hdr.BasePointerOffset = + Range.loc.vlStk.vlsOffset; + + unsigned Length = sizeof(DefRangeRegisterRelSymbol.Hdr); + Streamer->EmitBytes( + StringRef((char *)&DefRangeRegisterRelSymbol.Hdr, Length)); + EmitVarDefRange(Fn, DefRangeRegisterRelSymbol.Range); + break; + } + + case ICorDebugInfo::VLT_REG_BYREF: + case ICorDebugInfo::VLT_STK_BYREF: + case ICorDebugInfo::VLT_REG_REG: + case ICorDebugInfo::VLT_REG_STK: + case ICorDebugInfo::VLT_STK_REG: + case ICorDebugInfo::VLT_STK2: + case ICorDebugInfo::VLT_FPSTK: + case ICorDebugInfo::VLT_FIXED_VA: + // TODO: for optimized debugging + break; + + default: + assert(false && "Unknown varloc type!"); + break; + } + } + } +} + +void ObjectWriter::EmitCVDebugFunctionInfo(const char *FunctionName, + int FunctionSize) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF); + + // Mark the end of function. + MCSymbol *FnEnd = OutContext->createTempSymbol(); + Streamer->EmitLabel(FnEnd); + + MCSection *Section = ObjFileInfo->getCOFFDebugSymbolsSection(); + Streamer->SwitchSection(Section); + // Emit debug section magic before the first entry. + if (FuncId == 1) { + Streamer->EmitIntValue(COFF::DEBUG_SECTION_MAGIC, 4); + } + MCSymbol *Fn = OutContext->getOrCreateSymbol(Twine(FunctionName)); + + // Emit a symbol subsection, required by VS2012+ to find function boundaries. + MCSymbol *SymbolsBegin = OutContext->createTempSymbol(), + *SymbolsEnd = OutContext->createTempSymbol(); + Streamer->EmitIntValue(unsigned(DebugSubsectionKind::Symbols), 4); + EmitLabelDiff(SymbolsBegin, SymbolsEnd); + Streamer->EmitLabel(SymbolsBegin); + { + ProcSym ProcSymbol(SymbolRecordKind::GlobalProcIdSym); + ProcSymbol.CodeSize = FunctionSize; + ProcSymbol.DbgEnd = FunctionSize; + + unsigned FunctionNameLength = strlen(FunctionName) + 1; + unsigned HeaderSize = + sizeof(ProcSymbol.Parent) + sizeof(ProcSymbol.End) + + sizeof(ProcSymbol.Next) + sizeof(ProcSymbol.CodeSize) + + sizeof(ProcSymbol.DbgStart) + sizeof(ProcSymbol.DbgEnd) + + sizeof(ProcSymbol.FunctionType); + unsigned SymbolSize = HeaderSize + 4 + 2 + 1 + FunctionNameLength; + EmitSymRecord(SymbolSize, SymbolRecordKind::GlobalProcIdSym); + + Streamer->EmitBytes(StringRef((char *)&ProcSymbol.Parent, HeaderSize)); + // Emit relocation + Streamer->EmitCOFFSecRel32(Fn, 0); + Streamer->EmitCOFFSectionIndex(Fn); + + // Emit flags + Streamer->EmitIntValue(0, 1); + + // Emit the function display name as a null-terminated string. + + Streamer->EmitBytes(StringRef(FunctionName, FunctionNameLength)); + + // Emit local var info + int NumVarInfos = DebugVarInfos.size(); + if (NumVarInfos > 0) { + EmitCVDebugVarInfo(Fn, &DebugVarInfos[0], NumVarInfos); + DebugVarInfos.clear(); + } + + // We're done with this function. + EmitSymRecord(0, SymbolRecordKind::ProcEnd); + } + + Streamer->EmitLabel(SymbolsEnd); + + // Every subsection must be aligned to a 4-byte boundary. + Streamer->EmitValueToAlignment(4); + + // We have an assembler directive that takes care of the whole line table. + // We also increase function id for the next function. + Streamer->EmitCVLinetableDirective(FuncId++, Fn, FnEnd); +} + +void ObjectWriter::EmitDebugFileInfo(int FileId, const char *FileName) { + assert(FileId > 0 && "FileId should be greater than 0."); + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + Streamer->EmitCVFileDirective(FileId, FileName); + } else { + Streamer->EmitDwarfFileDirective(FileId, "", FileName); + } +} + +void ObjectWriter::EmitDebugFunctionInfo(const char *FunctionName, + int FunctionSize) { + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + Streamer->EmitCVFuncIdDirective(FuncId); + EmitCVDebugFunctionInfo(FunctionName, FunctionSize); + } else { + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsELF) { + MCSymbol *Sym = OutContext->getOrCreateSymbol(Twine(FunctionName)); + Streamer->EmitSymbolAttribute(Sym, MCSA_ELF_TypeFunction); + Streamer->emitELFSize(Sym, + MCConstantExpr::create(FunctionSize, *OutContext)); + } + // TODO: Should test it for Macho. + } +} + +void ObjectWriter::EmitDebugVar(char *Name, int TypeIndex, bool IsParm, + int RangeCount, + const ICorDebugInfo::NativeVarInfo *Ranges) { + assert(RangeCount != 0); + DebugVarInfo NewVar(Name, TypeIndex, IsParm); + + for (int I = 0; I < RangeCount; I++) { + assert(Ranges[0].varNumber == Ranges[I].varNumber); + NewVar.Ranges.push_back(Ranges[I]); + } + + DebugVarInfos.push_back(NewVar); +} + +void ObjectWriter::EmitDebugLoc(int NativeOffset, int FileId, int LineNumber, + int ColNumber) { + assert(FileId > 0 && "FileId should be greater than 0."); + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + Streamer->EmitCVFuncIdDirective(FuncId); + Streamer->EmitCVLocDirective(FuncId, FileId, LineNumber, ColNumber, false, + true, "", SMLoc()); + } else { + Streamer->EmitDwarfLocDirective(FileId, LineNumber, ColNumber, 1, 0, 0, ""); + } +} + +void ObjectWriter::EmitCVUserDefinedTypesSymbols() { + const auto &UDTs = TypeBuilder.GetUDTs(); + if (UDTs.empty()) { + return; + } + MCSection *Section = ObjFileInfo->getCOFFDebugSymbolsSection(); + Streamer->SwitchSection(Section); + + MCSymbol *SymbolsBegin = OutContext->createTempSymbol(), + *SymbolsEnd = OutContext->createTempSymbol(); + Streamer->EmitIntValue(unsigned(DebugSubsectionKind::Symbols), 4); + EmitLabelDiff(SymbolsBegin, SymbolsEnd); + Streamer->EmitLabel(SymbolsBegin); + + for (const std::pair<std::string, codeview::TypeIndex> &UDT : UDTs) { + unsigned NameLength = UDT.first.length() + 1; + unsigned RecordLength = 2 + 4 + NameLength; + Streamer->EmitIntValue(RecordLength, 2); + Streamer->EmitIntValue(unsigned(SymbolKind::S_UDT), 2); + Streamer->EmitIntValue(UDT.second.getIndex(), 4); + Streamer->EmitBytes(StringRef(UDT.first.c_str(), NameLength)); + } + Streamer->EmitLabel(SymbolsEnd); + Streamer->EmitValueToAlignment(4); +} + +void ObjectWriter::EmitDebugModuleInfo() { + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + TypeBuilder.EmitTypeInformation(ObjFileInfo->getCOFFDebugTypesSection()); + EmitCVUserDefinedTypesSymbols(); + } + + // Ensure ending all sections. + for (auto Section : Sections) { + Streamer->endSection(Section); + } + + if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF) { + MCSection *Section = ObjFileInfo->getCOFFDebugSymbolsSection(); + Streamer->SwitchSection(Section); + Streamer->EmitCVFileChecksumsDirective(); + Streamer->EmitCVStringTableDirective(); + } else { + OutContext->setGenDwarfForAssembly(true); + } +} + +unsigned +ObjectWriter::GetEnumTypeIndex(const EnumTypeDescriptor &TypeDescriptor, + const EnumRecordTypeDescriptor *TypeRecords) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF && + "only COFF is supported now"); + return TypeBuilder.GetEnumTypeIndex(TypeDescriptor, TypeRecords); +} + +unsigned +ObjectWriter::GetClassTypeIndex(const ClassTypeDescriptor &ClassDescriptor) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF && + "only COFF is supported now"); + return TypeBuilder.GetClassTypeIndex(ClassDescriptor); +} + +unsigned ObjectWriter::GetCompleteClassTypeIndex( + const ClassTypeDescriptor &ClassDescriptor, + const ClassFieldsTypeDescriptior &ClassFieldsDescriptor, + const DataFieldDescriptor *FieldsDescriptors) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF && + "only COFF is supported now"); + return TypeBuilder.GetCompleteClassTypeIndex( + ClassDescriptor, ClassFieldsDescriptor, FieldsDescriptors); +} + +unsigned +ObjectWriter::GetArrayTypeIndex(const ClassTypeDescriptor &ClassDescriptor, + const ArrayTypeDescriptor &ArrayDescriptor) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF && + "only COFF is supported now"); + return TypeBuilder.GetArrayTypeIndex(ClassDescriptor, ArrayDescriptor); +} + +unsigned +ObjectWriter::GetPointerTypeIndex(const PointerTypeDescriptor& PointerDescriptor) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF && + "only COFF is supported now"); + return TypeBuilder.GetPointerTypeIndex(PointerDescriptor); +} + +unsigned +ObjectWriter::GetMemberFunctionTypeIndex(const MemberFunctionTypeDescriptor& MemberDescriptor, + uint32_t const *const ArgumentTypes) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF && + "only COFF is supported now"); + return TypeBuilder.GetMemberFunctionTypeIndex(MemberDescriptor, ArgumentTypes); +} + +unsigned +ObjectWriter::GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor) { + assert(ObjFileInfo->getObjectFileType() == ObjFileInfo->IsCOFF && + "only COFF is supported now"); + return TypeBuilder.GetMemberFunctionId(MemberIdDescriptor); +} + |