[llvm] db995d7 - [JITLink][COFF] Initial COFF support.
Sunho Kim via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 12 11:54:06 PDT 2022
Author: Sunho Kim
Date: 2022-07-13T03:52:43+09:00
New Revision: db995d72db14e58a3a81b0c2668bf788a61cc131
URL: https://github.com/llvm/llvm-project/commit/db995d72db14e58a3a81b0c2668bf788a61cc131
DIFF: https://github.com/llvm/llvm-project/commit/db995d72db14e58a3a81b0c2668bf788a61cc131.diff
LOG: [JITLink][COFF] Initial COFF support.
Adds initial COFF support in JITLink. This is able to run a hello world c program in x86 windows successfully.
Implemented
- COFF object loader
- Static local symbols
- Absolute symbols
- External symbols
- Weak external symbols
- Common symbols
- COFF jitlink-check support
- All COMDAT selection type execpt largest
- Implicit symobl size calculation
- Rel32 relocation with PLT stub.
- IMAGE_REL_AMD64_ADDR32NB relocation
Reviewed By: lhames
Differential Revision: https://reviews.llvm.org/D128968
Added:
llvm/include/llvm/ExecutionEngine/JITLink/COFF.h
llvm/include/llvm/ExecutionEngine/JITLink/COFF_x86_64.h
llvm/lib/ExecutionEngine/JITLink/COFF.cpp
llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp
llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.h
llvm/lib/ExecutionEngine/JITLink/COFF_x86_64.cpp
llvm/test/ExecutionEngine/JITLink/X86/COFF_abs.s
llvm/test/ExecutionEngine/JITLink/X86/COFF_basic.s
llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_any.test
llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_associative.test
llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_exact_match.test
llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_largest.test
llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_noduplicate.test
llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_same_size.test
llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_weak.s
llvm/test/ExecutionEngine/JITLink/X86/COFF_common_symbol.s
llvm/test/ExecutionEngine/JITLink/X86/COFF_external_func.s
llvm/test/ExecutionEngine/JITLink/X86/COFF_external_var.s
llvm/test/ExecutionEngine/JITLink/X86/COFF_file_debug.s
llvm/test/ExecutionEngine/JITLink/X86/COFF_static_var.s
llvm/test/ExecutionEngine/JITLink/X86/COFF_weak_external.s
llvm/test/ExecutionEngine/JITLink/X86/COFF_x86-64_small_pic_relocations.s
llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp
Modified:
llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt
llvm/lib/ExecutionEngine/JITLink/JITLink.cpp
llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp
llvm/tools/llvm-jitlink/CMakeLists.txt
llvm/tools/llvm-jitlink/llvm-jitlink.cpp
llvm/tools/llvm-jitlink/llvm-jitlink.h
Removed:
################################################################################
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/COFF.h b/llvm/include/llvm/ExecutionEngine/JITLink/COFF.h
new file mode 100644
index 0000000000000..87d3648d37e8b
--- /dev/null
+++ b/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<std::unique_ptr<LinkGraph>>
+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<LinkGraph> G,
+ std::unique_ptr<JITLinkContext> Ctx);
+
+} // end namespace jitlink
+} // end namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_JITLINK_COFF_H
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/COFF_x86_64.h b/llvm/include/llvm/ExecutionEngine/JITLink/COFF_x86_64.h
new file mode 100644
index 0000000000000..fff32d6d96093
--- /dev/null
+++ b/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<std::unique_ptr<LinkGraph>>
+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<LinkGraph> G,
+ std::unique_ptr<JITLinkContext> 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
diff --git a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt
index 5a5bdae3a1bd8..87892c080af25 100644
--- a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt
@@ -21,6 +21,11 @@ add_llvm_component_library(LLVMJITLink
ELF_riscv.cpp
ELF_x86_64.cpp
+ # COFF
+ COFF.cpp
+ COFFLinkGraphBuilder.cpp
+ COFF_x86_64.cpp
+
# Architectures:
aarch64.cpp
riscv.cpp
diff --git a/llvm/lib/ExecutionEngine/JITLink/COFF.cpp b/llvm/lib/ExecutionEngine/JITLink/COFF.cpp
new file mode 100644
index 0000000000000..fddc9b813fb2a
--- /dev/null
+++ b/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 <cstring>
+
+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<std::unique_ptr<LinkGraph>>
+createLinkGraphFromCOFFObject(MemoryBufferRef ObjectBuffer) {
+ StringRef Data = ObjectBuffer.getBuffer();
+
+ // Check magic
+ auto Magic = identify_magic(ObjectBuffer.getBuffer());
+ if (Magic != file_magic::coff_object)
+ return make_error<JITLinkError>("Invalid COFF buffer");
+
+ if (Data.size() < sizeof(object::coff_file_header))
+ return make_error<JITLinkError>("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<const object::dos_header *>(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<JITLinkError>("Incorrect PE magic");
+ }
+ CurPtr += sizeof(COFF::PEMagic);
+ IsPE = true;
+ }
+ }
+ if (Data.size() < CurPtr + sizeof(object::coff_file_header))
+ return make_error<JITLinkError>("Truncated COFF buffer");
+
+ const object::coff_file_header *COFFHeader =
+ reinterpret_cast<const object::coff_file_header *>(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<JITLinkError>("Truncated COFF buffer");
+ }
+ COFFBigObjHeader =
+ reinterpret_cast<const object::coff_bigobj_file_header *>(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<JITLinkError>(
+ "Unsupported target machine architecture in COFF object " +
+ ObjectBuffer.getBufferIdentifier() + ": " + getMachineName(Machine));
+ }
+}
+
+void link_COFF(std::unique_ptr<LinkGraph> G,
+ std::unique_ptr<JITLinkContext> 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<JITLinkError>(
+ "Unsupported target machine architecture in COFF link graph " +
+ G->getName()));
+ return;
+ }
+}
+
+} // end namespace jitlink
+} // end namespace llvm
diff --git a/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp b/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp
new file mode 100644
index 0000000000000..fac3d6ae142b0
--- /dev/null
+++ b/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<LinkGraph>(
+ 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
diff erence 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<std::unique_ptr<LinkGraph>> COFFLinkGraphBuilder::buildGraph() {
+ if (!Obj.isRelocatableObject())
+ return make_error<JITLinkError>("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<StringRef> 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<const object::coff_section *> Sec = Obj.getSection(SecIndex);
+ if (!Sec)
+ return Sec.takeError();
+
+ StringRef SectionName;
+ if (Expected<StringRef> 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<JITLinkError>("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<uint8_t> Data;
+ if (auto Err = Obj.getSectionContents(*Sec, Data))
+ return Err;
+
+ B = &G->createContentBlock(
+ *GraphSec,
+ ArrayRef<char>(reinterpret_cast<const char *>(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");
+
+ SymbolSets.resize(Obj.getNumberOfSections() + 1);
+ GraphSymbols.resize(Obj.getNumberOfSymbols());
+
+ for (COFFSymbolIndex SymIndex = 0; SymIndex < Obj.getNumberOfSymbols();
+ SymIndex++) {
+ Expected<object::COFFSymbolRef> Sym = Obj.getSymbol(SymIndex);
+ if (!Sym)
+ return Sym.takeError();
+
+ StringRef SymbolName;
+ if (Expected<StringRef> 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<JITLinkError>(
+ "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<object::coff_aux_weak_external>()->TagIndex;
+ assert(Sym->getAux<object::coff_aux_weak_external>()->Characteristics !=
+ COFF::IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY &&
+ "IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY is not supported.");
+ assert(Sym->getAux<object::coff_aux_weak_external>()->Characteristics !=
+ COFF::IMAGE_WEAK_EXTERN_SEARCH_LIBRARY &&
+ "IMAGE_WEAK_EXTERN_SEARCH_LIBRARY is not supported.");
+ WeakAliasRequests.push_back({SymIndex, TagIndex, SymbolName});
+ } else {
+ Expected<jitlink::Symbol *> NewGSym =
+ createDefinedSymbol(SymIndex, SymbolName, *Sym, Sec);
+ if (!NewGSym)
+ return NewGSym.takeError();
+ 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<object::COFFSymbolRef> 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<JITLinkError>("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<JITLinkError>("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<Symbol *> 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<JITLinkError>(
+ "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 (!isComdatSection(Section))
+ return &G->addDefinedSymbol(
+ *B, Symbol.getValue(), SymbolName, 0, Linkage::Strong, Scope::Default,
+ Symbol.getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION, false);
+ else {
+ if (!PendingComdatExport)
+ return make_error<JITLinkError>("No pending COMDAT export for symbol " +
+ formatv("{0:d}", SymIndex));
+ if (PendingComdatExport->SectionIndex != Symbol.getSectionNumber())
+ return make_error<JITLinkError>(
+ "COMDAT export section number mismatch for symbol " +
+ formatv("{0:d}", SymIndex));
+ 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)) {
+ // 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);
+ }
+ if (PendingComdatExport)
+ return make_error<JITLinkError>(
+ "COMDAT export request already exists before symbol " +
+ formatv("{0:d}", SymIndex));
+ return createCOMDATExportRequest(SymIndex, Symbol, Definition);
+ }
+ return make_error<JITLinkError>("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 initiate pending COMDAT
+// export request when we encounter the first symbol and actually exports it
+// when we process the second symbol.
+//
+// Process the first symbol of COMDAT sequence.
+Expected<Symbol *> COFFLinkGraphBuilder::createCOMDATExportRequest(
+ 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<JITLinkError>(
+ "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<JITLinkError>(
+ "IMAGE_COMDAT_SELECT_NEWEST is not supported.");
+ }
+ default: {
+ return make_error<JITLinkError>("Invalid comdat selection type: " +
+ formatv("{0:d}", Definition->Selection));
+ }
+ }
+ PendingComdatExport = {SymIndex, Symbol.getSectionNumber(), L};
+ return &G->addAnonymousSymbol(*B, Symbol.getValue(), Definition->Length,
+ false, false);
+}
+
+// Process the second symbol of COMDAT sequence.
+Expected<Symbol *>
+COFFLinkGraphBuilder::exportCOMDATSymbol(COFFSymbolIndex SymIndex,
+ StringRef SymbolName,
+ object::COFFSymbolRef Symbol) {
+ COFFSymbolIndex TargetIndex = PendingComdatExport->SymbolIndex;
+ Linkage L = PendingComdatExport->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";
+ });
+ PendingComdatExport = None;
+ return Target;
+}
+
+} // namespace jitlink
+} // namespace llvm
\ No newline at end of file
diff --git a/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.h b/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.h
new file mode 100644
index 0000000000000..8c3e19d6518db
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.h
@@ -0,0 +1,197 @@
+//===----- 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 <list>
+
+namespace llvm {
+namespace jitlink {
+
+class COFFLinkGraphBuilder {
+public:
+ virtual ~COFFLinkGraphBuilder();
+ Expected<std::unique_ptr<LinkGraph>> 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 <typename RelocHandlerFunction>
+ 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 <typename ClassT, typename RelocHandlerMethod>
+ 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:
+ // Pending comdat symbol export that is initiated by the first symbol of
+ // COMDAT sequence.
+ struct ComdatExportRequest {
+ COFFSymbolIndex SymbolIndex;
+ COFFSectionIndex SectionIndex;
+ jitlink::Linkage Linkage;
+ };
+ Optional<ComdatExportRequest> PendingComdatExport;
+
+ // This represents a pending request to create a weak external symbol with a
+ // name.
+ struct WeakAliasRequest {
+ COFFSymbolIndex Alias;
+ COFFSymbolIndex Target;
+ StringRef SymbolName;
+ };
+ std::vector<WeakAliasRequest> WeakAliasRequests;
+
+ // Per COFF section jitlink symbol set sorted by offset.
+ // Used for calculating implicit size of defined symbols.
+ using SymbolSet = std::set<std::pair<orc::ExecutorAddrDiff, Symbol *>>;
+ std::vector<SymbolSet> SymbolSets;
+
+ Section &getCommonSection();
+
+ Expected<Symbol *> createDefinedSymbol(COFFSymbolIndex SymIndex,
+ StringRef SymbolName,
+ object::COFFSymbolRef Symbol,
+ const object::coff_section *Section);
+ Expected<Symbol *> createCOMDATExportRequest(
+ COFFSymbolIndex SymIndex, object::COFFSymbolRef Symbol,
+ const object::coff_aux_section_definition *Definition);
+ Expected<Symbol *> 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<LinkGraph> G;
+
+ Section *CommonSection = nullptr;
+ std::vector<Block *> GraphBlocks;
+ std::vector<Symbol *> GraphSymbols;
+};
+
+template <typename RelocHandlerFunction>
+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<StringRef> 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<StringError>(
+ "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
diff --git a/llvm/lib/ExecutionEngine/JITLink/COFF_x86_64.cpp b/llvm/lib/ExecutionEngine/JITLink/COFF_x86_64.cpp
new file mode 100644
index 0000000000000..d5d6fd9f197dd
--- /dev/null
+++ b/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<COFFJITLinker_x86_64> {
+ friend class JITLinker<COFFJITLinker_x86_64>;
+
+public:
+ COFFJITLinker_x86_64(std::unique_ptr<JITLinkContext> Ctx,
+ std::unique_ptr<LinkGraph> 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<COFFX86RelocationKind>
+ 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<JITLinkError>("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<uint64_t>::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<StringError>(
+ 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<StringError>(
+ formatv("Could not find symbol at given index, did you add it to "
+ "JITSymbolTable? index: {0}, section: {1}",
+ SymIndex, FixupSect.getIndex()),
+ inconvertibleErrorCode());
+
+ Expected<COFFX86RelocationKind> 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<std::unique_ptr<LinkGraph>>
+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<LinkGraph> G,
+ std::unique_ptr<JITLinkContext> 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
diff --git a/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp b/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp
index 3e8bfa35d16a3..08fdc7c9e6b10 100644
--- a/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp
+++ b/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"
@@ -408,6 +409,8 @@ createLinkGraphFromObject(MemoryBufferRef ObjectBuffer) {
return createLinkGraphFromMachOObject(ObjectBuffer);
case file_magic::elf_relocatable:
return createLinkGraphFromELFObject(ObjectBuffer);
+ case file_magic::coff_object:
+ return createLinkGraphFromCOFFObject(ObjectBuffer);
default:
return make_error<JITLinkError>("Unsupported file format");
};
@@ -419,6 +422,8 @@ void link(std::unique_ptr<LinkGraph> G, std::unique_ptr<JITLinkContext> Ctx) {
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<JITLinkError>("Unsupported object format"));
};
diff --git a/llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp b/llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp
index 394a555e453b8..356b81b4f1c56 100644
--- a/llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp
+++ b/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 @@ getELFObjectFileSymbolInfo(ExecutionSession &ES,
return I;
}
+static Expected<MaterializationUnit::Interface>
+getCOFFObjectFileSymbolInfo(ExecutionSession &ES,
+ const object::COFFObjectFile &Obj) {
+ MaterializationUnit::Interface I;
+
+ for (auto &Sym : Obj.symbols()) {
+ Expected<uint32_t> 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<MaterializationUnit::Interface>
getGenericObjectFileSymbolInfo(ExecutionSession &ES,
const object::ObjectFile &Obj) {
@@ -196,6 +246,8 @@ getObjectFileInterface(ExecutionSession &ES, MemoryBufferRef ObjBuffer) {
return getMachOObjectFileSymbolInfo(ES, *MachOObj);
else if (auto *ELFObj = dyn_cast<object::ELFObjectFileBase>(Obj->get()))
return getELFObjectFileSymbolInfo(ES, *ELFObj);
+ else if (auto *COFFObj = dyn_cast<object::COFFObjectFile>(Obj->get()))
+ return getCOFFObjectFileSymbolInfo(ES, *COFFObj);
return getGenericObjectFileSymbolInfo(ES, **Obj);
}
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_abs.s b/llvm/test/ExecutionEngine/JITLink/X86/COFF_abs.s
new file mode 100644
index 0000000000000..d80a5041e7609
--- /dev/null
+++ b/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
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_basic.s b/llvm/test/ExecutionEngine/JITLink/X86/COFF_basic.s
new file mode 100644
index 0000000000000..1965c2820c123
--- /dev/null
+++ b/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
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_any.test b/llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_any.test
new file mode 100644
index 0000000000000..7bac246911c9e
--- /dev/null
+++ b/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 - <anonymous symbol>
+# 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
+...
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_associative.test b/llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_associative.test
new file mode 100644
index 0000000000000..13ec0b4be2710
--- /dev/null
+++ b/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 - <anonymous symbol>
+# 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
+...
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_exact_match.test b/llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_exact_match.test
new file mode 100644
index 0000000000000..5b04480f71475
--- /dev/null
+++ b/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 - <anonymous symbol>
+# 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
+...
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_largest.test b/llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_largest.test
new file mode 100644
index 0000000000000..13150f00132e7
--- /dev/null
+++ b/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
+...
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_noduplicate.test b/llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_noduplicate.test
new file mode 100644
index 0000000000000..3880fe656ea60
--- /dev/null
+++ b/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 - <anonymous symbol>
+# 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
+...
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_same_size.test b/llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_same_size.test
new file mode 100644
index 0000000000000..1ec5ed31d96d6
--- /dev/null
+++ b/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 - <anonymous symbol>
+# 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
+...
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_weak.s b/llvm/test/ExecutionEngine/JITLink/X86/COFF_comdat_weak.s
new file mode 100644
index 0000000000000..8b25e42c92cf0
--- /dev/null
+++ b/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 - <anonymous symbol>
+# 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
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_common_symbol.s b/llvm/test/ExecutionEngine/JITLink/X86/COFF_common_symbol.s
new file mode 100644
index 0000000000000..e2a126a5169f2
--- /dev/null
+++ b/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
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_external_func.s b/llvm/test/ExecutionEngine/JITLink/X86/COFF_external_func.s
new file mode 100644
index 0000000000000..e84b975bc89a6
--- /dev/null
+++ b/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
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_external_var.s b/llvm/test/ExecutionEngine/JITLink/X86/COFF_external_var.s
new file mode 100644
index 0000000000000..c3575179ae97c
--- /dev/null
+++ b/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
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_file_debug.s b/llvm/test/ExecutionEngine/JITLink/X86/COFF_file_debug.s
new file mode 100644
index 0000000000000..8413c08d92207
--- /dev/null
+++ b/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
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_static_var.s b/llvm/test/ExecutionEngine/JITLink/X86/COFF_static_var.s
new file mode 100644
index 0000000000000..e5a6ebde86da5
--- /dev/null
+++ b/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
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_weak_external.s b/llvm/test/ExecutionEngine/JITLink/X86/COFF_weak_external.s
new file mode 100644
index 0000000000000..d3d65eff02b97
--- /dev/null
+++ b/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
diff --git a/llvm/test/ExecutionEngine/JITLink/X86/COFF_x86-64_small_pic_relocations.s b/llvm/test/ExecutionEngine/JITLink/X86/COFF_x86-64_small_pic_relocations.s
new file mode 100644
index 0000000000000..1118cf74abaf2
--- /dev/null
+++ b/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
+
diff --git a/llvm/tools/llvm-jitlink/CMakeLists.txt b/llvm/tools/llvm-jitlink/CMakeLists.txt
index c9f9a536d49d5..65cc0c9493ede 100644
--- a/llvm/tools/llvm-jitlink/CMakeLists.txt
+++ b/llvm/tools/llvm-jitlink/CMakeLists.txt
@@ -20,6 +20,7 @@ set(LLVM_LINK_COMPONENTS
add_llvm_tool(llvm-jitlink
llvm-jitlink.cpp
+ llvm-jitlink-coff.cpp
llvm-jitlink-elf.cpp
llvm-jitlink-macho.cpp
)
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink-coff.cpp
new file mode 100644
index 0000000000000..2e7b000da775f
--- /dev/null
+++ b/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<Edge &> 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<StringError>("GOT entry in " + G.getName() + ", \"" +
+ B.getSection().getName() +
+ "\" has no relocations",
+ inconvertibleErrorCode());
+ return *EItr;
+}
+
+static Expected<Symbol &> 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<StringError>(
+ "GOT entry in " + G.getName() + ", \"" +
+ TargetSym.getBlock().getSection().getName() +
+ "\" points to anonymous "
+ "symbol",
+ inconvertibleErrorCode());
+ return TargetSym;
+}
+
+static Expected<Symbol &> 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<StringError>(
+ "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<StringError>("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<StringError>("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<StringError>("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<StringError>("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<StringError>("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<char>(FirstSym->getBlock().getContent().data(), SecSize),
+ SecAddr.getValue()};
+ }
+
+ return Error::success();
+}
+
+} // end namespace llvm
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
index 8146419ac341c..a9187073beee3 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
@@ -1085,7 +1085,7 @@ Session::Session(std::unique_ptr<ExecutorProcessControl> EPC, Error &Err)
Err = P.takeError();
return;
}
- } else if (!TT.isOSWindows() && !TT.isOSBinFormatMachO()) {
+ } else if (TT.isOSBinFormatELF()) {
if (!NoExec)
ObjLayer.addPlugin(std::make_unique<EHFrameRegistrationPlugin>(
ES, ExitOnErr(EPCEHFrameRegistrar::Create(this->ES))));
@@ -1144,6 +1144,9 @@ void Session::modifyPassConfig(const Triple &TT,
if (EPC.getTargetTriple().getObjectFormat() == Triple::MachO)
return registerMachOGraphInfo(*this, G);
+ if (EPC.getTargetTriple().isOSWindows())
+ return registerCOFFGraphInfo(*this, G);
+
return make_error<StringError>("Unsupported object format for GOT/stub "
"registration",
inconvertibleErrorCode());
@@ -1252,13 +1255,17 @@ static Triple getFirstFileTriple() {
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;
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.h b/llvm/tools/llvm-jitlink/llvm-jitlink.h
index 71bdab7862c6e..9cac3766a7ad3 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.h
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.h
@@ -88,6 +88,9 @@ Error registerELFGraphInfo(Session &S, jitlink::LinkGraph &G);
/// 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
More information about the llvm-commits
mailing list