937 lines
34 KiB
C++
937 lines
34 KiB
C++
//===---- objwriter.cpp -----------------------------------------*- C++ -*-===//
|
|
//
|
|
// object writer
|
|
//
|
|
// Licensed to the .NET Foundation under one or more agreements.
|
|
// The .NET Foundation licenses this file to you under the MIT license.
|
|
// See the LICENSE file in the project root for more information.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
/// \brief Implementation of object writer API for JIT/AOT
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "objwriter.h"
|
|
#include "debugInfo/dwarf/dwarfTypeBuilder.h"
|
|
#include "debugInfo/codeView/codeViewTypeBuilder.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/MC/MCELFStreamer.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.reset(new UserDefinedCodeViewTypesBuilder());
|
|
} else {
|
|
TypeBuilder.reset(new UserDefinedDwarfTypesBuilder());
|
|
}
|
|
|
|
TypeBuilder->SetStreamer(Streamer);
|
|
unsigned TargetPointerSize = AssemblerPrinter->getPointerSize();
|
|
TypeBuilder->SetTargetPointerSize(TargetPointerSize);
|
|
|
|
if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsELF) {
|
|
DwarfGenerator.reset(new DwarfGen());
|
|
DwarfGenerator->SetTypeBuilder(static_cast<UserDefinedDwarfTypesBuilder*>(TypeBuilder.get()));
|
|
}
|
|
|
|
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) {
|
|
if (Streamer->getCurrentSectionOnly()->getKind().isText()) {
|
|
Streamer->EmitInstructionBytes(StringRef(Blob, BlobSize));
|
|
} else {
|
|
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);
|
|
|
|
// A Thumb2 function symbol should be marked with an appropriate ELF
|
|
// attribute to make later computation of a relocation address value correct
|
|
if (GetTriple().getArch() == Triple::thumb &&
|
|
GetTriple().getObjectFormat() == Triple::ELF &&
|
|
Streamer->getCurrentSectionOnly()->getKind().isText()) {
|
|
Streamer->EmitSymbolAttribute(Sym, MCSA_ELF_TypeFunction);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
unsigned ObjectWriter::GetDFSize() {
|
|
return Streamer->getOrCreateDataFragment()->getContents().size();
|
|
}
|
|
|
|
bool ObjectWriter::EmitRelocDirective(const int Offset, StringRef Name, const MCExpr *Expr) {
|
|
const MCExpr *OffsetExpr = MCConstantExpr::create(Offset, *OutContext);
|
|
return Streamer->EmitRelocDirective(*OffsetExpr, Name, Expr, SMLoc());
|
|
}
|
|
|
|
const MCExpr *ObjectWriter::GenTargetExpr(const char *SymbolName,
|
|
MCSymbolRefExpr::VariantKind Kind,
|
|
int Delta, bool IsPCRel, int Size) {
|
|
const MCExpr *TargetExpr = GetSymbolRefExpr(SymbolName, Kind);
|
|
if (IsPCRel && Size != 0) {
|
|
// 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);
|
|
}
|
|
return TargetExpr;
|
|
}
|
|
|
|
int ObjectWriter::EmitSymbolRef(const char *SymbolName,
|
|
RelocType RelocationType, int Delta) {
|
|
bool IsPCRel = 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;
|
|
IsPCRel = true;
|
|
break;
|
|
case RelocType::IMAGE_REL_BASED_RELPTR32:
|
|
Size = 4;
|
|
IsPCRel = true;
|
|
Delta += 4; // size of C# (int) type is always 4 bytes
|
|
break;
|
|
case RelocType::IMAGE_REL_BASED_THUMB_MOV32: {
|
|
const unsigned Offset = GetDFSize();
|
|
const MCExpr *TargetExpr = GenTargetExpr(SymbolName, Kind, Delta);
|
|
EmitRelocDirective(Offset, "R_ARM_THM_MOVW_ABS_NC", TargetExpr);
|
|
EmitRelocDirective(Offset + 4, "R_ARM_THM_MOVT_ABS", TargetExpr);
|
|
return 8;
|
|
}
|
|
case RelocType::IMAGE_REL_BASED_THUMB_BRANCH24: {
|
|
const MCExpr *TargetExpr = GenTargetExpr(SymbolName, Kind, Delta);
|
|
EmitRelocDirective(GetDFSize(), "R_ARM_THM_JUMP24", TargetExpr);
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
const MCExpr *TargetExpr = GenTargetExpr(SymbolName, Kind, Delta, IsPCRel, Size);
|
|
Streamer->EmitValueImpl(TargetExpr, Size, SMLoc(), IsPCRel);
|
|
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::EmitDwarfFunctionInfo(const char *FunctionName,
|
|
int FunctionSize,
|
|
unsigned MethodTypeIndex) {
|
|
if (FuncId == 1) {
|
|
DwarfGenerator->EmitCompileUnit();
|
|
}
|
|
|
|
DwarfGenerator->EmitSubprogramInfo(FunctionName, FunctionSize,
|
|
MethodTypeIndex, DebugVarInfos, DebugEHClauseInfos);
|
|
|
|
DebugVarInfos.clear();
|
|
DebugEHClauseInfos.clear();
|
|
|
|
FuncId++;
|
|
}
|
|
|
|
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,
|
|
unsigned MethodTypeIndex) {
|
|
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));
|
|
EmitDwarfFunctionInfo(FunctionName, FunctionSize, MethodTypeIndex);
|
|
}
|
|
// 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::EmitDebugEHClause(unsigned TryOffset, unsigned TryLength,
|
|
unsigned HandlerOffset, unsigned HandlerLength) {
|
|
if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsELF) {
|
|
DebugEHClauseInfos.emplace_back(TryOffset, TryLength, HandlerOffset, HandlerLength);
|
|
}
|
|
}
|
|
|
|
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, uint32_t> &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, 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 if (ObjFileInfo->getObjectFileType() == ObjFileInfo->IsELF) {
|
|
DwarfGenerator->EmitAbbrev();
|
|
DwarfGenerator->EmitAranges();
|
|
DwarfGenerator->Finish();
|
|
} else {
|
|
OutContext->setGenDwarfForAssembly(true);
|
|
}
|
|
}
|
|
|
|
unsigned
|
|
ObjectWriter::GetEnumTypeIndex(const EnumTypeDescriptor &TypeDescriptor,
|
|
const EnumRecordTypeDescriptor *TypeRecords) {
|
|
return TypeBuilder->GetEnumTypeIndex(TypeDescriptor, TypeRecords);
|
|
}
|
|
|
|
unsigned
|
|
ObjectWriter::GetClassTypeIndex(const ClassTypeDescriptor &ClassDescriptor) {
|
|
unsigned res = TypeBuilder->GetClassTypeIndex(ClassDescriptor);
|
|
return res;
|
|
}
|
|
|
|
unsigned ObjectWriter::GetCompleteClassTypeIndex(
|
|
const ClassTypeDescriptor &ClassDescriptor,
|
|
const ClassFieldsTypeDescriptior &ClassFieldsDescriptor,
|
|
const DataFieldDescriptor *FieldsDescriptors,
|
|
const StaticDataFieldDescriptor *StaticsDescriptors) {
|
|
unsigned res = TypeBuilder->GetCompleteClassTypeIndex(ClassDescriptor,
|
|
ClassFieldsDescriptor, FieldsDescriptors, StaticsDescriptors);
|
|
return res;
|
|
}
|
|
|
|
unsigned
|
|
ObjectWriter::GetArrayTypeIndex(const ClassTypeDescriptor &ClassDescriptor,
|
|
const ArrayTypeDescriptor &ArrayDescriptor) {
|
|
return TypeBuilder->GetArrayTypeIndex(ClassDescriptor, ArrayDescriptor);
|
|
}
|
|
|
|
unsigned
|
|
ObjectWriter::GetPointerTypeIndex(const PointerTypeDescriptor& PointerDescriptor) {
|
|
return TypeBuilder->GetPointerTypeIndex(PointerDescriptor);
|
|
}
|
|
|
|
unsigned
|
|
ObjectWriter::GetMemberFunctionTypeIndex(const MemberFunctionTypeDescriptor& MemberDescriptor,
|
|
uint32_t const *const ArgumentTypes) {
|
|
return TypeBuilder->GetMemberFunctionTypeIndex(MemberDescriptor, ArgumentTypes);
|
|
}
|
|
|
|
unsigned
|
|
ObjectWriter::GetMemberFunctionId(const MemberFunctionIdTypeDescriptor& MemberIdDescriptor) {
|
|
return TypeBuilder->GetMemberFunctionId(MemberIdDescriptor);
|
|
}
|
|
|
|
unsigned
|
|
ObjectWriter::GetPrimitiveTypeIndex(int Type) {
|
|
return TypeBuilder->GetPrimitiveTypeIndex(static_cast<PrimitiveTypeFlags>(Type));
|
|
}
|
|
|
|
void
|
|
ObjectWriter::EmitARMFnStart() {
|
|
MCTargetStreamer &TS = *(Streamer->getTargetStreamer());
|
|
ARMTargetStreamer &ATS = static_cast<ARMTargetStreamer &>(TS);
|
|
|
|
ATS.emitFnStart();
|
|
}
|
|
|
|
void ObjectWriter::EmitARMFnEnd() {
|
|
MCTargetStreamer &TS = *(Streamer->getTargetStreamer());
|
|
ARMTargetStreamer &ATS = static_cast<ARMTargetStreamer &>(TS);
|
|
|
|
ATS.emitFnEnd();
|
|
}
|
|
|
|
void ObjectWriter::EmitARMExIdxLsda(const char *LsdaBlobSymbolName)
|
|
{
|
|
MCTargetStreamer &TS = *(Streamer->getTargetStreamer());
|
|
ARMTargetStreamer &ATS = static_cast<ARMTargetStreamer &>(TS);
|
|
|
|
MCSymbol *T = OutContext->getOrCreateSymbol(LsdaBlobSymbolName);
|
|
Assembler->registerSymbol(*T);
|
|
|
|
ATS.emitLsda(T);
|
|
}
|
|
|
|
void ObjectWriter::EmitARMExIdxCode(int Offset, const char *Blob)
|
|
{
|
|
MCTargetStreamer &TS = *(Streamer->getTargetStreamer());
|
|
ARMTargetStreamer &ATS = static_cast<ARMTargetStreamer &>(TS);
|
|
SmallVector<unsigned, 4> RegList;
|
|
|
|
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");
|
|
ATS.emitPad(CfiCode->Offset);
|
|
break;
|
|
case CFI_REL_OFFSET:
|
|
RegList.push_back(CfiCode->DwarfReg + 14); // See ARMRegEncodingTable in ARMGenRegisterInfo.inc by getEncodingValue
|
|
ATS.emitRegSave(RegList, false);
|
|
break;
|
|
case CFI_DEF_CFA_REGISTER:
|
|
assert(CfiCode->Offset == 0 &&
|
|
"Unexpected Offset Value for OpDefCfaRegister");
|
|
ATS.emitMovSP(CfiCode->DwarfReg + 14, 0); // See ARMRegEncodingTable in ARMGenRegisterInfo.inc by getEncodingValue
|
|
break;
|
|
default:
|
|
assert(false && "Unrecognized CFI");
|
|
break;
|
|
}
|
|
}
|