| //===- lib/MC/MCObjectStreamer.cpp - Object File MCStreamer Interface -----===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/MC/MCObjectStreamer.h" | |
| #include "llvm/MC/MCAsmBackend.h" | |
| #include "llvm/MC/MCAsmInfo.h" | |
| #include "llvm/MC/MCAssembler.h" | |
| #include "llvm/MC/MCCodeEmitter.h" | |
| #include "llvm/MC/MCCodeView.h" | |
| #include "llvm/MC/MCContext.h" | |
| #include "llvm/MC/MCDwarf.h" | |
| #include "llvm/MC/MCExpr.h" | |
| #include "llvm/MC/MCObjectFileInfo.h" | |
| #include "llvm/MC/MCObjectWriter.h" | |
| #include "llvm/MC/MCSection.h" | |
| #include "llvm/MC/MCSymbol.h" | |
| #include "llvm/MC/MCValue.h" | |
| #include "llvm/Support/ErrorHandling.h" | |
| #include "llvm/Support/SourceMgr.h" | |
| using namespace llvm; | |
| MCObjectStreamer::MCObjectStreamer(MCContext &Context, | |
| std::unique_ptr<MCAsmBackend> TAB, | |
| std::unique_ptr<MCObjectWriter> OW, | |
| std::unique_ptr<MCCodeEmitter> Emitter) | |
| : MCStreamer(Context), | |
| Assembler(std::make_unique<MCAssembler>( | |
| Context, std::move(TAB), std::move(Emitter), std::move(OW))), | |
| EmitEHFrame(true), EmitDebugFrame(false) { | |
| if (Assembler->getBackendPtr()) | |
| setAllowAutoPadding(Assembler->getBackend().allowAutoPadding()); | |
| } | |
| MCObjectStreamer::~MCObjectStreamer() = default; | |
| // AssemblerPtr is used for evaluation of expressions and causes | |
| // difference between asm and object outputs. Return nullptr to in | |
| // inline asm mode to limit divergence to assembly inputs. | |
| MCAssembler *MCObjectStreamer::getAssemblerPtr() { | |
| if (getUseAssemblerInfoForParsing()) | |
| return Assembler.get(); | |
| return nullptr; | |
| } | |
| void MCObjectStreamer::addPendingLabel(MCSymbol* S) { | |
| MCSection *CurSection = getCurrentSectionOnly(); | |
| if (CurSection) { | |
| // Register labels that have not yet been assigned to a Section. | |
| if (!PendingLabels.empty()) { | |
| for (MCSymbol* Sym : PendingLabels) | |
| CurSection->addPendingLabel(Sym); | |
| PendingLabels.clear(); | |
| } | |
| // Add this label to the current Section / Subsection. | |
| CurSection->addPendingLabel(S, CurSubsectionIdx); | |
| // Add this Section to the list of PendingLabelSections. | |
| PendingLabelSections.insert(CurSection); | |
| } else | |
| // There is no Section / Subsection for this label yet. | |
| PendingLabels.push_back(S); | |
| } | |
| void MCObjectStreamer::flushPendingLabels(MCFragment *F, uint64_t FOffset) { | |
| MCSection *CurSection = getCurrentSectionOnly(); | |
| if (!CurSection) { | |
| assert(PendingLabels.empty()); | |
| return; | |
| } | |
| // Register labels that have not yet been assigned to a Section. | |
| if (!PendingLabels.empty()) { | |
| for (MCSymbol* Sym : PendingLabels) | |
| CurSection->addPendingLabel(Sym, CurSubsectionIdx); | |
| PendingLabels.clear(); | |
| } | |
| // Associate a fragment with this label, either the supplied fragment | |
| // or an empty data fragment. | |
| if (F) | |
| CurSection->flushPendingLabels(F, FOffset, CurSubsectionIdx); | |
| else | |
| CurSection->flushPendingLabels(nullptr, 0, CurSubsectionIdx); | |
| } | |
| void MCObjectStreamer::flushPendingLabels() { | |
| // Register labels that have not yet been assigned to a Section. | |
| if (!PendingLabels.empty()) { | |
| MCSection *CurSection = getCurrentSectionOnly(); | |
| assert(CurSection); | |
| for (MCSymbol* Sym : PendingLabels) | |
| CurSection->addPendingLabel(Sym, CurSubsectionIdx); | |
| PendingLabels.clear(); | |
| } | |
| // Assign an empty data fragment to all remaining pending labels. | |
| for (MCSection* Section : PendingLabelSections) | |
| Section->flushPendingLabels(); | |
| } | |
| // When fixup's offset is a forward declared label, e.g.: | |
| // | |
| // .reloc 1f, R_MIPS_JALR, foo | |
| // 1: nop | |
| // | |
| // postpone adding it to Fixups vector until the label is defined and its offset | |
| // is known. | |
| void MCObjectStreamer::resolvePendingFixups() { | |
| for (PendingMCFixup &PendingFixup : PendingFixups) { | |
| if (!PendingFixup.Sym || PendingFixup.Sym->isUndefined ()) { | |
| getContext().reportError(PendingFixup.Fixup.getLoc(), | |
| "unresolved relocation offset"); | |
| continue; | |
| } | |
| flushPendingLabels(PendingFixup.DF, PendingFixup.DF->getContents().size()); | |
| PendingFixup.Fixup.setOffset(PendingFixup.Sym->getOffset() + | |
| PendingFixup.Fixup.getOffset()); | |
| // If the location symbol to relocate is in MCEncodedFragmentWithFixups, | |
| // put the Fixup into location symbol's fragment. Otherwise | |
| // put into PendingFixup.DF | |
| MCFragment *SymFragment = PendingFixup.Sym->getFragment(); | |
| switch (SymFragment->getKind()) { | |
| case MCFragment::FT_Relaxable: | |
| case MCFragment::FT_Dwarf: | |
| case MCFragment::FT_PseudoProbe: | |
| cast<MCEncodedFragmentWithFixups<8, 1>>(SymFragment) | |
| ->getFixups() | |
| .push_back(PendingFixup.Fixup); | |
| break; | |
| case MCFragment::FT_Data: | |
| case MCFragment::FT_CVDefRange: | |
| cast<MCEncodedFragmentWithFixups<32, 4>>(SymFragment) | |
| ->getFixups() | |
| .push_back(PendingFixup.Fixup); | |
| break; | |
| default: | |
| PendingFixup.DF->getFixups().push_back(PendingFixup.Fixup); | |
| break; | |
| } | |
| } | |
| PendingFixups.clear(); | |
| } | |
| // As a compile-time optimization, avoid allocating and evaluating an MCExpr | |
| // tree for (Hi - Lo) when Hi and Lo are offsets into the same fragment. | |
| static std::optional<uint64_t> absoluteSymbolDiff(const MCSymbol *Hi, | |
| const MCSymbol *Lo) { | |
| assert(Hi && Lo); | |
| if (!Hi->getFragment() || Hi->getFragment() != Lo->getFragment() || | |
| Hi->isVariable() || Lo->isVariable()) | |
| return std::nullopt; | |
| return Hi->getOffset() - Lo->getOffset(); | |
| } | |
| void MCObjectStreamer::emitAbsoluteSymbolDiff(const MCSymbol *Hi, | |
| const MCSymbol *Lo, | |
| unsigned Size) { | |
| if (!getAssembler().getContext().getTargetTriple().isRISCV()) | |
| if (std::optional<uint64_t> Diff = absoluteSymbolDiff(Hi, Lo)) | |
| return emitIntValue(*Diff, Size); | |
| MCStreamer::emitAbsoluteSymbolDiff(Hi, Lo, Size); | |
| } | |
| void MCObjectStreamer::emitAbsoluteSymbolDiffAsULEB128(const MCSymbol *Hi, | |
| const MCSymbol *Lo) { | |
| if (!getAssembler().getContext().getTargetTriple().isRISCV()) | |
| if (std::optional<uint64_t> Diff = absoluteSymbolDiff(Hi, Lo)) { | |
| emitULEB128IntValue(*Diff); | |
| return; | |
| } | |
| MCStreamer::emitAbsoluteSymbolDiffAsULEB128(Hi, Lo); | |
| } | |
| void MCObjectStreamer::reset() { | |
| if (Assembler) | |
| Assembler->reset(); | |
| CurInsertionPoint = MCSection::iterator(); | |
| EmitEHFrame = true; | |
| EmitDebugFrame = false; | |
| PendingLabels.clear(); | |
| PendingLabelSections.clear(); | |
| MCStreamer::reset(); | |
| } | |
| void MCObjectStreamer::emitFrames(MCAsmBackend *MAB) { | |
| if (!getNumFrameInfos()) | |
| return; | |
| if (EmitEHFrame) | |
| MCDwarfFrameEmitter::Emit(*this, MAB, true); | |
| if (EmitDebugFrame) | |
| MCDwarfFrameEmitter::Emit(*this, MAB, false); | |
| } | |
| MCFragment *MCObjectStreamer::getCurrentFragment() const { | |
| assert(getCurrentSectionOnly() && "No current section!"); | |
| if (CurInsertionPoint != getCurrentSectionOnly()->getFragmentList().begin()) | |
| return &*std::prev(CurInsertionPoint); | |
| return nullptr; | |
| } | |
| static bool canReuseDataFragment(const MCDataFragment &F, | |
| const MCAssembler &Assembler, | |
| const MCSubtargetInfo *STI) { | |
| if (!F.hasInstructions()) | |
| return true; | |
| // When bundling is enabled, we don't want to add data to a fragment that | |
| // already has instructions (see MCELFStreamer::emitInstToData for details) | |
| if (Assembler.isBundlingEnabled()) | |
| return Assembler.getRelaxAll(); | |
| // If the subtarget is changed mid fragment we start a new fragment to record | |
| // the new STI. | |
| return !STI || F.getSubtargetInfo() == STI; | |
| } | |
| MCDataFragment * | |
| MCObjectStreamer::getOrCreateDataFragment(const MCSubtargetInfo *STI) { | |
| MCDataFragment *F = dyn_cast_or_null<MCDataFragment>(getCurrentFragment()); | |
| if (!F || !canReuseDataFragment(*F, *Assembler, STI)) { | |
| F = new MCDataFragment(); | |
| insert(F); | |
| } | |
| return F; | |
| } | |
| void MCObjectStreamer::visitUsedSymbol(const MCSymbol &Sym) { | |
| Assembler->registerSymbol(Sym); | |
| } | |
| void MCObjectStreamer::emitCFISections(bool EH, bool Debug) { | |
| MCStreamer::emitCFISections(EH, Debug); | |
| EmitEHFrame = EH; | |
| EmitDebugFrame = Debug; | |
| } | |
| void MCObjectStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, | |
| SMLoc Loc) { | |
| MCStreamer::emitValueImpl(Value, Size, Loc); | |
| MCDataFragment *DF = getOrCreateDataFragment(); | |
| flushPendingLabels(DF, DF->getContents().size()); | |
| MCDwarfLineEntry::make(this, getCurrentSectionOnly()); | |
| // Avoid fixups when possible. | |
| int64_t AbsValue; | |
| if (Value->evaluateAsAbsolute(AbsValue, getAssemblerPtr())) { | |
| if (!isUIntN(8 * Size, AbsValue) && !isIntN(8 * Size, AbsValue)) { | |
| getContext().reportError( | |
| Loc, "value evaluated as " + Twine(AbsValue) + " is out of range."); | |
| return; | |
| } | |
| emitIntValue(AbsValue, Size); | |
| return; | |
| } | |
| DF->getFixups().push_back( | |
| MCFixup::create(DF->getContents().size(), Value, | |
| MCFixup::getKindForSize(Size, false), Loc)); | |
| DF->getContents().resize(DF->getContents().size() + Size, 0); | |
| } | |
| MCSymbol *MCObjectStreamer::emitCFILabel() { | |
| MCSymbol *Label = getContext().createTempSymbol("cfi"); | |
| emitLabel(Label); | |
| return Label; | |
| } | |
| void MCObjectStreamer::emitCFIStartProcImpl(MCDwarfFrameInfo &Frame) { | |
| // We need to create a local symbol to avoid relocations. | |
| Frame.Begin = getContext().createTempSymbol(); | |
| emitLabel(Frame.Begin); | |
| } | |
| void MCObjectStreamer::emitCFIEndProcImpl(MCDwarfFrameInfo &Frame) { | |
| Frame.End = getContext().createTempSymbol(); | |
| emitLabel(Frame.End); | |
| } | |
| void MCObjectStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) { | |
| MCStreamer::emitLabel(Symbol, Loc); | |
| getAssembler().registerSymbol(*Symbol); | |
| // If there is a current fragment, mark the symbol as pointing into it. | |
| // Otherwise queue the label and set its fragment pointer when we emit the | |
| // next fragment. | |
| auto *F = dyn_cast_or_null<MCDataFragment>(getCurrentFragment()); | |
| if (F && !(getAssembler().isBundlingEnabled() && | |
| getAssembler().getRelaxAll())) { | |
| Symbol->setFragment(F); | |
| Symbol->setOffset(F->getContents().size()); | |
| } else { | |
| // Assign all pending labels to offset 0 within the dummy "pending" | |
| // fragment. (They will all be reassigned to a real fragment in | |
| // flushPendingLabels()) | |
| Symbol->setOffset(0); | |
| addPendingLabel(Symbol); | |
| } | |
| emitPendingAssignments(Symbol); | |
| } | |
| void MCObjectStreamer::emitPendingAssignments(MCSymbol *Symbol) { | |
| auto Assignments = pendingAssignments.find(Symbol); | |
| if (Assignments != pendingAssignments.end()) { | |
| for (const PendingAssignment &A : Assignments->second) | |
| emitAssignment(A.Symbol, A.Value); | |
| pendingAssignments.erase(Assignments); | |
| } | |
| } | |
| // Emit a label at a previously emitted fragment/offset position. This must be | |
| // within the currently-active section. | |
| void MCObjectStreamer::emitLabelAtPos(MCSymbol *Symbol, SMLoc Loc, | |
| MCFragment *F, uint64_t Offset) { | |
| assert(F->getParent() == getCurrentSectionOnly()); | |
| MCStreamer::emitLabel(Symbol, Loc); | |
| getAssembler().registerSymbol(*Symbol); | |
| auto *DF = dyn_cast_or_null<MCDataFragment>(F); | |
| Symbol->setOffset(Offset); | |
| if (DF) { | |
| Symbol->setFragment(F); | |
| } else { | |
| assert(isa<MCDummyFragment>(F) && | |
| "F must either be an MCDataFragment or the pending MCDummyFragment"); | |
| assert(Offset == 0); | |
| addPendingLabel(Symbol); | |
| } | |
| } | |
| void MCObjectStreamer::emitULEB128Value(const MCExpr *Value) { | |
| int64_t IntValue; | |
| if (Value->evaluateAsAbsolute(IntValue, getAssemblerPtr())) { | |
| emitULEB128IntValue(IntValue); | |
| return; | |
| } | |
| insert(new MCLEBFragment(*Value, false)); | |
| } | |
| void MCObjectStreamer::emitSLEB128Value(const MCExpr *Value) { | |
| int64_t IntValue; | |
| if (Value->evaluateAsAbsolute(IntValue, getAssemblerPtr())) { | |
| emitSLEB128IntValue(IntValue); | |
| return; | |
| } | |
| insert(new MCLEBFragment(*Value, true)); | |
| } | |
| void MCObjectStreamer::emitWeakReference(MCSymbol *Alias, | |
| const MCSymbol *Symbol) { | |
| report_fatal_error("This file format doesn't support weak aliases."); | |
| } | |
| void MCObjectStreamer::changeSection(MCSection *Section, | |
| const MCExpr *Subsection) { | |
| changeSectionImpl(Section, Subsection); | |
| } | |
| bool MCObjectStreamer::changeSectionImpl(MCSection *Section, | |
| const MCExpr *Subsection) { | |
| assert(Section && "Cannot switch to a null section!"); | |
| getContext().clearDwarfLocSeen(); | |
| bool Created = getAssembler().registerSection(*Section); | |
| int64_t IntSubsection = 0; | |
| if (Subsection && | |
| !Subsection->evaluateAsAbsolute(IntSubsection, getAssemblerPtr())) | |
| report_fatal_error("Cannot evaluate subsection number"); | |
| if (IntSubsection < 0 || IntSubsection > 8192) | |
| report_fatal_error("Subsection number out of range"); | |
| CurSubsectionIdx = unsigned(IntSubsection); | |
| CurInsertionPoint = | |
| Section->getSubsectionInsertionPoint(CurSubsectionIdx); | |
| return Created; | |
| } | |
| void MCObjectStreamer::emitAssignment(MCSymbol *Symbol, const MCExpr *Value) { | |
| getAssembler().registerSymbol(*Symbol); | |
| MCStreamer::emitAssignment(Symbol, Value); | |
| emitPendingAssignments(Symbol); | |
| } | |
| void MCObjectStreamer::emitConditionalAssignment(MCSymbol *Symbol, | |
| const MCExpr *Value) { | |
| const MCSymbol *Target = &cast<MCSymbolRefExpr>(*Value).getSymbol(); | |
| // If the symbol already exists, emit the assignment. Otherwise, emit it | |
| // later only if the symbol is also emitted. | |
| if (Target->isRegistered()) | |
| emitAssignment(Symbol, Value); | |
| else | |
| pendingAssignments[Target].push_back({Symbol, Value}); | |
| } | |
| bool MCObjectStreamer::mayHaveInstructions(MCSection &Sec) const { | |
| return Sec.hasInstructions(); | |
| } | |
| void MCObjectStreamer::emitInstruction(const MCInst &Inst, | |
| const MCSubtargetInfo &STI) { | |
| const MCSection &Sec = *getCurrentSectionOnly(); | |
| if (Sec.isVirtualSection()) { | |
| getContext().reportError(Inst.getLoc(), Twine(Sec.getVirtualSectionKind()) + | |
| " section '" + Sec.getName() + | |
| "' cannot have instructions"); | |
| return; | |
| } | |
| getAssembler().getBackend().emitInstructionBegin(*this, Inst, STI); | |
| emitInstructionImpl(Inst, STI); | |
| getAssembler().getBackend().emitInstructionEnd(*this, Inst); | |
| } | |
| void MCObjectStreamer::emitInstructionImpl(const MCInst &Inst, | |
| const MCSubtargetInfo &STI) { | |
| MCStreamer::emitInstruction(Inst, STI); | |
| MCSection *Sec = getCurrentSectionOnly(); | |
| Sec->setHasInstructions(true); | |
| // Now that a machine instruction has been assembled into this section, make | |
| // a line entry for any .loc directive that has been seen. | |
| MCDwarfLineEntry::make(this, getCurrentSectionOnly()); | |
| // If this instruction doesn't need relaxation, just emit it as data. | |
| MCAssembler &Assembler = getAssembler(); | |
| MCAsmBackend &Backend = Assembler.getBackend(); | |
| if (!(Backend.mayNeedRelaxation(Inst, STI) || | |
| Backend.allowEnhancedRelaxation())) { | |
| emitInstToData(Inst, STI); | |
| return; | |
| } | |
| // Otherwise, relax and emit it as data if either: | |
| // - The RelaxAll flag was passed | |
| // - Bundling is enabled and this instruction is inside a bundle-locked | |
| // group. We want to emit all such instructions into the same data | |
| // fragment. | |
| if (Assembler.getRelaxAll() || | |
| (Assembler.isBundlingEnabled() && Sec->isBundleLocked())) { | |
| MCInst Relaxed = Inst; | |
| while (Backend.mayNeedRelaxation(Relaxed, STI)) | |
| Backend.relaxInstruction(Relaxed, STI); | |
| emitInstToData(Relaxed, STI); | |
| return; | |
| } | |
| // Otherwise emit to a separate fragment. | |
| emitInstToFragment(Inst, STI); | |
| } | |
| void MCObjectStreamer::emitInstToFragment(const MCInst &Inst, | |
| const MCSubtargetInfo &STI) { | |
| if (getAssembler().getRelaxAll() && getAssembler().isBundlingEnabled()) | |
| llvm_unreachable("All instructions should have already been relaxed"); | |
| // Always create a new, separate fragment here, because its size can change | |
| // during relaxation. | |
| MCRelaxableFragment *IF = new MCRelaxableFragment(Inst, STI); | |
| insert(IF); | |
| SmallString<128> Code; | |
| raw_svector_ostream VecOS(Code); | |
| getAssembler().getEmitter().encodeInstruction(Inst, VecOS, IF->getFixups(), | |
| STI); | |
| IF->getContents().append(Code.begin(), Code.end()); | |
| } | |
| #ifndef NDEBUG | |
| static const char *const BundlingNotImplementedMsg = | |
| "Aligned bundling is not implemented for this object format"; | |
| #endif | |
| void MCObjectStreamer::emitBundleAlignMode(Align Alignment) { | |
| llvm_unreachable(BundlingNotImplementedMsg); | |
| } | |
| void MCObjectStreamer::emitBundleLock(bool AlignToEnd) { | |
| llvm_unreachable(BundlingNotImplementedMsg); | |
| } | |
| void MCObjectStreamer::emitBundleUnlock() { | |
| llvm_unreachable(BundlingNotImplementedMsg); | |
| } | |
| void MCObjectStreamer::emitDwarfLocDirective(unsigned FileNo, unsigned Line, | |
| unsigned Column, unsigned Flags, | |
| unsigned Isa, | |
| unsigned Discriminator, | |
| StringRef FileName) { | |
| // In case we see two .loc directives in a row, make sure the | |
| // first one gets a line entry. | |
| MCDwarfLineEntry::make(this, getCurrentSectionOnly()); | |
| this->MCStreamer::emitDwarfLocDirective(FileNo, Line, Column, Flags, Isa, | |
| Discriminator, FileName); | |
| } | |
| static const MCExpr *buildSymbolDiff(MCObjectStreamer &OS, const MCSymbol *A, | |
| const MCSymbol *B) { | |
| MCContext &Context = OS.getContext(); | |
| MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None; | |
| const MCExpr *ARef = MCSymbolRefExpr::create(A, Variant, Context); | |
| const MCExpr *BRef = MCSymbolRefExpr::create(B, Variant, Context); | |
| const MCExpr *AddrDelta = | |
| MCBinaryExpr::create(MCBinaryExpr::Sub, ARef, BRef, Context); | |
| return AddrDelta; | |
| } | |
| static void emitDwarfSetLineAddr(MCObjectStreamer &OS, | |
| MCDwarfLineTableParams Params, | |
| int64_t LineDelta, const MCSymbol *Label, | |
| int PointerSize) { | |
| // emit the sequence to set the address | |
| OS.emitIntValue(dwarf::DW_LNS_extended_op, 1); | |
| OS.emitULEB128IntValue(PointerSize + 1); | |
| OS.emitIntValue(dwarf::DW_LNE_set_address, 1); | |
| OS.emitSymbolValue(Label, PointerSize); | |
| // emit the sequence for the LineDelta (from 1) and a zero address delta. | |
| MCDwarfLineAddr::Emit(&OS, Params, LineDelta, 0); | |
| } | |
| void MCObjectStreamer::emitDwarfAdvanceLineAddr(int64_t LineDelta, | |
| const MCSymbol *LastLabel, | |
| const MCSymbol *Label, | |
| unsigned PointerSize) { | |
| if (!LastLabel) { | |
| emitDwarfSetLineAddr(*this, Assembler->getDWARFLinetableParams(), LineDelta, | |
| Label, PointerSize); | |
| return; | |
| } | |
| const MCExpr *AddrDelta = buildSymbolDiff(*this, Label, LastLabel); | |
| int64_t Res; | |
| if (AddrDelta->evaluateAsAbsolute(Res, getAssemblerPtr())) { | |
| MCDwarfLineAddr::Emit(this, Assembler->getDWARFLinetableParams(), LineDelta, | |
| Res); | |
| return; | |
| } | |
| insert(new MCDwarfLineAddrFragment(LineDelta, *AddrDelta)); | |
| } | |
| void MCObjectStreamer::emitDwarfLineEndEntry(MCSection *Section, | |
| MCSymbol *LastLabel) { | |
| // Emit a DW_LNE_end_sequence for the end of the section. | |
| // Use the section end label to compute the address delta and use INT64_MAX | |
| // as the line delta which is the signal that this is actually a | |
| // DW_LNE_end_sequence. | |
| MCSymbol *SectionEnd = endSection(Section); | |
| // Switch back the dwarf line section, in case endSection had to switch the | |
| // section. | |
| MCContext &Ctx = getContext(); | |
| switchSection(Ctx.getObjectFileInfo()->getDwarfLineSection()); | |
| const MCAsmInfo *AsmInfo = Ctx.getAsmInfo(); | |
| emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, SectionEnd, | |
| AsmInfo->getCodePointerSize()); | |
| } | |
| void MCObjectStreamer::emitDwarfAdvanceFrameAddr(const MCSymbol *LastLabel, | |
| const MCSymbol *Label) { | |
| const MCExpr *AddrDelta = buildSymbolDiff(*this, Label, LastLabel); | |
| int64_t Res; | |
| if (AddrDelta->evaluateAsAbsolute(Res, getAssemblerPtr())) { | |
| MCDwarfFrameEmitter::EmitAdvanceLoc(*this, Res); | |
| return; | |
| } | |
| insert(new MCDwarfCallFrameFragment(*AddrDelta)); | |
| } | |
| void MCObjectStreamer::emitCVLocDirective(unsigned FunctionId, unsigned FileNo, | |
| unsigned Line, unsigned Column, | |
| bool PrologueEnd, bool IsStmt, | |
| StringRef FileName, SMLoc Loc) { | |
| // Validate the directive. | |
| if (!checkCVLocSection(FunctionId, FileNo, Loc)) | |
| return; | |
| // Emit a label at the current position and record it in the CodeViewContext. | |
| MCSymbol *LineSym = getContext().createTempSymbol(); | |
| emitLabel(LineSym); | |
| getContext().getCVContext().recordCVLoc(getContext(), LineSym, FunctionId, | |
| FileNo, Line, Column, PrologueEnd, | |
| IsStmt); | |
| } | |
| void MCObjectStreamer::emitCVLinetableDirective(unsigned FunctionId, | |
| const MCSymbol *Begin, | |
| const MCSymbol *End) { | |
| getContext().getCVContext().emitLineTableForFunction(*this, FunctionId, Begin, | |
| End); | |
| this->MCStreamer::emitCVLinetableDirective(FunctionId, Begin, End); | |
| } | |
| void MCObjectStreamer::emitCVInlineLinetableDirective( | |
| unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, | |
| const MCSymbol *FnStartSym, const MCSymbol *FnEndSym) { | |
| getContext().getCVContext().emitInlineLineTableForFunction( | |
| *this, PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, | |
| FnEndSym); | |
| this->MCStreamer::emitCVInlineLinetableDirective( | |
| PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, FnEndSym); | |
| } | |
| void MCObjectStreamer::emitCVDefRangeDirective( | |
| ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, | |
| StringRef FixedSizePortion) { | |
| MCFragment *Frag = | |
| getContext().getCVContext().emitDefRange(*this, Ranges, FixedSizePortion); | |
| // Attach labels that were pending before we created the defrange fragment to | |
| // the beginning of the new fragment. | |
| flushPendingLabels(Frag, 0); | |
| this->MCStreamer::emitCVDefRangeDirective(Ranges, FixedSizePortion); | |
| } | |
| void MCObjectStreamer::emitCVStringTableDirective() { | |
| getContext().getCVContext().emitStringTable(*this); | |
| } | |
| void MCObjectStreamer::emitCVFileChecksumsDirective() { | |
| getContext().getCVContext().emitFileChecksums(*this); | |
| } | |
| void MCObjectStreamer::emitCVFileChecksumOffsetDirective(unsigned FileNo) { | |
| getContext().getCVContext().emitFileChecksumOffset(*this, FileNo); | |
| } | |
| void MCObjectStreamer::emitBytes(StringRef Data) { | |
| MCDwarfLineEntry::make(this, getCurrentSectionOnly()); | |
| MCDataFragment *DF = getOrCreateDataFragment(); | |
| flushPendingLabels(DF, DF->getContents().size()); | |
| DF->getContents().append(Data.begin(), Data.end()); | |
| } | |
| void MCObjectStreamer::emitValueToAlignment(Align Alignment, int64_t Value, | |
| unsigned ValueSize, | |
| unsigned MaxBytesToEmit) { | |
| if (MaxBytesToEmit == 0) | |
| MaxBytesToEmit = Alignment.value(); | |
| insert(new MCAlignFragment(Alignment, Value, ValueSize, MaxBytesToEmit)); | |
| // Update the maximum alignment on the current section if necessary. | |
| MCSection *CurSec = getCurrentSectionOnly(); | |
| CurSec->ensureMinAlignment(Alignment); | |
| } | |
| void MCObjectStreamer::emitCodeAlignment(Align Alignment, | |
| const MCSubtargetInfo *STI, | |
| unsigned MaxBytesToEmit) { | |
| emitValueToAlignment(Alignment, 0, 1, MaxBytesToEmit); | |
| cast<MCAlignFragment>(getCurrentFragment())->setEmitNops(true, STI); | |
| } | |
| void MCObjectStreamer::emitValueToOffset(const MCExpr *Offset, | |
| unsigned char Value, | |
| SMLoc Loc) { | |
| insert(new MCOrgFragment(*Offset, Value, Loc)); | |
| } | |
| // Associate DTPRel32 fixup with data and resize data area | |
| void MCObjectStreamer::emitDTPRel32Value(const MCExpr *Value) { | |
| MCDataFragment *DF = getOrCreateDataFragment(); | |
| flushPendingLabels(DF, DF->getContents().size()); | |
| DF->getFixups().push_back(MCFixup::create(DF->getContents().size(), | |
| Value, FK_DTPRel_4)); | |
| DF->getContents().resize(DF->getContents().size() + 4, 0); | |
| } | |
| // Associate DTPRel64 fixup with data and resize data area | |
| void MCObjectStreamer::emitDTPRel64Value(const MCExpr *Value) { | |
| MCDataFragment *DF = getOrCreateDataFragment(); | |
| flushPendingLabels(DF, DF->getContents().size()); | |
| DF->getFixups().push_back(MCFixup::create(DF->getContents().size(), | |
| Value, FK_DTPRel_8)); | |
| DF->getContents().resize(DF->getContents().size() + 8, 0); | |
| } | |
| // Associate TPRel32 fixup with data and resize data area | |
| void MCObjectStreamer::emitTPRel32Value(const MCExpr *Value) { | |
| MCDataFragment *DF = getOrCreateDataFragment(); | |
| flushPendingLabels(DF, DF->getContents().size()); | |
| DF->getFixups().push_back(MCFixup::create(DF->getContents().size(), | |
| Value, FK_TPRel_4)); | |
| DF->getContents().resize(DF->getContents().size() + 4, 0); | |
| } | |
| // Associate TPRel64 fixup with data and resize data area | |
| void MCObjectStreamer::emitTPRel64Value(const MCExpr *Value) { | |
| MCDataFragment *DF = getOrCreateDataFragment(); | |
| flushPendingLabels(DF, DF->getContents().size()); | |
| DF->getFixups().push_back(MCFixup::create(DF->getContents().size(), | |
| Value, FK_TPRel_8)); | |
| DF->getContents().resize(DF->getContents().size() + 8, 0); | |
| } | |
| // Associate GPRel32 fixup with data and resize data area | |
| void MCObjectStreamer::emitGPRel32Value(const MCExpr *Value) { | |
| MCDataFragment *DF = getOrCreateDataFragment(); | |
| flushPendingLabels(DF, DF->getContents().size()); | |
| DF->getFixups().push_back( | |
| MCFixup::create(DF->getContents().size(), Value, FK_GPRel_4)); | |
| DF->getContents().resize(DF->getContents().size() + 4, 0); | |
| } | |
| // Associate GPRel64 fixup with data and resize data area | |
| void MCObjectStreamer::emitGPRel64Value(const MCExpr *Value) { | |
| MCDataFragment *DF = getOrCreateDataFragment(); | |
| flushPendingLabels(DF, DF->getContents().size()); | |
| DF->getFixups().push_back( | |
| MCFixup::create(DF->getContents().size(), Value, FK_GPRel_4)); | |
| DF->getContents().resize(DF->getContents().size() + 8, 0); | |
| } | |
| static std::optional<std::pair<bool, std::string>> | |
| getOffsetAndDataFragment(const MCSymbol &Symbol, uint32_t &RelocOffset, | |
| MCDataFragment *&DF) { | |
| if (Symbol.isVariable()) { | |
| const MCExpr *SymbolExpr = Symbol.getVariableValue(); | |
| MCValue OffsetVal; | |
| if(!SymbolExpr->evaluateAsRelocatable(OffsetVal, nullptr, nullptr)) | |
| return std::make_pair(false, | |
| std::string("symbol in .reloc offset is not " | |
| "relocatable")); | |
| if (OffsetVal.isAbsolute()) { | |
| RelocOffset = OffsetVal.getConstant(); | |
| MCFragment *Fragment = Symbol.getFragment(); | |
| // FIXME Support symbols with no DF. For example: | |
| // .reloc .data, ENUM_VALUE, <some expr> | |
| if (!Fragment || Fragment->getKind() != MCFragment::FT_Data) | |
| return std::make_pair(false, | |
| std::string("symbol in offset has no data " | |
| "fragment")); | |
| DF = cast<MCDataFragment>(Fragment); | |
| return std::nullopt; | |
| } | |
| if (OffsetVal.getSymB()) | |
| return std::make_pair(false, | |
| std::string(".reloc symbol offset is not " | |
| "representable")); | |
| const MCSymbolRefExpr &SRE = cast<MCSymbolRefExpr>(*OffsetVal.getSymA()); | |
| if (!SRE.getSymbol().isDefined()) | |
| return std::make_pair(false, | |
| std::string("symbol used in the .reloc offset is " | |
| "not defined")); | |
| if (SRE.getSymbol().isVariable()) | |
| return std::make_pair(false, | |
| std::string("symbol used in the .reloc offset is " | |
| "variable")); | |
| MCFragment *Fragment = SRE.getSymbol().getFragment(); | |
| // FIXME Support symbols with no DF. For example: | |
| // .reloc .data, ENUM_VALUE, <some expr> | |
| if (!Fragment || Fragment->getKind() != MCFragment::FT_Data) | |
| return std::make_pair(false, | |
| std::string("symbol in offset has no data " | |
| "fragment")); | |
| RelocOffset = SRE.getSymbol().getOffset() + OffsetVal.getConstant(); | |
| DF = cast<MCDataFragment>(Fragment); | |
| } else { | |
| RelocOffset = Symbol.getOffset(); | |
| MCFragment *Fragment = Symbol.getFragment(); | |
| // FIXME Support symbols with no DF. For example: | |
| // .reloc .data, ENUM_VALUE, <some expr> | |
| if (!Fragment || Fragment->getKind() != MCFragment::FT_Data) | |
| return std::make_pair(false, | |
| std::string("symbol in offset has no data " | |
| "fragment")); | |
| DF = cast<MCDataFragment>(Fragment); | |
| } | |
| return std::nullopt; | |
| } | |
| std::optional<std::pair<bool, std::string>> | |
| MCObjectStreamer::emitRelocDirective(const MCExpr &Offset, StringRef Name, | |
| const MCExpr *Expr, SMLoc Loc, | |
| const MCSubtargetInfo &STI) { | |
| std::optional<MCFixupKind> MaybeKind = | |
| Assembler->getBackend().getFixupKind(Name); | |
| if (!MaybeKind) | |
| return std::make_pair(true, std::string("unknown relocation name")); | |
| MCFixupKind Kind = *MaybeKind; | |
| if (Expr == nullptr) | |
| Expr = | |
| MCSymbolRefExpr::create(getContext().createTempSymbol(), getContext()); | |
| MCDataFragment *DF = getOrCreateDataFragment(&STI); | |
| flushPendingLabels(DF, DF->getContents().size()); | |
| MCValue OffsetVal; | |
| if (!Offset.evaluateAsRelocatable(OffsetVal, nullptr, nullptr)) | |
| return std::make_pair(false, | |
| std::string(".reloc offset is not relocatable")); | |
| if (OffsetVal.isAbsolute()) { | |
| if (OffsetVal.getConstant() < 0) | |
| return std::make_pair(false, std::string(".reloc offset is negative")); | |
| DF->getFixups().push_back( | |
| MCFixup::create(OffsetVal.getConstant(), Expr, Kind, Loc)); | |
| return std::nullopt; | |
| } | |
| if (OffsetVal.getSymB()) | |
| return std::make_pair(false, | |
| std::string(".reloc offset is not representable")); | |
| const MCSymbolRefExpr &SRE = cast<MCSymbolRefExpr>(*OffsetVal.getSymA()); | |
| const MCSymbol &Symbol = SRE.getSymbol(); | |
| if (Symbol.isDefined()) { | |
| uint32_t SymbolOffset = 0; | |
| std::optional<std::pair<bool, std::string>> Error = | |
| getOffsetAndDataFragment(Symbol, SymbolOffset, DF); | |
| if (Error != std::nullopt) | |
| return Error; | |
| DF->getFixups().push_back( | |
| MCFixup::create(SymbolOffset + OffsetVal.getConstant(), | |
| Expr, Kind, Loc)); | |
| return std::nullopt; | |
| } | |
| PendingFixups.emplace_back( | |
| &SRE.getSymbol(), DF, | |
| MCFixup::create(OffsetVal.getConstant(), Expr, Kind, Loc)); | |
| return std::nullopt; | |
| } | |
| void MCObjectStreamer::emitFill(const MCExpr &NumBytes, uint64_t FillValue, | |
| SMLoc Loc) { | |
| MCDataFragment *DF = getOrCreateDataFragment(); | |
| flushPendingLabels(DF, DF->getContents().size()); | |
| assert(getCurrentSectionOnly() && "need a section"); | |
| insert(new MCFillFragment(FillValue, 1, NumBytes, Loc)); | |
| } | |
| void MCObjectStreamer::emitFill(const MCExpr &NumValues, int64_t Size, | |
| int64_t Expr, SMLoc Loc) { | |
| int64_t IntNumValues; | |
| // Do additional checking now if we can resolve the value. | |
| if (NumValues.evaluateAsAbsolute(IntNumValues, getAssemblerPtr())) { | |
| if (IntNumValues < 0) { | |
| getContext().getSourceManager()->PrintMessage( | |
| Loc, SourceMgr::DK_Warning, | |
| "'.fill' directive with negative repeat count has no effect"); | |
| return; | |
| } | |
| // Emit now if we can for better errors. | |
| int64_t NonZeroSize = Size > 4 ? 4 : Size; | |
| Expr &= ~0ULL >> (64 - NonZeroSize * 8); | |
| for (uint64_t i = 0, e = IntNumValues; i != e; ++i) { | |
| emitIntValue(Expr, NonZeroSize); | |
| if (NonZeroSize < Size) | |
| emitIntValue(0, Size - NonZeroSize); | |
| } | |
| return; | |
| } | |
| // Otherwise emit as fragment. | |
| MCDataFragment *DF = getOrCreateDataFragment(); | |
| flushPendingLabels(DF, DF->getContents().size()); | |
| assert(getCurrentSectionOnly() && "need a section"); | |
| insert(new MCFillFragment(Expr, Size, NumValues, Loc)); | |
| } | |
| void MCObjectStreamer::emitNops(int64_t NumBytes, int64_t ControlledNopLength, | |
| SMLoc Loc, const MCSubtargetInfo &STI) { | |
| // Emit an NOP fragment. | |
| MCDataFragment *DF = getOrCreateDataFragment(); | |
| flushPendingLabels(DF, DF->getContents().size()); | |
| assert(getCurrentSectionOnly() && "need a section"); | |
| insert(new MCNopsFragment(NumBytes, ControlledNopLength, Loc, STI)); | |
| } | |
| void MCObjectStreamer::emitFileDirective(StringRef Filename) { | |
| getAssembler().addFileName(Filename); | |
| } | |
| void MCObjectStreamer::emitFileDirective(StringRef Filename, | |
| StringRef CompilerVerion, | |
| StringRef TimeStamp, | |
| StringRef Description) { | |
| getAssembler().addFileName(Filename); | |
| // TODO: add additional info to integrated assembler. | |
| } | |
| void MCObjectStreamer::emitAddrsig() { | |
| getAssembler().getWriter().emitAddrsigSection(); | |
| } | |
| void MCObjectStreamer::emitAddrsigSym(const MCSymbol *Sym) { | |
| getAssembler().getWriter().addAddrsigSymbol(Sym); | |
| } | |
| void MCObjectStreamer::finishImpl() { | |
| getContext().RemapDebugPaths(); | |
| // If we are generating dwarf for assembly source files dump out the sections. | |
| if (getContext().getGenDwarfForAssembly()) | |
| MCGenDwarfInfo::Emit(this); | |
| // Dump out the dwarf file & directory tables and line tables. | |
| MCDwarfLineTable::emit(this, getAssembler().getDWARFLinetableParams()); | |
| // Emit pseudo probes for the current module. | |
| MCPseudoProbeTable::emit(this); | |
| // Update any remaining pending labels with empty data fragments. | |
| flushPendingLabels(); | |
| resolvePendingFixups(); | |
| getAssembler().Finish(); | |
| } |