[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