Index: llvm/include/llvm/ExecutionEngine/JITLink/COFF.h =================================================================== --- /dev/null +++ llvm/include/llvm/ExecutionEngine/JITLink/COFF.h @@ -0,0 +1,39 @@ +//===------- COFF.h - Generic JIT link function for COFF ------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Generic jit-link functions for COFF. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_COFF_H +#define LLVM_EXECUTIONENGINE_JITLINK_COFF_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" + +namespace llvm { +namespace jitlink { + +/// Create a LinkGraph from an COFF relocatable object. +/// +/// Note: The graph does not take ownership of the underlying buffer, nor copy +/// its contents. The caller is responsible for ensuring that the object buffer +/// outlives the graph. +Expected> +createLinkGraphFromCOFFObject(MemoryBufferRef ObjectBuffer); + +/// Link the given graph. +/// +/// Uses conservative defaults for GOT and stub handling based on the target +/// platform. +void link_COFF(std::unique_ptr G, + std::unique_ptr Ctx); + +} // end namespace jitlink +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_JITLINK_COFF_H Index: llvm/include/llvm/ExecutionEngine/JITLink/COFF_x86_64.h =================================================================== --- /dev/null +++ llvm/include/llvm/ExecutionEngine/JITLink/COFF_x86_64.h @@ -0,0 +1,38 @@ +//===--- COFF_x86_64.h - JIT link functions for COFF/x86-64 ---*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// jit-link functions for COFF/x86-64. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_COFF_X86_64_H +#define LLVM_EXECUTIONENGINE_JITLINK_COFF_X86_64_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" + +namespace llvm { +namespace jitlink { + +/// Create a LinkGraph from an COFF/x86-64 relocatable object. +/// +/// Note: The graph does not take ownership of the underlying buffer, nor copy +/// its contents. The caller is responsible for ensuring that the object buffer +/// outlives the graph. +Expected> +createLinkGraphFromCOFFObject_x86_64(MemoryBufferRef ObjectBuffer); + +/// jit-link the given object buffer, which must be a COFF x86-64 object file. +void link_COFF_x86_64(std::unique_ptr G, + std::unique_ptr Ctx); + +/// Return the string name of the given COFF x86-64 edge kind. +const char *getCOFFX86RelocationKindName(Edge::Kind R); +} // end namespace jitlink +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_JITLINK_COFF_X86_64_H Index: llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt =================================================================== --- llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt +++ llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt @@ -21,6 +21,11 @@ ELF_riscv.cpp ELF_x86_64.cpp + # COFF + COFF.cpp + COFFLinkGraphBuilder.cpp + COFF_x86_64.cpp + # Architectures: aarch64.cpp riscv.cpp Index: llvm/lib/ExecutionEngine/JITLink/COFF.cpp =================================================================== --- /dev/null +++ llvm/lib/ExecutionEngine/JITLink/COFF.cpp @@ -0,0 +1,137 @@ +//===-------------- COFF.cpp - JIT linker function for COFF -------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// COFF jit-link function. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITLink/COFF.h" + +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/ExecutionEngine/JITLink/COFF_x86_64.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MemoryBuffer.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "jitlink" + +namespace llvm { +namespace jitlink { + +static StringRef getMachineName(uint16_t Machine) { + switch (Machine) { + case COFF::IMAGE_FILE_MACHINE_I386: + return "i386"; + case COFF::IMAGE_FILE_MACHINE_AMD64: + return "x86_64"; + case COFF::IMAGE_FILE_MACHINE_ARMNT: + return "ARM"; + case COFF::IMAGE_FILE_MACHINE_ARM64: + return "ARM64"; + default: + return "unknown"; + } +} + +Expected> +createLinkGraphFromCOFFObject(MemoryBufferRef ObjectBuffer) { + StringRef Data = ObjectBuffer.getBuffer(); + + // Check magic + auto Magic = identify_magic(ObjectBuffer.getBuffer()); + if (Magic != file_magic::coff_object) + return make_error("Invalid COFF buffer"); + + if (Data.size() < sizeof(object::coff_file_header)) + return make_error("Truncated COFF buffer"); + + uint64_t CurPtr = 0; + bool IsPE = false; + + // Check if this is a PE/COFF file. + if (Data.size() >= sizeof(object::dos_header) + sizeof(COFF::PEMagic)) { + const auto *DH = + reinterpret_cast(Data.data() + CurPtr); + if (DH->Magic[0] == 'M' && DH->Magic[1] == 'Z') { + // Check the PE magic bytes. ("PE\0\0") + CurPtr = DH->AddressOfNewExeHeader; + if (memcmp(Data.data() + CurPtr, COFF::PEMagic, sizeof(COFF::PEMagic)) != + 0) { + return make_error("Incorrect PE magic"); + } + CurPtr += sizeof(COFF::PEMagic); + IsPE = true; + } + } + if (Data.size() < CurPtr + sizeof(object::coff_file_header)) + return make_error("Truncated COFF buffer"); + + const object::coff_file_header *COFFHeader = + reinterpret_cast(Data.data() + CurPtr); + const object::coff_bigobj_file_header *COFFBigObjHeader = nullptr; + + // Deal with bigobj file + if (!IsPE && COFFHeader->Machine == COFF::IMAGE_FILE_MACHINE_UNKNOWN && + COFFHeader->NumberOfSections == uint16_t(0xffff) && + Data.size() >= sizeof(object::coff_bigobj_file_header)) { + if (Data.size() < sizeof(object::coff_file_header)) { + return make_error("Truncated COFF buffer"); + } + COFFBigObjHeader = + reinterpret_cast(Data.data() + + CurPtr); + + // Verify that we are dealing with bigobj. + if (COFFBigObjHeader->Version >= COFF::BigObjHeader::MinBigObjectVersion && + std::memcmp(COFFBigObjHeader->UUID, COFF::BigObjMagic, + sizeof(COFF::BigObjMagic)) == 0) { + COFFHeader = nullptr; + CurPtr += sizeof(object::coff_bigobj_file_header); + } else + COFFBigObjHeader = nullptr; + } + + uint16_t Machine = + COFFHeader ? COFFHeader->Machine : COFFBigObjHeader->Machine; + LLVM_DEBUG({ + dbgs() << "jitLink_COFF: PE = " << (IsPE ? "yes" : "no") + << ", bigobj = " << (COFFBigObjHeader ? "yes" : "no") + << ", identifier = \"" << ObjectBuffer.getBufferIdentifier() << "\" " + << "machine = " << getMachineName(Machine) << "\n"; + }); + + switch (Machine) { + case COFF::IMAGE_FILE_MACHINE_AMD64: + return createLinkGraphFromCOFFObject_x86_64(ObjectBuffer); + default: + return make_error( + "Unsupported target machine architecture in COFF object " + + ObjectBuffer.getBufferIdentifier() + ": " + getMachineName(Machine)); + } +} + +void link_COFF(std::unique_ptr G, + std::unique_ptr Ctx) { + switch (G->getTargetTriple().getArch()) { + case Triple::x86_64: + link_COFF_x86_64(std::move(G), std::move(Ctx)); + return; + default: + Ctx->notifyFailed(make_error( + "Unsupported target machine architecture in COFF link graph " + + G->getName())); + return; + } +} + +} // end namespace jitlink +} // end namespace llvm Index: llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.h =================================================================== --- /dev/null +++ llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.h @@ -0,0 +1,199 @@ +//===----- COFFLinkGraphBuilder.h - COFF LinkGraph builder ----*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Generic COFF LinkGraph building code. +// +//===----------------------------------------------------------------------===// + +#ifndef LIB_EXECUTIONENGINE_JITLINK_COFFLINKGRAPHBUILDER_H +#define LIB_EXECUTIONENGINE_JITLINK_COFFLINKGRAPHBUILDER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/Object/COFF.h" + +#include "EHFrameSupportImpl.h" +#include "JITLinkGeneric.h" + +#define DEBUG_TYPE "jitlink" + +#include + +namespace llvm { +namespace jitlink { + +class COFFLinkGraphBuilder { +public: + virtual ~COFFLinkGraphBuilder(); + Expected> buildGraph(); + +protected: + using COFFSectionIndex = int32_t; + using COFFSymbolIndex = int32_t; + + COFFLinkGraphBuilder(const object::COFFObjectFile &Obj, Triple TT, + LinkGraph::GetEdgeKindNameFunction GetEdgeKindName); + + LinkGraph &getGraph() const { return *G; } + + const object::COFFObjectFile &getObject() const { return Obj; } + + virtual Error addRelocations() = 0; + + Error graphifySections(); + Error graphifySymbols(); + + void setGraphSymbol(COFFSectionIndex SecIndex, COFFSymbolIndex SymIndex, + Symbol &Sym) { + assert(!GraphSymbols[SymIndex] && "Duplicate symbol at index"); + GraphSymbols[SymIndex] = &Sym; + if (!COFF::isReservedSectionNumber(SecIndex)) + SymbolSets[SecIndex].insert({Sym.getOffset(), &Sym}); + } + + Symbol *getGraphSymbol(COFFSymbolIndex SymIndex) const { + if (SymIndex < 0 || SymIndex >= GraphSymbols.size()) + return nullptr; + return GraphSymbols[SymIndex]; + } + + void setGraphBlock(COFFSectionIndex SecIndex, Block *B) { + assert(!GraphBlocks[SecIndex] && "Duplicate section at index"); + assert(!COFF::isReservedSectionNumber(SecIndex) && "Invalid section index"); + GraphBlocks[SecIndex] = B; + } + + Block *getGraphBlock(COFFSectionIndex SecIndex) const { + if (SecIndex <= 0 || SecIndex >= GraphSymbols.size()) + return nullptr; + return GraphBlocks[SecIndex]; + } + + object::COFFObjectFile::section_iterator_range sections() const { + return Obj.sections(); + } + + /// Traverse all matching relocation records in the given section. The handler + /// function Func should be callable with this signature: + /// Error(const object::RelocationRef&, + /// const object::SectionRef&, Section &) + /// + template + Error forEachRelocation(const object::SectionRef &RelSec, + RelocHandlerFunction &&Func, + bool ProcessDebugSections = false); + + /// Traverse all matching relocation records in the given section. Convenience + /// wrapper to allow passing a member function for the handler. + /// + template + Error forEachRelocation(const object::SectionRef &RelSec, ClassT *Instance, + RelocHandlerMethod &&Method, + bool ProcessDebugSections = false) { + return forEachRelocation( + RelSec, + [Instance, Method](const auto &Rel, const auto &Target, auto &GS) { + return (Instance->*Method)(Rel, Target, GS); + }, + ProcessDebugSections); + } + +private: + // The first symbol of COMDAT sequence. + // This symbol contains the actual definition of the symbol and proper linkage + // mode specified by the selection type. This symbol will be exported when + // second symbol of COMDAT sequence is processed which contains the external + // name of the symbol. + struct ComdatLeader { + COFFSymbolIndex Index; + Linkage Linkage; + }; + std::vector> ComdatLeaders; + + // This represents a pending request to create a weak external symbol with a + // name. + struct WeakAliasRequest { + COFFSymbolIndex Alias; + COFFSymbolIndex Target; + StringRef SymbolName; + }; + std::vector WeakAliasRequests; + + // Per COFF section jitlink symbol set sorted by offset. + // Used for calculating implicit size of defined symbols. + using SymbolSet = std::set>; + std::vector SymbolSets; + + Section &getCommonSection(); + + Expected createDefinedSymbol(COFFSymbolIndex SymIndex, + StringRef SymbolName, + object::COFFSymbolRef Symbol, + const object::coff_section *Section); + Expected createCOMDATLeaderSymbol( + COFFSymbolIndex SymIndex, object::COFFSymbolRef Symbol, + const object::coff_aux_section_definition *Definition); + Expected exportCOMDATSymbol(COFFSymbolIndex SymIndex, + StringRef SymbolName, + object::COFFSymbolRef Symbol); + Error flushWeakAliasRequests(); + Error calculateImplicitSizeOfSymbols(); + + static uint64_t getSectionAddress(const object::COFFObjectFile &Obj, + const object::coff_section *Section); + static uint64_t getSectionSize(const object::COFFObjectFile &Obj, + const object::coff_section *Section); + static bool isComdatSection(const object::coff_section *Section); + static unsigned getPointerSize(const object::COFFObjectFile &Obj); + static support::endianness getEndianness(const object::COFFObjectFile &Obj); + StringRef getCOFFSectionName(COFFSectionIndex SectionIndex, + const object::coff_section *Sec, + object::COFFSymbolRef Sym); + + const object::COFFObjectFile &Obj; + std::unique_ptr G; + + Section *CommonSection = nullptr; + std::vector GraphBlocks; + std::vector GraphSymbols; +}; + +template +Error COFFLinkGraphBuilder::forEachRelocation(const object::SectionRef &RelSec, + RelocHandlerFunction &&Func, + bool ProcessDebugSections) { + + auto COFFRelSect = Obj.getCOFFSection(RelSec); + + // Target sections have names in valid COFF object files. + Expected Name = Obj.getSectionName(COFFRelSect); + if (!Name) + return Name.takeError(); + LLVM_DEBUG(dbgs() << " " << *Name << ":\n"); + + // Lookup the link-graph node corresponding to the target section name. + auto *BlockToFix = getGraphBlock(RelSec.getIndex() + 1); + if (!BlockToFix) + return make_error( + "Referencing a section that wasn't added to the graph: " + *Name, + inconvertibleErrorCode()); + + // Let the callee process relocation entries one by one. + for (const auto &R : RelSec.relocations()) + if (Error Err = Func(R, RelSec, *BlockToFix)) + return Err; + + LLVM_DEBUG(dbgs() << "\n"); + return Error::success(); +} + +} // end namespace jitlink +} // end namespace llvm + +#endif // LIB_EXECUTIONENGINE_JITLINK_COFFLINKGRAPHBUILDER_H Index: llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp =================================================================== --- /dev/null +++ llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp @@ -0,0 +1,523 @@ +//=--------- COFFLinkGraphBuilder.cpp - COFF LinkGraph builder ----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Generic COFF LinkGraph buliding code. +// +//===----------------------------------------------------------------------===// +#include "COFFLinkGraphBuilder.h" + +#define DEBUG_TYPE "jitlink" + +static const char *CommonSectionName = "__common"; + +namespace llvm { +namespace jitlink { + +COFFLinkGraphBuilder::COFFLinkGraphBuilder( + const object::COFFObjectFile &Obj, Triple TT, + LinkGraph::GetEdgeKindNameFunction GetEdgeKindName) + : G(std::make_unique( + Obj.getFileName().str(), Triple(std::move(TT)), getPointerSize(Obj), + getEndianness(Obj), std::move(GetEdgeKindName))), + Obj(Obj) { + LLVM_DEBUG({ + dbgs() << "Created COFFLinkGraphBuilder for \"" << Obj.getFileName() + << "\"\n"; + }); +} + +COFFLinkGraphBuilder::~COFFLinkGraphBuilder() = default; + +unsigned +COFFLinkGraphBuilder::getPointerSize(const object::COFFObjectFile &Obj) { + return Obj.getBytesInAddress(); +} + +support::endianness +COFFLinkGraphBuilder::getEndianness(const object::COFFObjectFile &Obj) { + return Obj.isLittleEndian() ? support::little : support::big; +} + +uint64_t COFFLinkGraphBuilder::getSectionSize(const object::COFFObjectFile &Obj, + const object::coff_section *Sec) { + // Consider the difference between executable form and object form. + // More information is inside COFFObjectFile::getSectionSize + if (Obj.getDOSHeader()) + return std::min(Sec->VirtualSize, Sec->SizeOfRawData); + return Sec->SizeOfRawData; +} + +uint64_t +COFFLinkGraphBuilder::getSectionAddress(const object::COFFObjectFile &Obj, + const object::coff_section *Section) { + return Section->VirtualAddress + Obj.getImageBase(); +} + +bool COFFLinkGraphBuilder::isComdatSection( + const object::coff_section *Section) { + return Section->Characteristics & COFF::IMAGE_SCN_LNK_COMDAT; +} + +Section &COFFLinkGraphBuilder::getCommonSection() { + if (!CommonSection) + CommonSection = + &G->createSection(CommonSectionName, MemProt::Read | MemProt::Write); + return *CommonSection; +} + +Expected> COFFLinkGraphBuilder::buildGraph() { + if (!Obj.isRelocatableObject()) + return make_error("Object is not a relocatable COFF file"); + + if (auto Err = graphifySections()) + return std::move(Err); + + if (auto Err = graphifySymbols()) + return std::move(Err); + + if (auto Err = addRelocations()) + return std::move(Err); + + return std::move(G); +} + +StringRef +COFFLinkGraphBuilder::getCOFFSectionName(COFFSectionIndex SectionIndex, + const object::coff_section *Sec, + object::COFFSymbolRef Sym) { + switch (SectionIndex) { + case COFF::IMAGE_SYM_UNDEFINED: { + if (Sym.getValue()) + return "(common)"; + else + return "(external)"; + } + case COFF::IMAGE_SYM_ABSOLUTE: + return "(absolute)"; + case COFF::IMAGE_SYM_DEBUG: { + // Used with .file symbol + return "(debug)"; + } + default: { + // Non reserved regular section numbers + if (Expected SecNameOrErr = Obj.getSectionName(Sec)) + return *SecNameOrErr; + } + } + return ""; +} + +Error COFFLinkGraphBuilder::graphifySections() { + LLVM_DEBUG(dbgs() << " Creating graph sections...\n"); + + GraphBlocks.resize(Obj.getNumberOfSections() + 1); + // For each section... + for (COFFSectionIndex SecIndex = 1; SecIndex <= Obj.getNumberOfSections(); + SecIndex++) { + Expected Sec = Obj.getSection(SecIndex); + if (!Sec) + return Sec.takeError(); + + StringRef SectionName; + if (Expected SecNameOrErr = Obj.getSectionName(*Sec)) + SectionName = *SecNameOrErr; + + bool IsDiscardable = + (*Sec)->Characteristics & + (COFF::IMAGE_SCN_MEM_DISCARDABLE | COFF::IMAGE_SCN_LNK_INFO); + if (IsDiscardable) { + LLVM_DEBUG(dbgs() << " " << SecIndex << ": \"" << SectionName + << "\" is discardable: " + "No graph section will be created.\n"); + continue; + } + + // FIXME: Skip debug info sections + + LLVM_DEBUG({ + dbgs() << " " + << "Creating section for \"" << SectionName << "\"\n"; + }); + + // Get the section's memory protection flags. + MemProt Prot = MemProt::None; + if ((*Sec)->Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE) + Prot |= MemProt::Exec; + if ((*Sec)->Characteristics & COFF::IMAGE_SCN_MEM_READ) + Prot |= MemProt::Read; + if ((*Sec)->Characteristics & COFF::IMAGE_SCN_MEM_WRITE) + Prot |= MemProt::Write; + + // Look for existing sections first. + auto *GraphSec = G->findSectionByName(SectionName); + if (!GraphSec) + GraphSec = &G->createSection(SectionName, Prot); + if (GraphSec->getMemProt() != Prot) + return make_error("MemProt should match"); + + Block *B = nullptr; + if ((*Sec)->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) + B = &G->createZeroFillBlock( + *GraphSec, getSectionSize(Obj, *Sec), + orc::ExecutorAddr(getSectionAddress(Obj, *Sec)), + (*Sec)->getAlignment(), 0); + else { + ArrayRef Data; + if (auto Err = Obj.getSectionContents(*Sec, Data)) + return Err; + + B = &G->createContentBlock( + *GraphSec, + ArrayRef(reinterpret_cast(Data.data()), + Data.size()), + orc::ExecutorAddr(getSectionAddress(Obj, *Sec)), + (*Sec)->getAlignment(), 0); + } + + setGraphBlock(SecIndex, B); + } + + return Error::success(); +} + +Error COFFLinkGraphBuilder::graphifySymbols() { + LLVM_DEBUG(dbgs() << " Creating graph symbols...\n"); + + ComdatLeaders.resize(Obj.getNumberOfSections() + 1); + SymbolSets.resize(Obj.getNumberOfSections() + 1); + GraphSymbols.resize(Obj.getNumberOfSymbols()); + + for (COFFSymbolIndex SymIndex = 0; SymIndex < Obj.getNumberOfSymbols(); + SymIndex++) { + Expected Sym = Obj.getSymbol(SymIndex); + if (!Sym) + return Sym.takeError(); + + StringRef SymbolName; + if (Expected SymNameOrErr = Obj.getSymbolName(*Sym)) + SymbolName = *SymNameOrErr; + + COFFSectionIndex SectionIndex = Sym->getSectionNumber(); + const object::coff_section *Sec = nullptr; + + if (!COFF::isReservedSectionNumber(SectionIndex)) { + auto SecOrErr = Obj.getSection(SectionIndex); + if (!SecOrErr) + return make_error( + "Invalid COFF section number:" + formatv("{0:d}: ", SectionIndex) + + " (" + toString(SecOrErr.takeError()) + ")"); + Sec = *SecOrErr; + } + + // Create jitlink symbol + jitlink::Symbol *GSym = nullptr; + if (Sym->isFileRecord()) + LLVM_DEBUG({ + dbgs() << " " << SymIndex << ": Skipping FileRecord symbol \"" + << SymbolName << "\" in " + << getCOFFSectionName(SectionIndex, Sec, *Sym) + << " (index: " << SectionIndex << ") \n"; + }); + else if (Sym->isUndefined()) { + LLVM_DEBUG({ + dbgs() << " " << SymIndex + << ": Creating external graph symbol for COFF symbol \"" + << SymbolName << "\" in " + << getCOFFSectionName(SectionIndex, Sec, *Sym) + << " (index: " << SectionIndex << ") \n"; + }); + GSym = + &G->addExternalSymbol(SymbolName, Sym->getValue(), Linkage::Strong); + } else if (Sym->isWeakExternal()) { + COFFSymbolIndex TagIndex = + Sym->getAux()->TagIndex; + assert(Sym->getAux()->Characteristics != + COFF::IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY && + "IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY is not supported."); + assert(Sym->getAux()->Characteristics != + COFF::IMAGE_WEAK_EXTERN_SEARCH_LIBRARY && + "IMAGE_WEAK_EXTERN_SEARCH_LIBRARY is not supported."); + WeakAliasRequests.push_back({SymIndex, TagIndex, SymbolName}); + } else { + Expected NewGSym = + createDefinedSymbol(SymIndex, SymbolName, *Sym, Sec); + if (!NewGSym) { + LLVM_DEBUG({ + dbgs() << " " << SymIndex + << ": Not creating graph symbol for COFF symbol \"" + << SymbolName << "\" in " + << getCOFFSectionName(SectionIndex, Sec, *Sym) + << " with an error : " << NewGSym.takeError() << "\n"; + }); + SymIndex += Sym->getNumberOfAuxSymbols(); + continue; + } + GSym = *NewGSym; + if (GSym) { + LLVM_DEBUG({ + dbgs() << " " << SymIndex + << ": Creating defined graph symbol for COFF symbol \"" + << SymbolName << "\" in " + << getCOFFSectionName(SectionIndex, Sec, *Sym) + << " (index: " << SectionIndex << ") \n"; + dbgs() << " " << *GSym << "\n"; + }); + } + } + + // Register the symbol + if (GSym) + setGraphSymbol(SectionIndex, SymIndex, *GSym); + SymIndex += Sym->getNumberOfAuxSymbols(); + } + + if (auto Err = flushWeakAliasRequests()) + return Err; + + if (auto Err = calculateImplicitSizeOfSymbols()) + return Err; + + return Error::success(); +} + +Error COFFLinkGraphBuilder::flushWeakAliasRequests() { + // Export the weak external symbols and alias it + for (auto &WeakAlias : WeakAliasRequests) { + if (auto *Target = getGraphSymbol(WeakAlias.Target)) { + Expected AliasSymbol = + Obj.getSymbol(WeakAlias.Alias); + if (!AliasSymbol) + return AliasSymbol.takeError(); + + // FIXME: Support this when there's a way to handle this. + if (!Target->isDefined()) + return make_error("Weak external symbol with external " + "symbol as alternative not supported."); + + jitlink::Symbol *NewSymbol = &G->addDefinedSymbol( + Target->getBlock(), Target->getOffset(), WeakAlias.SymbolName, + Target->getSize(), Linkage::Weak, Scope::Default, + Target->isCallable(), false); + setGraphSymbol(AliasSymbol->getSectionNumber(), WeakAlias.Alias, + *NewSymbol); + LLVM_DEBUG({ + dbgs() << " " << WeakAlias.Alias + << ": Creating weak external symbol for COFF symbol \"" + << WeakAlias.SymbolName << "\" in section " + << AliasSymbol->getSectionNumber() << "\n"; + dbgs() << " " << *NewSymbol << "\n"; + }); + } else + return make_error("Weak symbol alias requested but actual " + "symbol not found for symbol " + + formatv("{0:d}", WeakAlias.Alias)); + } + return Error::success(); +} + +// In COFF, most of the defined symbols don't contain the size information. +// Hence, we calculate the "implicit" size of symbol by taking the delta of +// offsets of consecutive symbols within a block. We maintain a balanced tree +// set of symbols sorted by offset per each block in order to achieve +// logarithmic time complexity of sorted symbol insertion. Symbol is inserted to +// the set once it's processed in graphifySymbols. In this function, we iterate +// each collected symbol in sorted order and calculate the implicit size. +Error COFFLinkGraphBuilder::calculateImplicitSizeOfSymbols() { + for (COFFSectionIndex SecIndex = 1; SecIndex <= Obj.getNumberOfSections(); + SecIndex++) { + auto &SymbolSet = SymbolSets[SecIndex]; + jitlink::Block *B = getGraphBlock(SecIndex); + orc::ExecutorAddrDiff LastOffset = B->getSize(); + orc::ExecutorAddrDiff LastDifferentOffset = B->getSize(); + orc::ExecutorAddrDiff LastSize = 0; + for (auto It = SymbolSet.rbegin(); It != SymbolSet.rend(); It++) { + orc::ExecutorAddrDiff Offset = It->first; + jitlink::Symbol *Symbol = It->second; + orc::ExecutorAddrDiff CandSize; + // Last offset can be same when aliasing happened + if (Symbol->getOffset() == LastOffset) + CandSize = LastSize; + else + CandSize = LastOffset - Offset; + + LLVM_DEBUG({ + if (Offset + Symbol->getSize() > LastDifferentOffset) + dbgs() << " Overlapping symbol range generated for the following " + "symbol:" + << "\n" + << " " << *Symbol << "\n"; + }); + if (LastOffset != Offset) + LastDifferentOffset = Offset; + LastSize = CandSize; + LastOffset = Offset; + if (Symbol->getSize()) { + // Non empty symbol can happen in COMDAT symbol. + // We don't consider the possibility of overlapping symbol range that + // could be introduced by disparity between inferred symbol size and + // defined symbol size because symbol size information is currently only + // used by jitlink-check where we have control to not make overlapping + // ranges. + continue; + } + + LLVM_DEBUG({ + if (!CandSize) + dbgs() << " Empty implicit symbol size generated for the following " + "symbol:" + << "\n" + << " " << *Symbol << "\n"; + }); + + Symbol->setSize(CandSize); + } + } + return Error::success(); +} + +Expected COFFLinkGraphBuilder::createDefinedSymbol( + COFFSymbolIndex SymIndex, StringRef SymbolName, + object::COFFSymbolRef Symbol, const object::coff_section *Section) { + if (Symbol.isCommon()) { + // FIXME: correct alignment + return &G->addCommonSymbol(SymbolName, Scope::Default, getCommonSection(), + orc::ExecutorAddr(), Symbol.getValue(), + Symbol.getValue(), false); + } + if (Symbol.isAbsolute()) + return &G->addAbsoluteSymbol(SymbolName, + orc::ExecutorAddr(Symbol.getValue()), 0, + Linkage::Strong, Scope::Local, false); + + if (llvm::COFF::isReservedSectionNumber(Symbol.getSectionNumber())) + return make_error( + "Reserved section number used in regular symbol " + + formatv("{0:d}", SymIndex)); + + Block *B = getGraphBlock(Symbol.getSectionNumber()); + if (Symbol.isExternal()) { + // This is not a comdat sequence, export the symbol as it is + if (!ComdatLeaders[Symbol.getSectionNumber()]) + return &G->addDefinedSymbol( + *B, Symbol.getValue(), SymbolName, 0, Linkage::Strong, Scope::Default, + Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, false); + else + return exportCOMDATSymbol(SymIndex, SymbolName, Symbol); + } + + if (Symbol.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC) { + const object::coff_aux_section_definition *Definition = + Symbol.getSectionDefinition(); + if (!Definition || !isComdatSection(Section) || + ComdatLeaders[Symbol.getSectionNumber()]) { + // Handle typical static symbol + return &G->addDefinedSymbol( + *B, Symbol.getValue(), SymbolName, 0, Linkage::Strong, Scope::Local, + Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, false); + } + if (Definition->Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { + // FIXME: don't dead strip this when parent section is alive + return &G->addDefinedSymbol( + *B, Symbol.getValue(), SymbolName, 0, Linkage::Strong, Scope::Local, + Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, false); + } + + return createCOMDATLeaderSymbol(SymIndex, Symbol, Definition); + } + return make_error("Unsupported storage class " + + formatv("{0:d}", Symbol.getStorageClass()) + + " in symbol " + formatv("{0:d}", SymIndex)); +} + +// COMDAT handling: +// When IMAGE_SCN_LNK_COMDAT flag is set in the flags of a section, +// the section is called a COMDAT section. It contains two symbols +// in a sequence that specifes the behavior. First symbol is the section +// symbol which contains the size and name of the section. It also contains +// selection type that specifies how duplicate of the symbol is handled. +// Second symbol is COMDAT symbol which usually defines the external name and +// data type. +// +// Since two symbols always come in a specific order, we store the first +// symbol to ComdatLeaders table and emits it as a external symbol when +// we encounter the COMDAT symbol. + +// Process the first symbol of COMDAT sequence. +Expected COFFLinkGraphBuilder::createCOMDATLeaderSymbol( + COFFSymbolIndex SymIndex, object::COFFSymbolRef Symbol, + const object::coff_aux_section_definition *Definition) { + Block *B = getGraphBlock(Symbol.getSectionNumber()); + Linkage L = Linkage::Strong; + switch (Definition->Selection) { + case COFF::IMAGE_COMDAT_SELECT_NODUPLICATES: { + L = Linkage::Strong; + break; + } + case COFF::IMAGE_COMDAT_SELECT_ANY: { + L = Linkage::Weak; + break; + } + case COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH: + case COFF::IMAGE_COMDAT_SELECT_SAME_SIZE: { + // FIXME: Implement size/content validation when LinkGraph is able to + // handle this. + L = Linkage::Weak; + break; + } + case COFF::IMAGE_COMDAT_SELECT_LARGEST: { + // FIXME: Support IMAGE_COMDAT_SELECT_LARGEST when LinkGraph is able to + // handle this. + return make_error( + "IMAGE_COMDAT_SELECT_LARGEST is not supported."); + } + case COFF::IMAGE_COMDAT_SELECT_NEWEST: { + // Even link.exe doesn't support this selection properly. + return make_error( + "IMAGE_COMDAT_SELECT_NEWEST is not supported."); + } + default: { + return make_error("Invalid comdat selection type: " + + formatv("{0:d}", Definition->Selection)); + } + } + ComdatLeaders[Symbol.getSectionNumber()] = {SymIndex, L}; + return &G->addAnonymousSymbol(*B, Symbol.getValue(), Definition->Length, + false, false); +} + +// Process the second symbol of COMDAT sequence. +Expected +COFFLinkGraphBuilder::exportCOMDATSymbol(COFFSymbolIndex SymIndex, + StringRef SymbolName, + object::COFFSymbolRef Symbol) { + auto Leader = *ComdatLeaders[Symbol.getSectionNumber()]; + COFFSymbolIndex TargetIndex = Leader.Index; + Linkage L = Leader.Linkage; + jitlink::Symbol *Target = getGraphSymbol(TargetIndex); + assert(Target && "COMDAT leaader is invalid."); + assert((llvm::count_if(G->defined_symbols(), + [&](const jitlink::Symbol *Sym) { + return Sym->getName() == SymbolName; + }) == 0) && + "Duplicate defined symbol"); + Target->setName(SymbolName); + Target->setLinkage(L); + Target->setCallable(Symbol.getComplexType() == + COFF::IMAGE_SYM_DTYPE_FUNCTION); + Target->setScope(Scope::Default); + LLVM_DEBUG({ + dbgs() << " " << SymIndex + << ": Exporting COMDAT graph symbol for COFF symbol \"" << SymbolName + << "\" in section " << Symbol.getSectionNumber() << "\n"; + dbgs() << " " << *Target << "\n"; + }); + return Target; +} + +} // namespace jitlink +} // namespace llvm \ No newline at end of file Index: llvm/lib/ExecutionEngine/JITLink/COFF_x86_64.cpp =================================================================== --- /dev/null +++ llvm/lib/ExecutionEngine/JITLink/COFF_x86_64.cpp @@ -0,0 +1,220 @@ +//===----- COFF_x86_64.cpp - JIT linker implementation for COFF/x86_64 ----===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// COFF/x86_64 jit-link implementation. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITLink/COFF_x86_64.h" +#include "COFFLinkGraphBuilder.h" +#include "EHFrameSupportImpl.h" +#include "JITLinkGeneric.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/ExecutionEngine/JITLink/x86_64.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Endian.h" + +#define DEBUG_TYPE "jitlink" + +using namespace llvm; +using namespace llvm::jitlink; + +namespace { + +class COFFJITLinker_x86_64 : public JITLinker { + friend class JITLinker; + +public: + COFFJITLinker_x86_64(std::unique_ptr Ctx, + std::unique_ptr G, + PassConfiguration PassConfig) + : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {} + +private: + Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const { + return x86_64::applyFixup(G, B, E, nullptr); + } +}; + +class COFFLinkGraphBuilder_x86_64 : public COFFLinkGraphBuilder { +private: + uint64_t ImageBase = 0; + enum COFFX86RelocationKind { + COFFAddr32NB, + COFFRel32, + }; + + static Expected + getRelocationKind(const uint32_t Type) { + switch (Type) { + case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_ADDR32NB: + return COFFAddr32NB; + case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32: + return COFFRel32; + } + + return make_error("Unsupported x86_64 relocation:" + + formatv("{0:d}", Type)); + } + + Error addRelocations() override { + + LLVM_DEBUG(dbgs() << "Processing relocations:\n"); + + for (const auto &RelSect : sections()) + if (Error Err = COFFLinkGraphBuilder::forEachRelocation( + RelSect, this, &COFFLinkGraphBuilder_x86_64::addSingleRelocation)) + return Err; + + return Error::success(); + } + + uint64_t getImageBase() { + if (!ImageBase) { + ImageBase = std::numeric_limits::max(); + for (const auto &Block : getGraph().blocks()) { + if (Block->getAddress().getValue()) + ImageBase = std::min(ImageBase, Block->getAddress().getValue()); + } + } + return ImageBase; + } + + Error addSingleRelocation(const object::RelocationRef &Rel, + const object::SectionRef &FixupSect, + Block &BlockToFix) { + + const object::coff_relocation *COFFRel = getObject().getCOFFRelocation(Rel); + auto SymbolIt = Rel.getSymbol(); + if (SymbolIt == getObject().symbol_end()) { + return make_error( + formatv("Invalid symbol index in relocation entry. " + "index: {0}, section: {1}", + COFFRel->SymbolTableIndex, FixupSect.getIndex()), + inconvertibleErrorCode()); + } + + object::COFFSymbolRef COFFSymbol = getObject().getCOFFSymbol(*SymbolIt); + COFFSymbolIndex SymIndex = getObject().getSymbolIndex(COFFSymbol); + + Symbol *GraphSymbol = getGraphSymbol(SymIndex); + if (!GraphSymbol) + return make_error( + formatv("Could not find symbol at given index, did you add it to " + "JITSymbolTable? index: {0}, section: {1}", + SymIndex, FixupSect.getIndex()), + inconvertibleErrorCode()); + + Expected RelocKind = + getRelocationKind(Rel.getType()); + if (!RelocKind) + return RelocKind.takeError(); + + int64_t Addend = 0; + orc::ExecutorAddr FixupAddress = + orc::ExecutorAddr(FixupSect.getAddress()) + Rel.getOffset(); + Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress(); + + // Get a pointer to the fixup content. + const void *FixupContent = BlockToFix.getContent().data() + + (FixupAddress - BlockToFix.getAddress()); + + Edge::Kind Kind = Edge::Invalid; + + switch (*RelocKind) { + case COFFAddr32NB: { + Kind = x86_64::Pointer32; + Offset -= getImageBase(); + break; + } + case COFFRel32: { + Kind = x86_64::BranchPCRel32; + break; + } + }; + + Edge GE(Kind, Offset, *GraphSymbol, Addend); + LLVM_DEBUG({ + dbgs() << " "; + printEdge(dbgs(), BlockToFix, GE, x86_64::getEdgeKindName(Kind)); + dbgs() << "\n"; + }); + + BlockToFix.addEdge(std::move(GE)); + return Error::success(); + } + + /// Return the string name of the given COFF x86_64 edge kind. + const char *getCOFFX86RelocationKindName(COFFX86RelocationKind R) { + switch (R) { + case COFFAddr32NB: + return "COFFAddr32NB"; + case COFFRel32: + return "COFFRel32"; + } + } + +public: + COFFLinkGraphBuilder_x86_64(const object::COFFObjectFile &Obj, const Triple T) + : COFFLinkGraphBuilder(Obj, std::move(T), x86_64::getEdgeKindName) {} +}; + +Error buildTables_COFF_x86_64(LinkGraph &G) { + LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n"); + + x86_64::GOTTableManager GOT; + x86_64::PLTTableManager PLT(GOT); + visitExistingEdges(G, GOT, PLT); + return Error::success(); +} +} // namespace + +namespace llvm { +namespace jitlink { + +Expected> +createLinkGraphFromCOFFObject_x86_64(MemoryBufferRef ObjectBuffer) { + LLVM_DEBUG({ + dbgs() << "Building jitlink graph for new input " + << ObjectBuffer.getBufferIdentifier() << "...\n"; + }); + + auto COFFObj = object::ObjectFile::createCOFFObjectFile(ObjectBuffer); + if (!COFFObj) + return COFFObj.takeError(); + + return COFFLinkGraphBuilder_x86_64(**COFFObj, (*COFFObj)->makeTriple()) + .buildGraph(); +} + +void link_COFF_x86_64(std::unique_ptr G, + std::unique_ptr Ctx) { + PassConfiguration Config; + const Triple &TT = G->getTargetTriple(); + if (Ctx->shouldAddDefaultTargetPasses(TT)) { + // Add a mark-live pass. + if (auto MarkLive = Ctx->getMarkLivePass(TT)) + Config.PrePrunePasses.push_back(std::move(MarkLive)); + else + Config.PrePrunePasses.push_back(markAllSymbolsLive); + + // Add an in-place GOT/Stubs/TLSInfoEntry build pass. + Config.PostPrunePasses.push_back(buildTables_COFF_x86_64); + + // Add GOT/Stubs optimizer pass. + Config.PreFixupPasses.push_back(x86_64::optimizeGOTAndStubAccesses); + } + + if (auto Err = Ctx->modifyPassConfig(*G, Config)) + return Ctx->notifyFailed(std::move(Err)); + + COFFJITLinker_x86_64::link(std::move(Ctx), std::move(G), std::move(Config)); +} + +} // namespace jitlink +} // namespace llvm Index: llvm/lib/ExecutionEngine/JITLink/JITLink.cpp =================================================================== --- llvm/lib/ExecutionEngine/JITLink/JITLink.cpp +++ llvm/lib/ExecutionEngine/JITLink/JITLink.cpp @@ -9,6 +9,7 @@ #include "llvm/ExecutionEngine/JITLink/JITLink.h" #include "llvm/BinaryFormat/Magic.h" +#include "llvm/ExecutionEngine/JITLink/COFF.h" #include "llvm/ExecutionEngine/JITLink/ELF.h" #include "llvm/ExecutionEngine/JITLink/MachO.h" #include "llvm/Support/Format.h" @@ -410,6 +411,8 @@ return createLinkGraphFromMachOObject(ObjectBuffer); case file_magic::elf_relocatable: return createLinkGraphFromELFObject(ObjectBuffer); + case file_magic::coff_object: + return createLinkGraphFromCOFFObject(ObjectBuffer); default: return make_error("Unsupported file format"); }; @@ -421,6 +424,8 @@ return link_MachO(std::move(G), std::move(Ctx)); case Triple::ELF: return link_ELF(std::move(G), std::move(Ctx)); + case Triple::COFF: + return link_COFF(std::move(G), std::move(Ctx)); default: Ctx->notifyFailed(make_error("Unsupported object format")); }; Index: llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp =================================================================== --- llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp +++ llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp @@ -9,6 +9,7 @@ #include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h" #include "llvm/ExecutionEngine/Orc/ELFNixPlatform.h" #include "llvm/ExecutionEngine/Orc/MachOPlatform.h" +#include "llvm/Object/COFF.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" @@ -145,6 +146,55 @@ return I; } +static Expected +getCOFFObjectFileSymbolInfo(ExecutionSession &ES, + const object::COFFObjectFile &Obj) { + MaterializationUnit::Interface I; + + for (auto &Sym : Obj.symbols()) { + Expected SymFlagsOrErr = Sym.getFlags(); + if (!SymFlagsOrErr) + // TODO: Test this error. + return SymFlagsOrErr.takeError(); + + // Skip symbols not defined in this object file. + if (*SymFlagsOrErr & object::BasicSymbolRef::SF_Undefined) + continue; + + // Skip symbols that are not global. + if (!(*SymFlagsOrErr & object::BasicSymbolRef::SF_Global)) + continue; + + // Skip symbols that have type SF_File. + if (auto SymType = Sym.getType()) { + if (*SymType == object::SymbolRef::ST_File) + continue; + } else + return SymType.takeError(); + + auto Name = Sym.getName(); + if (!Name) + return Name.takeError(); + + auto SymFlags = JITSymbolFlags::fromObjectSymbol(Sym); + if (!SymFlags) + return SymFlags.takeError(); + *SymFlags |= JITSymbolFlags::Exported; + auto COFFSym = Obj.getCOFFSymbol(Sym); + + // Weak external is always a function + if (COFFSym.isWeakExternal()) { + *SymFlags |= JITSymbolFlags::Callable; + } + + I.SymbolFlags[ES.intern(*Name)] = std::move(*SymFlags); + } + + // FIXME: handle init symbols + + return I; +} + Expected getGenericObjectFileSymbolInfo(ExecutionSession &ES, const object::ObjectFile &Obj) { @@ -196,6 +246,8 @@ return getMachOObjectFileSymbolInfo(ES, *MachOObj); else if (auto *ELFObj = dyn_cast(Obj->get())) return getELFObjectFileSymbolInfo(ES, *ELFObj); + else if (auto *COFFObj = dyn_cast(Obj->get())) + return getCOFFObjectFileSymbolInfo(ES, *COFFObj); return getGenericObjectFileSymbolInfo(ES, **Obj); } Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_abs.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_abs.s @@ -0,0 +1,25 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check absolute symbol is created with a correct value. +# +# CHECK: Creating graph symbols... +# CHECK: 6: Creating defined graph symbol for COFF symbol "abs" in (absolute) (index: -1) +# CHECK-NEXT: 0x53 (addressable + 0x00000000): size: 0x00000000, linkage: strong, scope: local, dead - abs + + .text + .def abs; + .scl 3; + .type 0; + .endef + .globl abs +.set abs, 0x53 + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + retq Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_basic.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_basic.s @@ -0,0 +1,29 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink -noexec %t +# +# Check a basic COFF object file loads successfully. + + .text + .def @feat.00; + .scl 3; + .type 0; + .endef + .globl @feat.00 +.set @feat.00, 0 + .file "main.c" + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + .seh_proc main + pushq %rax + .seh_stackalloc 8 + .seh_endprologue + movl $0, 4(%rsp) + xorl %eax, %eax + popq %rcx + retq + .seh_endproc Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_any.test =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_any.test @@ -0,0 +1,63 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-jitlink -noexec --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a weak symbol is created for a COMDAT symbol with IMAGE_COMDAT_SELECT_ANY selection type. +# +# CHECK: Creating graph symbols... +# CHECK: 2: Creating defined graph symbol for COFF symbol ".text" in .text (index: 2) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: strong, scope: local, dead - +# CHECK-NEXT: 4: Exporting COMDAT graph symbol for COFF symbol "func" in section 2 +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: weak, scope: default, dead - func + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 1 + - Name: .text + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_ANY + - Name: func + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_associative.test =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_associative.test @@ -0,0 +1,82 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-jitlink -noexec --debug-only=jitlink -noexec %t 2>&1 +# +# Check COMDAT associative symbol is emitted as local symbol. +# +# CHECK: Creating graph symbols... +# CHECK: 2: Creating defined graph symbol for COFF symbol ".text" in .text (index: 2) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: strong, scope: local, dead - +# CHECK-NEXT: 4: Exporting COMDAT graph symbol for COFF symbol "func" in section 2 +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: weak, scope: default, dead - func +# CHECK-NEXT: 5: Creating defined graph symbol for COFF symbol ".xdata" in .xdata (index: 3) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000000, linkage: strong, scope: local, dead - .xdata + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .xdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '0100000000000000' +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 1 + - Name: .text + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_ANY + - Name: func + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: .xdata + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 8 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 3433693342 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_exact_match.test =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_exact_match.test @@ -0,0 +1,64 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-jitlink -noexec --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a weak symbol is created for a COMDAT symbol with IMAGE_COMDAT_SELECT_EXACT_MATCH selection type. +# Doesn't check the content validation. +# +# CHECK: Creating graph symbols... +# CHECK: 2: Creating defined graph symbol for COFF symbol ".text" in .text (index: 2) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: strong, scope: local, dead - +# CHECK-NEXT: 4: Exporting COMDAT graph symbol for COFF symbol "func" in section 2 +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: weak, scope: default, dead - func + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 1 + - Name: .text + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_EXACT_MATCH + - Name: func + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_largest.test =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_largest.test @@ -0,0 +1,59 @@ +# XFAIL: * +# RUN: yaml2obj %s -o %t +# RUN: llvm-jitlink -noexec --debug-only=jitlink -noexec %t 2>&1 +# +# Check jitlink return an error when IMAGE_COMDAT_SELECT_LARGEST selection type is encountered. +# + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 1 + - Name: .text + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_LARGEST + - Name: func + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_noduplicate.test =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_noduplicate.test @@ -0,0 +1,63 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-jitlink -noexec --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a strong symbol is created for a COMDAT symbol with IMAGE_COMDAT_SELECT_NODUPLICATES selection type. +# +# CHECK: Creating graph symbols... +# CHECK: 2: Creating defined graph symbol for COFF symbol ".text" in .text (index: 2) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: strong, scope: local, dead - +# CHECK-NEXT: 4: Exporting COMDAT graph symbol for COFF symbol "func" in section 2 +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: strong, scope: default, dead - func + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 1 + - Name: .text + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_NODUPLICATES + - Name: func + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_same_size.test =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_same_size.test @@ -0,0 +1,64 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-jitlink -noexec --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a weak symbol is created for a COMDAT symbol with IMAGE_COMDAT_SELECT_SAME_SIZE selection type. +# Doesn't check the size validation. +# +# CHECK: Creating graph symbols... +# CHECK: 2: Creating defined graph symbol for COFF symbol ".text" in .text (index: 2) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: strong, scope: local, dead - +# CHECK-NEXT: 4: Exporting COMDAT graph symbol for COFF symbol "func" in section 2 +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: weak, scope: default, dead - func + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 1 + - Name: .text + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 40735498 + Number: 2 + Selection: IMAGE_COMDAT_SELECT_SAME_SIZE + - Name: func + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_weak.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_weak.s @@ -0,0 +1,32 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a COMDAT any symbol is exported as a weak symbol. +# +# CHECK: Creating graph symbols... +# CHECK: 6: Creating defined graph symbol for COFF symbol ".text" in .text (index: 4) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: strong, scope: local, dead - +# CHECK-NEXT: 8: Exporting COMDAT graph symbol for COFF symbol "func" in section 4 +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000001, linkage: weak, scope: default, dead - func + + .text + + .def func; + .scl 2; + .type 32; + .endef + .section .text,"xr",discard,func + .globl func + .p2align 4, 0x90 +func: + retq + + .def main; + .scl 2; + .type 32; + .endef + .text + .globl main + .p2align 4, 0x90 +main: + retq Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_common_symbol.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_common_symbol.s @@ -0,0 +1,22 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a common symbol is created. +# +# CHECK: Creating graph symbols... +# CHECK: 7: Creating defined graph symbol for COFF symbol "var" in (common) (index: 0) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000004, linkage: weak, scope: default, dead - var + + .text + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4 +main: + movl var(%rip), %eax + retq + + .comm var,4,2 Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_external_func.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_external_func.s @@ -0,0 +1,19 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink -abs func=0xcafef00d --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check an external symbol to a functon is created. +# +# CHECK: Creating graph symbols... +# CHECK: 7: Creating external graph symbol for COFF symbol "func" in (external) (index: 0) + + .text + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + callq func + retq Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_external_var.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_external_var.s @@ -0,0 +1,19 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink -abs var=0xcafef00d --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check an external symbol to a variable is created. +# +# CHECK: Creating graph symbols... +# CHECK: 7: Creating external graph symbol for COFF symbol "var" in (external) (index: 0) + + .text + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + movl var(%rip), %eax + retq Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_file_debug.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_file_debug.s @@ -0,0 +1,21 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink -abs func=0xcafef00d --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a file debug symbol is skipped. +# +# CHECK: Creating graph symbols... +# CHECK: 8: Skipping FileRecord symbol ".file" in (debug) (index: -2) + + .text + + .file "skip_this_file_symbol" + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + callq func + retq Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_static_var.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_static_var.s @@ -0,0 +1,24 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink -abs var=0xcafef00d --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a local symbol is created for a static variable. +# +# CHECK: Creating graph symbols... +# CHECK: 7: Creating defined graph symbol for COFF symbol "var" in .data (index: 2) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000000, linkage: strong, scope: local, dead - var + + .text + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + retq + + .data + .p2align 2 +var: + .long 53 Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_weak_external.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_weak_external.s @@ -0,0 +1,32 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %t +# RUN: llvm-jitlink -abs var=0xcafef00d --debug-only=jitlink -noexec %t 2>&1 | FileCheck %s +# +# Check a default symbol is aliased as a weak external symbol. +# +# CHECK: Creating graph symbols... +# CHECK: 8: Creating defined graph symbol for COFF symbol ".weak.func.default.main" in .text (index: 1) +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000000, linkage: strong, scope: default, dead - .weak.func.default.main +# CHECK-NEXT: 9: Creating defined graph symbol for COFF symbol "main" in .text (index: 1) +# CHECK-NEXT: 0x10 (block + 0x00000010): size: 0x00000000, linkage: strong, scope: default, dead - main +# CHECK-NEXT: 6: Creating weak external symbol for COFF symbol "func" in section 0 +# CHECK-NEXT: 0x0 (block + 0x00000000): size: 0x00000000, linkage: weak, scope: default, dead - func + + .text + + .def func; + .scl 2; + .type 32; + .endef + .weak func + .p2align 4, 0x90 +func: + retq + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + retq Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_x86-64_small_pic_relocations.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_x86-64_small_pic_relocations.s @@ -0,0 +1,78 @@ +# RUN: rm -rf %t && mkdir -p %t +# RUN: llvm-mc -triple=x86_64-windows-msvc -relax-relocations=false \ +# RUN: -position-independent -filetype=obj -o %t/coff_sm_reloc.o %s +# RUN: llvm-jitlink -noexec \ +# RUN: -slab-allocate 100Kb -slab-address 0xfff00000 -slab-page-size 4096 \ +# RUN: -abs external_data=0xdeadbeef \ +# RUN: -abs extern_out_of_range32=0x7fff00000000 \ +# RUN: -check %s %t/coff_sm_reloc.o + + .text + + .def main; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +main: + retq + +# Check a IMAGE_REL_AMD64_REL32 relocation to local function symbol. +# jitlink-check: decode_operand(test_rel32_func, 0) = named_func - next_pc(test_rel32_func) + .def test_rel32_func; + .scl 2; + .type 32; + .endef + .globl test_rel32_func + .p2align 4, 0x90 +test_rel32_func: + callq named_func + +# Check a IMAGE_REL_AMD64_REL32 relocation to local data symbol. +# jitlink-check: decode_operand(test_rel32_data, 4) = named_data - next_pc(test_rel32_data) + .def test_rel32_data; + .scl 2; + .type 32; + .endef + .globl test_rel32_data + .p2align 4, 0x90 +test_rel32_data: + leaq named_data(%rip), %rax + +# Check that calls to external functions out-of-range from the callsite trigger +# the generation of stubs and GOT entries. This produces a BranchPCRel32 edge, +# but STUB table manager will create a STUB sequence because external function +# is out-of-range from the callsite. +# +# jitlink-check: decode_operand(test_call_extern_out_of_range32, 0) = \ +# jitlink-check: stub_addr(coff_sm_reloc.o, extern_out_of_range32) - \ +# jitlink-check: next_pc(test_call_extern_out_of_range32) +# jitlink-check: *{8}(got_addr(coff_sm_reloc.o, extern_out_of_range32)) = \ +# jitlink-check: extern_out_of_range32 + .def test_call_extern_out_of_range32; + .scl 2; + .type 32; + .endef + .globl main + .p2align 4, 0x90 +test_call_extern_out_of_range32: + callq extern_out_of_range32 + retq + +# Local named data/func that is used in conjunction with other test cases + .text + .def named_func; + .scl 2; + .type 32; + .endef + .globl named_func + .p2align 4, 0x90 +named_func: + retq + + .data + .p2align 3 +named_data: + .quad 53 + Index: llvm/tools/llvm-jitlink/CMakeLists.txt =================================================================== --- llvm/tools/llvm-jitlink/CMakeLists.txt +++ llvm/tools/llvm-jitlink/CMakeLists.txt @@ -20,6 +20,7 @@ add_llvm_tool(llvm-jitlink llvm-jitlink.cpp + llvm-jitlink-coff.cpp llvm-jitlink-elf.cpp llvm-jitlink-macho.cpp ) Index: llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp @@ -0,0 +1,174 @@ +//===--- llvm-jitlink-coff.cpp -- COFF parsing support for llvm-jitlink ---===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// COFF parsing support for llvm-jitlink. +// +//===----------------------------------------------------------------------===// + +#include "llvm-jitlink.h" + +#include "llvm/Support/Error.h" +#include "llvm/Support/Path.h" + +#define DEBUG_TYPE "llvm_jitlink" + +using namespace llvm; +using namespace llvm::jitlink; + +static bool isCOFFGOTSection(Section &S) { return S.getName() == "$__GOT"; } + +static bool isCOFFStubsSection(Section &S) { return S.getName() == "$__STUBS"; } + +static Expected getFirstRelocationEdge(LinkGraph &G, Block &B) { + auto EItr = std::find_if(B.edges().begin(), B.edges().end(), + [](Edge &E) { return E.isRelocation(); }); + if (EItr == B.edges().end()) + return make_error("GOT entry in " + G.getName() + ", \"" + + B.getSection().getName() + + "\" has no relocations", + inconvertibleErrorCode()); + return *EItr; +} + +static Expected getCOFFGOTTarget(LinkGraph &G, Block &B) { + auto E = getFirstRelocationEdge(G, B); + if (!E) + return E.takeError(); + auto &TargetSym = E->getTarget(); + if (!TargetSym.hasName()) + return make_error( + "GOT entry in " + G.getName() + ", \"" + + TargetSym.getBlock().getSection().getName() + + "\" points to anonymous " + "symbol", + inconvertibleErrorCode()); + return TargetSym; +} + +static Expected getCOFFStubTarget(LinkGraph &G, Block &B) { + auto E = getFirstRelocationEdge(G, B); + if (!E) + return E.takeError(); + auto &GOTSym = E->getTarget(); + if (!GOTSym.isDefined() || !isCOFFGOTSection(GOTSym.getBlock().getSection())) + return make_error( + "Stubs entry in " + G.getName() + ", \"" + + GOTSym.getBlock().getSection().getName() + + "\" does not point to GOT entry", + inconvertibleErrorCode()); + return getCOFFGOTTarget(G, GOTSym.getBlock()); +} + +namespace llvm { +Error registerCOFFGraphInfo(Session &S, LinkGraph &G) { + auto FileName = sys::path::filename(G.getName()); + if (S.FileInfos.count(FileName)) { + return make_error("When -check is passed, file names must be " + "distinct (duplicate: \"" + + FileName + "\")", + inconvertibleErrorCode()); + } + + auto &FileInfo = S.FileInfos[FileName]; + LLVM_DEBUG( + { dbgs() << "Registering COFF file info for \"" << FileName << "\"\n"; }); + for (auto &Sec : G.sections()) { + LLVM_DEBUG({ + dbgs() << " Section \"" << Sec.getName() << "\": " + << (llvm::empty(Sec.symbols()) ? "empty. skipping." + : "processing...") + << "\n"; + }); + + // Skip empty sections. + if (llvm::empty(Sec.symbols())) + continue; + + if (FileInfo.SectionInfos.count(Sec.getName())) + return make_error("Encountered duplicate section name \"" + + Sec.getName() + "\" in \"" + FileName + + "\"", + inconvertibleErrorCode()); + + bool isGOTSection = isCOFFGOTSection(Sec); + bool isStubsSection = isCOFFStubsSection(Sec); + + bool SectionContainsContent = false; + bool SectionContainsZeroFill = false; + + auto *FirstSym = *Sec.symbols().begin(); + auto *LastSym = FirstSym; + for (auto *Sym : Sec.symbols()) { + if (Sym->getAddress() < FirstSym->getAddress()) + FirstSym = Sym; + if (Sym->getAddress() > LastSym->getAddress()) + LastSym = Sym; + + if (isGOTSection) { + if (Sym->isSymbolZeroFill()) + return make_error("zero-fill atom in GOT section", + inconvertibleErrorCode()); + + // If this is a GOT symbol with size (i.e. not the GOT start symbol) + // then add it to the GOT entry info table. + if (Sym->getSize() != 0) { + if (auto TS = getCOFFGOTTarget(G, Sym->getBlock())) + FileInfo.GOTEntryInfos[TS->getName()] = { + Sym->getSymbolContent(), Sym->getAddress().getValue()}; + else + return TS.takeError(); + } + SectionContainsContent = true; + } else if (isStubsSection) { + if (Sym->isSymbolZeroFill()) + return make_error("zero-fill atom in Stub section", + inconvertibleErrorCode()); + + if (auto TS = getCOFFStubTarget(G, Sym->getBlock())) + FileInfo.StubInfos[TS->getName()] = {Sym->getSymbolContent(), + Sym->getAddress().getValue()}; + else + return TS.takeError(); + SectionContainsContent = true; + } + + if (Sym->hasName()) { + if (Sym->isSymbolZeroFill()) { + S.SymbolInfos[Sym->getName()] = {Sym->getSize(), + Sym->getAddress().getValue()}; + SectionContainsZeroFill = true; + } else { + S.SymbolInfos[Sym->getName()] = {Sym->getSymbolContent(), + Sym->getAddress().getValue()}; + SectionContainsContent = true; + } + } + } + + auto SecAddr = FirstSym->getAddress(); + auto SecSize = + (LastSym->getBlock().getAddress() + LastSym->getBlock().getSize()) - + SecAddr; + + if (SectionContainsZeroFill && SectionContainsContent) + return make_error("Mixed zero-fill and content sections not " + "supported yet", + inconvertibleErrorCode()); + + if (SectionContainsZeroFill) + FileInfo.SectionInfos[Sec.getName()] = {SecSize, SecAddr.getValue()}; + else + FileInfo.SectionInfos[Sec.getName()] = { + ArrayRef(FirstSym->getBlock().getContent().data(), SecSize), + SecAddr.getValue()}; + } + + return Error::success(); +} + +} // end namespace llvm Index: llvm/tools/llvm-jitlink/llvm-jitlink.h =================================================================== --- llvm/tools/llvm-jitlink/llvm-jitlink.h +++ llvm/tools/llvm-jitlink/llvm-jitlink.h @@ -88,6 +88,9 @@ /// Record symbols, GOT entries, stubs, and sections for MachO file. Error registerMachOGraphInfo(Session &S, jitlink::LinkGraph &G); +/// Record symbols, GOT entries, stubs, and sections for COFF file. +Error registerCOFFGraphInfo(Session &S, jitlink::LinkGraph &G); + } // end namespace llvm #endif // LLVM_TOOLS_LLVM_JITLINK_LLVM_JITLINK_H Index: llvm/tools/llvm-jitlink/llvm-jitlink.cpp =================================================================== --- llvm/tools/llvm-jitlink/llvm-jitlink.cpp +++ llvm/tools/llvm-jitlink/llvm-jitlink.cpp @@ -1085,7 +1085,7 @@ Err = P.takeError(); return; } - } else if (!TT.isOSWindows() && !TT.isOSBinFormatMachO()) { + } else if (TT.isOSBinFormatELF()) { if (!NoExec) ObjLayer.addPlugin(std::make_unique( ES, ExitOnErr(EPCEHFrameRegistrar::Create(this->ES)))); @@ -1144,6 +1144,9 @@ if (EPC.getTargetTriple().getObjectFormat() == Triple::MachO) return registerMachOGraphInfo(*this, G); + if (EPC.getTargetTriple().isOSWindows()) + return registerCOFFGraphInfo(*this, G); + return make_error("Unsupported object format for GOT/stub " "registration", inconvertibleErrorCode()); @@ -1252,13 +1255,17 @@ assert(!InputFiles.empty() && "InputFiles can not be empty"); for (auto InputFile : InputFiles) { auto ObjBuffer = ExitOnErr(getFile(InputFile)); - switch (identify_magic(ObjBuffer->getBuffer())) { + file_magic Magic = identify_magic(ObjBuffer->getBuffer()); + switch (Magic) { + case file_magic::coff_object: case file_magic::elf_relocatable: - case file_magic::macho_object: - case file_magic::coff_object: { + case file_magic::macho_object: { auto Obj = ExitOnErr( object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef())); - return Obj->makeTriple(); + Triple TT = Obj->makeTriple(); + if (Magic == file_magic::coff_object) + TT.setOS(Triple::OSType::Win32); + return TT; } default: break;