//===- llvm/MC/MCWinCOFFStreamer.cpp --------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file contains an implementation of a Windows COFF object file streamer. // //===----------------------------------------------------------------------===// #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCFixup.h" #include "llvm/MC/MCFragment.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectStreamer.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCSymbolCOFF.h" #include "llvm/MC/MCWinCOFFStreamer.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cassert> #include <cstdint> using namespace llvm; #define DEBUG_TYPE "WinCOFFStreamer" MCWinCOFFStreamer::MCWinCOFFStreamer(MCContext &Context, std::unique_ptr<MCAsmBackend> MAB, std::unique_ptr<MCCodeEmitter> CE, std::unique_ptr<MCObjectWriter> OW) : MCObjectStreamer(Context, std::move(MAB), std::move(OW), std::move(CE)), CurSymbol(nullptr) {} void MCWinCOFFStreamer::EmitInstToData(const MCInst &Inst, const MCSubtargetInfo &STI) { MCDataFragment *DF = getOrCreateDataFragment(); SmallVector<MCFixup, 4> Fixups; SmallString<256> Code; raw_svector_ostream VecOS(Code); getAssembler().getEmitter().encodeInstruction(Inst, VecOS, Fixups, STI); // Add the fixups and data. for (unsigned i = 0, e = Fixups.size(); i != e; ++i) { Fixups[i].setOffset(Fixups[i].getOffset() + DF->getContents().size()); DF->getFixups().push_back(Fixups[i]); } DF->setHasInstructions(STI); DF->getContents().append(Code.begin(), Code.end()); } void MCWinCOFFStreamer::InitSections(bool NoExecStack) { // FIXME: this is identical to the ELF one. // This emulates the same behavior of GNU as. This makes it easier // to compare the output as the major sections are in the same order. SwitchSection(getContext().getObjectFileInfo()->getTextSection()); EmitCodeAlignment(4); SwitchSection(getContext().getObjectFileInfo()->getDataSection()); EmitCodeAlignment(4); SwitchSection(getContext().getObjectFileInfo()->getBSSSection()); EmitCodeAlignment(4); SwitchSection(getContext().getObjectFileInfo()->getTextSection()); } void MCWinCOFFStreamer::EmitLabel(MCSymbol *S, SMLoc Loc) { auto *Symbol = cast<MCSymbolCOFF>(S); MCObjectStreamer::EmitLabel(Symbol, Loc); } void MCWinCOFFStreamer::EmitAssemblerFlag(MCAssemblerFlag Flag) { llvm_unreachable("not implemented"); } void MCWinCOFFStreamer::EmitThumbFunc(MCSymbol *Func) { llvm_unreachable("not implemented"); } bool MCWinCOFFStreamer::EmitSymbolAttribute(MCSymbol *S, MCSymbolAttr Attribute) { auto *Symbol = cast<MCSymbolCOFF>(S); getAssembler().registerSymbol(*Symbol); switch (Attribute) { default: return false; case MCSA_WeakReference: case MCSA_Weak: Symbol->setIsWeakExternal(); Symbol->setExternal(true); break; case MCSA_Global: Symbol->setExternal(true); break; case MCSA_AltEntry: llvm_unreachable("COFF doesn't support the .alt_entry attribute"); } return true; } void MCWinCOFFStreamer::EmitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) { llvm_unreachable("not implemented"); } void MCWinCOFFStreamer::BeginCOFFSymbolDef(MCSymbol const *S) { auto *Symbol = cast<MCSymbolCOFF>(S); if (CurSymbol) Error("starting a new symbol definition without completing the " "previous one"); CurSymbol = Symbol; } void MCWinCOFFStreamer::EmitCOFFSymbolStorageClass(int StorageClass) { if (!CurSymbol) { Error("storage class specified outside of symbol definition"); return; } if (StorageClass & ~COFF::SSC_Invalid) { Error("storage class value '" + Twine(StorageClass) + "' out of range"); return; } getAssembler().registerSymbol(*CurSymbol); cast<MCSymbolCOFF>(CurSymbol)->setClass((uint16_t)StorageClass); } void MCWinCOFFStreamer::EmitCOFFSymbolType(int Type) { if (!CurSymbol) { Error("symbol type specified outside of a symbol definition"); return; } if (Type & ~0xffff) { Error("type value '" + Twine(Type) + "' out of range"); return; } getAssembler().registerSymbol(*CurSymbol); cast<MCSymbolCOFF>(CurSymbol)->setType((uint16_t)Type); } void MCWinCOFFStreamer::EndCOFFSymbolDef() { if (!CurSymbol) Error("ending symbol definition without starting one"); CurSymbol = nullptr; } void MCWinCOFFStreamer::EmitCOFFSafeSEH(MCSymbol const *Symbol) { // SafeSEH is a feature specific to 32-bit x86. It does not exist (and is // unnecessary) on all platforms which use table-based exception dispatch. if (getContext().getObjectFileInfo()->getTargetTriple().getArch() != Triple::x86) return; const MCSymbolCOFF *CSymbol = cast<MCSymbolCOFF>(Symbol); if (CSymbol->isSafeSEH()) return; MCSection *SXData = getContext().getObjectFileInfo()->getSXDataSection(); getAssembler().registerSection(*SXData); if (SXData->getAlignment() < 4) SXData->setAlignment(4); new MCSymbolIdFragment(Symbol, SXData); getAssembler().registerSymbol(*Symbol); CSymbol->setIsSafeSEH(); // The Microsoft linker requires that the symbol type of a handler be // function. Go ahead and oblige it here. CSymbol->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT); } void MCWinCOFFStreamer::EmitCOFFSymbolIndex(MCSymbol const *Symbol) { MCSection *Sec = getCurrentSectionOnly(); getAssembler().registerSection(*Sec); if (Sec->getAlignment() < 4) Sec->setAlignment(4); new MCSymbolIdFragment(Symbol, getCurrentSectionOnly()); getAssembler().registerSymbol(*Symbol); } void MCWinCOFFStreamer::EmitCOFFSectionIndex(const MCSymbol *Symbol) { visitUsedSymbol(*Symbol); MCDataFragment *DF = getOrCreateDataFragment(); const MCSymbolRefExpr *SRE = MCSymbolRefExpr::create(Symbol, getContext()); MCFixup Fixup = MCFixup::create(DF->getContents().size(), SRE, FK_SecRel_2); DF->getFixups().push_back(Fixup); DF->getContents().resize(DF->getContents().size() + 2, 0); } void MCWinCOFFStreamer::EmitCOFFSecRel32(const MCSymbol *Symbol, uint64_t Offset) { visitUsedSymbol(*Symbol); MCDataFragment *DF = getOrCreateDataFragment(); // Create Symbol A for the relocation relative reference. const MCExpr *MCE = MCSymbolRefExpr::create(Symbol, getContext()); // Add the constant offset, if given. if (Offset) MCE = MCBinaryExpr::createAdd( MCE, MCConstantExpr::create(Offset, getContext()), getContext()); // Build the secrel32 relocation. MCFixup Fixup = MCFixup::create(DF->getContents().size(), MCE, FK_SecRel_4); // Record the relocation. DF->getFixups().push_back(Fixup); // Emit 4 bytes (zeros) to the object file. DF->getContents().resize(DF->getContents().size() + 4, 0); } void MCWinCOFFStreamer::EmitCOFFImgRel32(const MCSymbol *Symbol, int64_t Offset) { visitUsedSymbol(*Symbol); MCDataFragment *DF = getOrCreateDataFragment(); // Create Symbol A for the relocation relative reference. const MCExpr *MCE = MCSymbolRefExpr::create( Symbol, MCSymbolRefExpr::VK_COFF_IMGREL32, getContext()); // Add the constant offset, if given. if (Offset) MCE = MCBinaryExpr::createAdd( MCE, MCConstantExpr::create(Offset, getContext()), getContext()); // Build the imgrel relocation. MCFixup Fixup = MCFixup::create(DF->getContents().size(), MCE, FK_Data_4); // Record the relocation. DF->getFixups().push_back(Fixup); // Emit 4 bytes (zeros) to the object file. DF->getContents().resize(DF->getContents().size() + 4, 0); } void MCWinCOFFStreamer::EmitCommonSymbol(MCSymbol *S, uint64_t Size, unsigned ByteAlignment) { auto *Symbol = cast<MCSymbolCOFF>(S); const Triple &T = getContext().getObjectFileInfo()->getTargetTriple(); if (T.isKnownWindowsMSVCEnvironment()) { if (ByteAlignment > 32) report_fatal_error("alignment is limited to 32-bytes"); // Round size up to alignment so that we will honor the alignment request. Size = std::max(Size, static_cast<uint64_t>(ByteAlignment)); } getAssembler().registerSymbol(*Symbol); Symbol->setExternal(true); Symbol->setCommon(Size, ByteAlignment); if (!T.isKnownWindowsMSVCEnvironment() && ByteAlignment > 1) { SmallString<128> Directive; raw_svector_ostream OS(Directive); const MCObjectFileInfo *MFI = getContext().getObjectFileInfo(); OS << " -aligncomm:\"" << Symbol->getName() << "\"," << Log2_32_Ceil(ByteAlignment); PushSection(); SwitchSection(MFI->getDrectveSection()); EmitBytes(Directive); PopSection(); } } void MCWinCOFFStreamer::EmitLocalCommonSymbol(MCSymbol *S, uint64_t Size, unsigned ByteAlignment) { auto *Symbol = cast<MCSymbolCOFF>(S); MCSection *Section = getContext().getObjectFileInfo()->getBSSSection(); PushSection(); SwitchSection(Section); EmitValueToAlignment(ByteAlignment, 0, 1, 0); EmitLabel(Symbol); Symbol->setExternal(false); EmitZeros(Size); PopSection(); } void MCWinCOFFStreamer::EmitZerofill(MCSection *Section, MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment, SMLoc Loc) { llvm_unreachable("not implemented"); } void MCWinCOFFStreamer::EmitTBSSSymbol(MCSection *Section, MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment) { llvm_unreachable("not implemented"); } // TODO: Implement this if you want to emit .comment section in COFF obj files. void MCWinCOFFStreamer::EmitIdent(StringRef IdentString) { llvm_unreachable("not implemented"); } void MCWinCOFFStreamer::EmitWinEHHandlerData(SMLoc Loc) { llvm_unreachable("not implemented"); } void MCWinCOFFStreamer::FinishImpl() { MCObjectStreamer::FinishImpl(); } void MCWinCOFFStreamer::Error(const Twine &Msg) const { getContext().reportError(SMLoc(), Msg); }