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 @@ +//===--- ELF_x86_64.h - JIT link functions for ELF/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 ELF/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,68 @@ +//===-------------- 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 { + +Expected> +createLinkGraphFromCOFFObject(MemoryBufferRef ObjectBuffer) { + StringRef Buffer = ObjectBuffer.getBuffer(); + /* + std::error_code EC; + if (Buffer.size() < sizeof(object::coff_file_header)) + return make_error("Truncated COFF buffer"); + + Expected TargetMachineArch = readTargetMachineArch(Buffer); + if (!TargetMachineArch) + return TargetMachineArch.takeError();*/ + /* + switch (*TargetMachineArch) { + case ELF::EM_X86_64: + return createLinkGraphFromCOFFObject_x86_64(ObjectBuffer); + default: + return make_error( + "Unsupported target machine architecture in COFF object " + + ObjectBuffer.getBufferIdentifier()); + }*/ + return createLinkGraphFromCOFFObject_x86_64(ObjectBuffer); +} + +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,166 @@ +//===----- 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(COFFSymbolIndex SymIndex, Symbol &Sym) { + assert(!GraphSymbols[SymIndex] && "Duplicate symbol at index"); + GraphSymbols[SymIndex] = &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"); + 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 COFF::relocation&, + /// 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: + 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); + + struct WeakAliasRequest { + COFFSymbolIndex Alias; + COFFSymbolIndex Target; + StringRef SymbolName; + }; + + Section &getCommonSection(); + Expected createDefinedSymbol(COFFSymbolIndex SymIndex, + StringRef SymbolName, + object::COFFSymbolRef Symbol, + const object::coff_section *Section); + + const object::COFFObjectFile &Obj; + std::unique_ptr G; + + Section *CommonSection = nullptr; + std::vector GraphBlocks; + std::vector GraphSymbols; + std::vector>> ComdatLeaders; + std::vector WeakAliases; +}; + +template +Error COFFLinkGraphBuilder::forEachRelocation(const object::SectionRef &RelSec, + RelocHandlerFunction &&Func, + bool ProcessDebugSections) { + + auto COFFRelSect = Obj.getCOFFSection(RelSec); + + // Target sections have names in valid ELF 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,392 @@ +//=--------- 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() + << "\""; + }); +} + +COFFLinkGraphBuilder::~COFFLinkGraphBuilder() = default; + +unsigned +COFFLinkGraphBuilder::getPointerSize(const object::COFFObjectFile &Obj) { + return Obj.is64() ? 8 : 4; +} + +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); +} + +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; + + // Avoid loading zero-sized COFF sections. + /*bool HasContent = getSectionSize(Obj, *Sec) > 0; + bool IsDiscardable = + (*Sec)->Characteristics & + (COFF::IMAGE_SCN_MEM_DISCARDABLE | COFF::IMAGE_SCN_LNK_INFO); + dbgs() << getSectionSize(Obj, *Sec) << "\n"; + if (!HasContent || IsDiscardable) { + LLVM_DEBUG( + dbgs() << " " << SecIndex << ": \"" << SectionName + << "\" is either empty or discardable: " + "No graph section will be created.\n" + ); + continue; + }*/ + + // FIXME: Possibly skip debug info sections + + LLVM_DEBUG({ + dbgs() << " " + << "Creating section for \"" << SectionName << "\"\n"; + }); + + // Get the section's memory protection flags. + MemProt Prot = {}; + 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); + assert(GraphSec->getMemProt() == Prot && "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); + 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; + + const COFFSectionIndex SectionIndex = Sym->getSectionNumber(); + const object::coff_section *Sec = nullptr; + + // Get section name + StringRef SectionName; + if (SectionIndex == COFF::IMAGE_SYM_UNDEFINED) { + if (Sym->getValue()) + SectionName = "(common)"; + else + SectionName = "(external)"; + } else if (SectionIndex == COFF::IMAGE_SYM_ABSOLUTE) + SectionName = "(absolute)"; + else if (SectionIndex == COFF::IMAGE_SYM_DEBUG) + SectionName = "(debug)"; + else { + // Non reserved regular section number + Expected SecOrErr = + Obj.getSection(SectionIndex); + if (SecOrErr.takeError()) + return make_error("Invalid COFF section number:" + + formatv("{0:d}: ", SectionIndex)); + if (Expected SecNameOrErr = Obj.getSectionName(*SecOrErr)) + SectionName = *SecNameOrErr; + Sec = *SecOrErr; + } + + jitlink::Symbol *GSym = nullptr; + if (Sym->isFileRecord()) + LLVM_DEBUG({ + dbgs() << " " << SymIndex << ": Skipping FileRecord symbol \"" + << SymbolName << "\" in " << SectionName << "\n"; + }); + else if (Sym->isUndefined()) { + LLVM_DEBUG({ + dbgs() << " " << SymIndex + << ": Creating external graph symbol for COFF symbol \"" + << SymbolName << "\" in " << SectionName << "\n"; + }); + GSym = + &G->addExternalSymbol(SymbolName, Sym->getValue(), Linkage::Strong); + } else if (Sym->isWeakExternal()) { + const COFFSymbolIndex TagIndex = + Sym->getAux()->TagIndex; + WeakAliases.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 " << SectionName + << " 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 " << SectionName << "\n"; + }); + } + + if (GSym) + setGraphSymbol(SymIndex, *GSym); + SymIndex += Sym->getNumberOfAuxSymbols(); + } + + // Export the weak external symbols and alias it + for (auto &WeakAlias : WeakAliases) { + if (auto Target = getGraphSymbol(WeakAlias.Target)) { + assert((llvm::count_if(G->defined_symbols(), + [&](const jitlink::Symbol *Sym) { + return Sym->getName() == WeakAlias.SymbolName; + }) == 0) && + "Duplicate defined symbol"); + + Target->setLinkage(Linkage::Weak); + Target->setScope(Scope::Default); + Target->setName(WeakAlias.SymbolName); + setGraphSymbol(WeakAlias.Alias, *Target); + } else + return make_error("Weak symbol alias requested but actual " + "symbol not defined in symbol " + + WeakAlias.Alias); + } + + 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); + + const COFFSectionIndex SectionIndex = Symbol.getSectionNumber(); + if (llvm::COFF::isReservedSectionNumber(SectionIndex)) + return make_error( + "Reserved section number used in regular symbol " + + formatv("{0:d}", SymIndex)); + + // 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 + // selectino type that specifies how duplicates 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. + 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 { + // This is the second symbol of a COMDAT sequence that contains the name + // and type of the symbol. + auto Leader = *ComdatLeaders[SectionIndex]; + const COFFSymbolIndex TargetIndex = Leader.first; + const Linkage L = Leader.second; + 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); + setGraphSymbol(SymIndex, *Target); + return nullptr; + } + } + + if (Symbol.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC) { + const object::coff_aux_section_definition *Def = + Symbol.getSectionDefinition(); + if (!Def || !isComdatSection(Section) || ComdatLeaders[SectionIndex]) { + // 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 (Def->Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { + // Dead strip should handle this implictly + return nullptr; + } + Linkage L = Linkage::Strong; + jitlink::Symbol *GSym = &G->addAnonymousSymbol(*B, Symbol.getValue(), + Def->Length, false, false); + switch (Def->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}", Def->Selection)); + } + } + ComdatLeaders[SectionIndex] = {SymIndex, L}; + return GSym; + } + return make_error("Unsupported storage class " + + formatv("{0:d}", Symbol.getStorageClass()) + + " in symbol " + formatv("{0:d}", SymIndex)); +} +} // 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,188 @@ +//===----- COFF_x86_64.cpp - JIT linker implementation for ELF/aarch64 ----===// +// +// 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: + enum COFFX86RelocationKind : Edge::Kind { + COFFAddr32NB = Edge::FirstRelocation, + }; + + static Expected + getRelocationKind(const uint32_t Type) { + switch (Type) { + case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_ADDR32NB: + return COFFAddr32NB; + } + + 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(); + } + + Error addSingleRelocation(const object::RelocationRef &Rel, + const object::SectionRef &FixupSect, + Block &BlockToFix) { + + const object::coff_relocation *COFFRel = getObject().getCOFFRelocation(Rel); + const 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()); + } + + const object::COFFSymbolRef COFFSymbol = + getObject().getCOFFSymbol(*SymbolIt); + const 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; + 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 ELF aarch64 edge kind. + const char *getCOFFX86RelocationKindName(Edge::Kind R) { + switch (R) { + case COFFAddr32NB: + return "COFFAddr32NB"; + default: + return getGenericEdgeKindName(static_cast(R)); + } + } + +public: + COFFLinkGraphBuilder_x86_64(const object::COFFObjectFile &Obj, const Triple T) + : COFFLinkGraphBuilder(Obj, std::move(T), x86_64::getEdgeKindName) {} +}; + +} // 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); + } + + 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/Core.cpp =================================================================== --- llvm/lib/ExecutionEngine/Orc/Core.cpp +++ llvm/lib/ExecutionEngine/Orc/Core.cpp @@ -2910,7 +2910,11 @@ #ifndef NDEBUG for (auto &KV : Symbols) { auto WeakFlags = JITSymbolFlags::Weak | JITSymbolFlags::Common; + dbgs() << KV.first << "\n"; + + dbgs() << KV.second.getFlags() << "\n"; auto I = MR.SymbolFlags.find(KV.first); + dbgs() << I->second << "\n"; assert(I != MR.SymbolFlags.end() && "Resolving symbol outside this responsibility set"); assert(!I->second.hasMaterializationSideEffectsOnly() && 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,61 @@ 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(); + + auto COFFSym = Obj.getCOFFSymbol(Sym); + if (!COFFSym.isUndefined() && COFFSym.isExternal()) { + *SymFlags |= JITSymbolFlags::Exported; + } + + I.SymbolFlags[ES.intern(*Name)] = std::move(*SymFlags); + } + + SymbolStringPtr InitSymbol; + for (auto &Sec : Obj.sections()) { + if (auto SecName = Sec.getName()) { + if (ELFNixPlatform::isInitializerSection(*SecName)) { + addInitSymbol(I, ES, Obj.getFileName()); + break; + } + } + } + + return I; +} + Expected getGenericObjectFileSymbolInfo(ExecutionSession &ES, const object::ObjectFile &Obj) { @@ -196,6 +252,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/lib/Object/ObjectFile.cpp =================================================================== --- llvm/lib/Object/ObjectFile.cpp +++ llvm/lib/Object/ObjectFile.cpp @@ -120,6 +120,7 @@ const auto COFFObj = cast(this); if (COFFObj->getArch() == Triple::thumb) TheTriple.setTriple("thumbv7-windows"); + TheTriple.setObjectFormat(Triple::COFF); } else if (isXCOFF()) { // XCOFF implies AIX. TheTriple.setOS(Triple::AIX); Index: llvm/test/ExecutionEngine/JITLink/X86/COFF_basic.s =================================================================== --- /dev/null +++ llvm/test/ExecutionEngine/JITLink/X86/COFF_basic.s @@ -0,0 +1,27 @@ +// RUN: llvm-mc -filetype=obj -triple x86_64-windows-msvc %s -o %t +// RUN: llvm-jitlink -noexec %t + + .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/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.isOSBinFormatCOFF() && !TT.isOSBinFormatMachO()) { 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().getObjectFormat() == Triple::COFF) + return registerCOFFGraphInfo(*this, G); + return make_error("Unsupported object format for GOT/stub " "registration", inconvertibleErrorCode()); @@ -1252,6 +1255,7 @@ assert(!InputFiles.empty() && "InputFiles can not be empty"); for (auto InputFile : InputFiles) { auto ObjBuffer = ExitOnErr(getFile(InputFile)); + auto kk = identify_magic(ObjBuffer->getBuffer()); switch (identify_magic(ObjBuffer->getBuffer())) { case file_magic::elf_relocatable: case file_magic::macho_object: