[lld] 7370ff6 - [LLD] Remove global state in lld/COFF

Alexandre Ganea via llvm-commits llvm-commits at lists.llvm.org
Sun Jan 8 15:57:45 PST 2023


Author: Amy Huang
Date: 2023-01-08T18:43:13-05:00
New Revision: 7370ff624d217b0f8f7512ca5b651a9b8095a411

URL: https://github.com/llvm/llvm-project/commit/7370ff624d217b0f8f7512ca5b651a9b8095a411
DIFF: https://github.com/llvm/llvm-project/commit/7370ff624d217b0f8f7512ca5b651a9b8095a411.diff

LOG: [LLD] Remove global state in lld/COFF

Remove globals from the lldCOFF library, by moving globals into a context class.
This patch mostly moves the config object into COFFLinkerContext.

See https://lists.llvm.org/pipermail/llvm-dev/2021-June/151184.html for
context about removing globals from LLD.

Reviewed By: aganea

Differential Revision: https://reviews.llvm.org/D110450

Added: 
    

Modified: 
    lld/COFF/COFFLinkerContext.cpp
    lld/COFF/COFFLinkerContext.h
    lld/COFF/CallGraphSort.cpp
    lld/COFF/Chunks.cpp
    lld/COFF/Chunks.h
    lld/COFF/Config.h
    lld/COFF/DLL.cpp
    lld/COFF/DLL.h
    lld/COFF/DebugTypes.cpp
    lld/COFF/Driver.cpp
    lld/COFF/Driver.h
    lld/COFF/DriverUtils.cpp
    lld/COFF/ICF.cpp
    lld/COFF/ICF.h
    lld/COFF/InputFiles.cpp
    lld/COFF/InputFiles.h
    lld/COFF/LLDMapFile.cpp
    lld/COFF/LTO.cpp
    lld/COFF/LTO.h
    lld/COFF/MapFile.cpp
    lld/COFF/MarkLive.cpp
    lld/COFF/MinGW.cpp
    lld/COFF/MinGW.h
    lld/COFF/PDB.cpp
    lld/COFF/SymbolTable.cpp
    lld/COFF/SymbolTable.h
    lld/COFF/Symbols.cpp
    lld/COFF/Symbols.h
    lld/COFF/TypeMerger.h
    lld/COFF/Writer.cpp
    lld/COFF/Writer.h

Removed: 
    


################################################################################
diff  --git a/lld/COFF/COFFLinkerContext.cpp b/lld/COFF/COFFLinkerContext.cpp
index cd497903f183..4f1c7afb5a6b 100644
--- a/lld/COFF/COFFLinkerContext.cpp
+++ b/lld/COFF/COFFLinkerContext.cpp
@@ -10,13 +10,15 @@
 //===----------------------------------------------------------------------===//
 
 #include "COFFLinkerContext.h"
+#include "Symbols.h"
 #include "lld/Common/Memory.h"
+#include "llvm/BinaryFormat/COFF.h"
 #include "llvm/DebugInfo/CodeView/TypeHashing.h"
+#include "llvm/Demangle/Demangle.h"
 
 namespace lld::coff {
-
 COFFLinkerContext::COFFLinkerContext()
-    : symtab(*this), rootTimer("Total Linking Time"),
+    : driver(*this), symtab(*this), rootTimer("Total Linking Time"),
       inputFileTimer("Input File Reading", rootTimer),
       ltoTimer("LTO", rootTimer), gcTimer("GC", rootTimer),
       icfTimer("ICF", rootTimer), codeLayoutTimer("Code Layout", rootTimer),
@@ -33,6 +35,10 @@ COFFLinkerContext::COFFLinkerContext()
       symbolMergingTimer("Symbol Merging", addObjectsTimer),
       publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer),
       tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer),
-      diskCommitTimer("Commit to Disk", totalPdbLinkTimer) {}
-
+      diskCommitTimer("Commit to Disk", totalPdbLinkTimer) {
+  FakeSection ltoTextSection(llvm::COFF::IMAGE_SCN_MEM_EXECUTE);
+  FakeSection ltoDataSection(llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA);
+  ltoTextSectionChunk = make<FakeSectionChunk>(&ltoTextSection.section);
+  ltoDataSectionChunk = make<FakeSectionChunk>(&ltoDataSection.section);
+}
 } // namespace lld::coff

diff  --git a/lld/COFF/COFFLinkerContext.h b/lld/COFF/COFFLinkerContext.h
index 96b4de436398..8814efbd9099 100644
--- a/lld/COFF/COFFLinkerContext.h
+++ b/lld/COFF/COFFLinkerContext.h
@@ -6,12 +6,13 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLD_COFF_COFFLinkerContext_H
-#define LLD_COFF_COFFLinkerContext_H
+#ifndef LLD_COFF_COFFLINKERCONTEXT_H
+#define LLD_COFF_COFFLINKERCONTEXT_H
 
 #include "Chunks.h"
 #include "Config.h"
 #include "DebugTypes.h"
+#include "Driver.h"
 #include "InputFiles.h"
 #include "SymbolTable.h"
 #include "Writer.h"
@@ -27,9 +28,9 @@ class COFFLinkerContext : public CommonLinkerContext {
   COFFLinkerContext &operator=(const COFFLinkerContext &) = delete;
   ~COFFLinkerContext() = default;
 
-  void addTpiSource(TpiSource *tpi) { tpiSourceList.push_back(tpi); }
-
+  LinkerDriver driver;
   SymbolTable symtab;
+  COFFOptTable optTable;
 
   std::vector<ObjFile *> objFileInstances;
   std::map<std::string, PDBInputFile *> pdbInputFileInstances;
@@ -41,6 +42,8 @@ class COFFLinkerContext : public CommonLinkerContext {
   /// All sources of type information in the program.
   std::vector<TpiSource *> tpiSourceList;
 
+  void addTpiSource(TpiSource *tpi) { tpiSourceList.push_back(tpi); }
+
   std::map<llvm::codeview::GUID, TpiSource *> typeServerSourceMappings;
   std::map<uint32_t, TpiSource *> precompSourceMappings;
 
@@ -52,6 +55,10 @@ class COFFLinkerContext : public CommonLinkerContext {
     return c->osidx == 0 ? nullptr : outputSections[c->osidx - 1];
   }
 
+  // Fake sections for parsing bitcode files.
+  FakeSectionChunk *ltoTextSectionChunk;
+  FakeSectionChunk *ltoDataSectionChunk;
+
   // All timers used in the COFF linker.
   Timer rootTimer;
   Timer inputFileTimer;
@@ -77,6 +84,8 @@ class COFFLinkerContext : public CommonLinkerContext {
   Timer publicsLayoutTimer;
   Timer tpiStreamLayoutTimer;
   Timer diskCommitTimer;
+
+  Configuration config;
 };
 
 } // namespace lld::coff

diff  --git a/lld/COFF/CallGraphSort.cpp b/lld/COFF/CallGraphSort.cpp
index 709e69b24914..e83996b035f6 100644
--- a/lld/COFF/CallGraphSort.cpp
+++ b/lld/COFF/CallGraphSort.cpp
@@ -56,6 +56,8 @@ class CallGraphSort {
 private:
   std::vector<Cluster> clusters;
   std::vector<const SectionChunk *> sections;
+
+  const COFFLinkerContext &ctx;
 };
 
 // Maximum amount the combined cluster density can be worse than the original
@@ -71,8 +73,8 @@ using SectionPair = std::pair<const SectionChunk *, const SectionChunk *>;
 // Take the edge list in Config->CallGraphProfile, resolve symbol names to
 // Symbols, and generate a graph between InputSections with the provided
 // weights.
-CallGraphSort::CallGraphSort(const COFFLinkerContext &ctx) {
-  MapVector<SectionPair, uint64_t> &profile = config->callGraphProfile;
+CallGraphSort::CallGraphSort(const COFFLinkerContext &ctx) : ctx(ctx) {
+  const MapVector<SectionPair, uint64_t> &profile = ctx.config.callGraphProfile;
   DenseMap<const SectionChunk *, int> secToCluster;
 
   auto getOrCreateNode = [&](const SectionChunk *isec) -> int {
@@ -85,7 +87,7 @@ CallGraphSort::CallGraphSort(const COFFLinkerContext &ctx) {
   };
 
   // Create the graph.
-  for (std::pair<SectionPair, uint64_t> &c : profile) {
+  for (const std::pair<SectionPair, uint64_t> &c : profile) {
     const auto *fromSec = cast<SectionChunk>(c.first.first->repl);
     const auto *toSec = cast<SectionChunk>(c.first.second->repl);
     uint64_t weight = c.second;
@@ -205,11 +207,11 @@ DenseMap<const SectionChunk *, int> CallGraphSort::run() {
         break;
     }
   }
-  if (!config->printSymbolOrder.empty()) {
+  if (!ctx.config.printSymbolOrder.empty()) {
     std::error_code ec;
-    raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::OF_None);
+    raw_fd_ostream os(ctx.config.printSymbolOrder, ec, sys::fs::OF_None);
     if (ec) {
-      error("cannot open " + config->printSymbolOrder + ": " + ec.message());
+      error("cannot open " + ctx.config.printSymbolOrder + ": " + ec.message());
       return orderMap;
     }
     // Print the symbols ordered by C3, in the order of increasing curOrder

diff  --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index f626458fea95..880bfc34727e 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -53,8 +53,8 @@ SectionChunk::SectionChunk(ObjFile *f, const coff_section *h)
   // enabled, treat non-comdat sections as roots. Generally optimized object
   // files will be built with -ffunction-sections or /Gy, so most things worth
   // stripping will be in a comdat.
-  if (config)
-    live = !config->doGC || !isCOMDAT();
+  if (file)
+    live = !file->ctx.config.doGC || !isCOMDAT();
   else
     live = true;
 }
@@ -94,21 +94,32 @@ static void applySecRel(const SectionChunk *sec, uint8_t *off,
   add32(off, secRel);
 }
 
-static void applySecIdx(uint8_t *off, OutputSection *os) {
+static void applySecIdx(uint8_t *off, OutputSection *os,
+                        unsigned numOutputSections) {
+  // numOutputSections is the largest valid section index. Make sure that
+  // it fits in 16 bits.
+  assert(sizeof(numOutputSections) <= 0xffff &&
+         "size of outputSections is too big");
+
   // Absolute symbol doesn't have section index, but section index relocation
   // against absolute symbol should be resolved to one plus the last output
   // section index. This is required for compatibility with MSVC.
   if (os)
     add16(off, os->sectionIndex);
   else
-    add16(off, DefinedAbsolute::numOutputSections + 1);
+    add16(off, numOutputSections + 1);
 }
 
 void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os,
-                               uint64_t s, uint64_t p) const {
+                               uint64_t s, uint64_t p,
+                               uint64_t imageBase) const {
   switch (type) {
-  case IMAGE_REL_AMD64_ADDR32:   add32(off, s + config->imageBase); break;
-  case IMAGE_REL_AMD64_ADDR64:   add64(off, s + config->imageBase); break;
+  case IMAGE_REL_AMD64_ADDR32:
+    add32(off, s + imageBase);
+    break;
+  case IMAGE_REL_AMD64_ADDR64:
+    add64(off, s + imageBase);
+    break;
   case IMAGE_REL_AMD64_ADDR32NB: add32(off, s); break;
   case IMAGE_REL_AMD64_REL32:    add32(off, s - p - 4); break;
   case IMAGE_REL_AMD64_REL32_1:  add32(off, s - p - 5); break;
@@ -116,7 +127,9 @@ void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os,
   case IMAGE_REL_AMD64_REL32_3:  add32(off, s - p - 7); break;
   case IMAGE_REL_AMD64_REL32_4:  add32(off, s - p - 8); break;
   case IMAGE_REL_AMD64_REL32_5:  add32(off, s - p - 9); break;
-  case IMAGE_REL_AMD64_SECTION:  applySecIdx(off, os); break;
+  case IMAGE_REL_AMD64_SECTION:
+    applySecIdx(off, os, file->ctx.outputSections.size());
+    break;
   case IMAGE_REL_AMD64_SECREL:   applySecRel(this, off, os, s); break;
   default:
     error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
@@ -125,13 +138,18 @@ void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os,
 }
 
 void SectionChunk::applyRelX86(uint8_t *off, uint16_t type, OutputSection *os,
-                               uint64_t s, uint64_t p) const {
+                               uint64_t s, uint64_t p,
+                               uint64_t imageBase) const {
   switch (type) {
   case IMAGE_REL_I386_ABSOLUTE: break;
-  case IMAGE_REL_I386_DIR32:    add32(off, s + config->imageBase); break;
+  case IMAGE_REL_I386_DIR32:
+    add32(off, s + imageBase);
+    break;
   case IMAGE_REL_I386_DIR32NB:  add32(off, s); break;
   case IMAGE_REL_I386_REL32:    add32(off, s - p - 4); break;
-  case IMAGE_REL_I386_SECTION:  applySecIdx(off, os); break;
+  case IMAGE_REL_I386_SECTION:
+    applySecIdx(off, os, file->ctx.outputSections.size());
+    break;
   case IMAGE_REL_I386_SECREL:   applySecRel(this, off, os, s); break;
   default:
     error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
@@ -188,19 +206,26 @@ void applyBranch24T(uint8_t *off, int32_t v) {
 }
 
 void SectionChunk::applyRelARM(uint8_t *off, uint16_t type, OutputSection *os,
-                               uint64_t s, uint64_t p) const {
+                               uint64_t s, uint64_t p,
+                               uint64_t imageBase) const {
   // Pointer to thumb code must have the LSB set.
   uint64_t sx = s;
   if (os && (os->header.Characteristics & IMAGE_SCN_MEM_EXECUTE))
     sx |= 1;
   switch (type) {
-  case IMAGE_REL_ARM_ADDR32:    add32(off, sx + config->imageBase); break;
+  case IMAGE_REL_ARM_ADDR32:
+    add32(off, sx + imageBase);
+    break;
   case IMAGE_REL_ARM_ADDR32NB:  add32(off, sx); break;
-  case IMAGE_REL_ARM_MOV32T:    applyMOV32T(off, sx + config->imageBase); break;
+  case IMAGE_REL_ARM_MOV32T:
+    applyMOV32T(off, sx + imageBase);
+    break;
   case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(off, sx - p - 4); break;
   case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(off, sx - p - 4); break;
   case IMAGE_REL_ARM_BLX23T:    applyBranch24T(off, sx - p - 4); break;
-  case IMAGE_REL_ARM_SECTION:   applySecIdx(off, os); break;
+  case IMAGE_REL_ARM_SECTION:
+    applySecIdx(off, os, file->ctx.outputSections.size());
+    break;
   case IMAGE_REL_ARM_SECREL:    applySecRel(this, off, os, s); break;
   case IMAGE_REL_ARM_REL32:     add32(off, sx - p - 4); break;
   default:
@@ -298,7 +323,8 @@ static void applyArm64Branch14(uint8_t *off, int64_t v) {
 }
 
 void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os,
-                                 uint64_t s, uint64_t p) const {
+                                 uint64_t s, uint64_t p,
+                                 uint64_t imageBase) const {
   switch (type) {
   case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(off, s, p, 12); break;
   case IMAGE_REL_ARM64_REL21:          applyArm64Addr(off, s, p, 0); break;
@@ -307,14 +333,20 @@ void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os,
   case IMAGE_REL_ARM64_BRANCH26:       applyArm64Branch26(off, s - p); break;
   case IMAGE_REL_ARM64_BRANCH19:       applyArm64Branch19(off, s - p); break;
   case IMAGE_REL_ARM64_BRANCH14:       applyArm64Branch14(off, s - p); break;
-  case IMAGE_REL_ARM64_ADDR32:         add32(off, s + config->imageBase); break;
+  case IMAGE_REL_ARM64_ADDR32:
+    add32(off, s + imageBase);
+    break;
   case IMAGE_REL_ARM64_ADDR32NB:       add32(off, s); break;
-  case IMAGE_REL_ARM64_ADDR64:         add64(off, s + config->imageBase); break;
+  case IMAGE_REL_ARM64_ADDR64:
+    add64(off, s + imageBase);
+    break;
   case IMAGE_REL_ARM64_SECREL:         applySecRel(this, off, os, s); break;
   case IMAGE_REL_ARM64_SECREL_LOW12A:  applySecRelLow12A(this, off, os, s); break;
   case IMAGE_REL_ARM64_SECREL_HIGH12A: applySecRelHigh12A(this, off, os, s); break;
   case IMAGE_REL_ARM64_SECREL_LOW12L:  applySecRelLdr(this, off, os, s); break;
-  case IMAGE_REL_ARM64_SECTION:        applySecIdx(off, os); break;
+  case IMAGE_REL_ARM64_SECTION:
+    applySecIdx(off, os, file->ctx.outputSections.size());
+    break;
   case IMAGE_REL_ARM64_REL32:          add32(off, s - p - 4); break;
   default:
     error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
@@ -324,12 +356,13 @@ void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os,
 
 static void maybeReportRelocationToDiscarded(const SectionChunk *fromChunk,
                                              Defined *sym,
-                                             const coff_relocation &rel) {
+                                             const coff_relocation &rel,
+                                             bool isMinGW) {
   // Don't report these errors when the relocation comes from a debug info
   // section or in mingw mode. MinGW mode object files (built by GCC) can
   // have leftover sections with relocations against discarded comdat
   // sections. Such sections are left as is, with relocations untouched.
-  if (fromChunk->isCodeView() || fromChunk->isDWARF() || config->mingw)
+  if (fromChunk->isCodeView() || fromChunk->isDWARF() || isMinGW)
     return;
 
   // Get the name of the symbol. If it's null, it was discarded early, so we
@@ -395,7 +428,7 @@ void SectionChunk::applyRelocation(uint8_t *off,
   // it was an absolute or synthetic symbol.
   if (!sym ||
       (!os && !isa<DefinedAbsolute>(sym) && !isa<DefinedSynthetic>(sym))) {
-    maybeReportRelocationToDiscarded(this, sym, rel);
+    maybeReportRelocationToDiscarded(this, sym, rel, file->ctx.config.mingw);
     return;
   }
 
@@ -403,18 +436,19 @@ void SectionChunk::applyRelocation(uint8_t *off,
 
   // Compute the RVA of the relocation for relative relocations.
   uint64_t p = rva + rel.VirtualAddress;
-  switch (config->machine) {
+  uint64_t imageBase = file->ctx.config.imageBase;
+  switch (file->ctx.config.machine) {
   case AMD64:
-    applyRelX64(off, rel.Type, os, s, p);
+    applyRelX64(off, rel.Type, os, s, p, imageBase);
     break;
   case I386:
-    applyRelX86(off, rel.Type, os, s, p);
+    applyRelX86(off, rel.Type, os, s, p, imageBase);
     break;
   case ARMNT:
-    applyRelARM(off, rel.Type, os, s, p);
+    applyRelARM(off, rel.Type, os, s, p, imageBase);
     break;
   case ARM64:
-    applyRelARM64(off, rel.Type, os, s, p);
+    applyRelARM64(off, rel.Type, os, s, p, imageBase);
     break;
   default:
     llvm_unreachable("unknown machine type");
@@ -479,8 +513,9 @@ void SectionChunk::addAssociative(SectionChunk *child) {
   child->assocChildren = next;
 }
 
-static uint8_t getBaserelType(const coff_relocation &rel) {
-  switch (config->machine) {
+static uint8_t getBaserelType(const coff_relocation &rel,
+                              llvm::COFF::MachineTypes machine) {
+  switch (machine) {
   case AMD64:
     if (rel.Type == IMAGE_REL_AMD64_ADDR64)
       return IMAGE_REL_BASED_DIR64;
@@ -512,7 +547,7 @@ static uint8_t getBaserelType(const coff_relocation &rel) {
 // Only called when base relocation is enabled.
 void SectionChunk::getBaserels(std::vector<Baserel> *res) {
   for (const coff_relocation &rel : getRelocs()) {
-    uint8_t ty = getBaserelType(rel);
+    uint8_t ty = getBaserelType(rel, file->ctx.config.machine);
     if (ty == IMAGE_REL_BASED_ABSOLUTE)
       continue;
     Symbol *target = file->getSymbol(rel.SymbolTableIndex);
@@ -529,7 +564,8 @@ void SectionChunk::getBaserels(std::vector<Baserel> *res) {
 // another DLL) This returns the size the relocation is supposed to update,
 // in bits, or 0 if the relocation cannot be handled as a runtime pseudo
 // relocation.
-static int getRuntimePseudoRelocSize(uint16_t type) {
+static int getRuntimePseudoRelocSize(uint16_t type,
+                                     llvm::COFF::MachineTypes machine) {
   // Relocations that either contain an absolute address, or a plain
   // relative offset, since the runtime pseudo reloc implementation
   // adds 8/16/32/64 bit values to a memory address.
@@ -555,7 +591,7 @@ static int getRuntimePseudoRelocSize(uint16_t type) {
   // the image, or temporarily changed at runtime with VirtualProtect.
   // Since this only operates on direct address values, it doesn't work for
   // ARM/ARM64 relocations, other than the plain ADDR32/ADDR64 relocations.
-  switch (config->machine) {
+  switch (machine) {
   case AMD64:
     switch (type) {
     case IMAGE_REL_AMD64_ADDR64:
@@ -612,7 +648,8 @@ void SectionChunk::getRuntimePseudoRelocs(
         dyn_cast_or_null<Defined>(file->getSymbol(rel.SymbolTableIndex));
     if (!target || !target->isRuntimePseudoReloc)
       continue;
-    int sizeInBits = getRuntimePseudoRelocSize(rel.Type);
+    int sizeInBits =
+        getRuntimePseudoRelocSize(rel.Type, file->ctx.config.machine);
     if (sizeInBits == 0) {
       error("unable to automatically import from " + target->getName() +
             " with relocation type " +
@@ -718,7 +755,8 @@ void StringChunk::writeTo(uint8_t *buf) const {
   buf[str.size()] = '\0';
 }
 
-ImportThunkChunkX64::ImportThunkChunkX64(Defined *s) : ImportThunkChunk(s) {
+ImportThunkChunkX64::ImportThunkChunkX64(COFFLinkerContext &ctx, Defined *s)
+    : ImportThunkChunk(ctx, s) {
   // Intel Optimization Manual says that all branch targets
   // should be 16-byte aligned. MSVC linker does this too.
   setAlignment(16);
@@ -731,14 +769,13 @@ void ImportThunkChunkX64::writeTo(uint8_t *buf) const {
 }
 
 void ImportThunkChunkX86::getBaserels(std::vector<Baserel> *res) {
-  res->emplace_back(getRVA() + 2);
+  res->emplace_back(getRVA() + 2, ctx.config.machine);
 }
 
 void ImportThunkChunkX86::writeTo(uint8_t *buf) const {
   memcpy(buf, importThunkX86, sizeof(importThunkX86));
   // The first two bytes is a JMP instruction. Fill its operand.
-  write32le(buf + 2,
-            impSymbol->getRVA() + config->imageBase);
+  write32le(buf + 2, impSymbol->getRVA() + ctx.config.imageBase);
 }
 
 void ImportThunkChunkARM::getBaserels(std::vector<Baserel> *res) {
@@ -748,7 +785,7 @@ void ImportThunkChunkARM::getBaserels(std::vector<Baserel> *res) {
 void ImportThunkChunkARM::writeTo(uint8_t *buf) const {
   memcpy(buf, importThunkARM, sizeof(importThunkARM));
   // Fix mov.w and mov.t operands.
-  applyMOV32T(buf, impSymbol->getRVA() + config->imageBase);
+  applyMOV32T(buf, impSymbol->getRVA() + ctx.config.imageBase);
 }
 
 void ImportThunkChunkARM64::writeTo(uint8_t *buf) const {
@@ -766,12 +803,12 @@ const uint8_t armThunk[] = {
 };
 
 size_t RangeExtensionThunkARM::getSize() const {
-  assert(config->machine == ARMNT);
+  assert(ctx.config.machine == ARMNT);
   return sizeof(armThunk);
 }
 
 void RangeExtensionThunkARM::writeTo(uint8_t *buf) const {
-  assert(config->machine == ARMNT);
+  assert(ctx.config.machine == ARMNT);
   uint64_t offset = target->getRVA() - rva - 12;
   memcpy(buf, armThunk, sizeof(armThunk));
   applyMOV32T(buf, uint32_t(offset));
@@ -786,28 +823,33 @@ const uint8_t arm64Thunk[] = {
 };
 
 size_t RangeExtensionThunkARM64::getSize() const {
-  assert(config->machine == ARM64);
+  assert(ctx.config.machine == ARM64);
   return sizeof(arm64Thunk);
 }
 
 void RangeExtensionThunkARM64::writeTo(uint8_t *buf) const {
-  assert(config->machine == ARM64);
+  assert(ctx.config.machine == ARM64);
   memcpy(buf, arm64Thunk, sizeof(arm64Thunk));
   applyArm64Addr(buf + 0, target->getRVA(), rva, 12);
   applyArm64Imm(buf + 4, target->getRVA() & 0xfff, 0);
 }
 
+LocalImportChunk::LocalImportChunk(COFFLinkerContext &c, Defined *s)
+    : sym(s), ctx(c) {
+  setAlignment(ctx.config.wordsize);
+}
+
 void LocalImportChunk::getBaserels(std::vector<Baserel> *res) {
-  res->emplace_back(getRVA());
+  res->emplace_back(getRVA(), ctx.config.machine);
 }
 
-size_t LocalImportChunk::getSize() const { return config->wordsize; }
+size_t LocalImportChunk::getSize() const { return ctx.config.wordsize; }
 
 void LocalImportChunk::writeTo(uint8_t *buf) const {
-  if (config->is64()) {
-    write64le(buf, sym->getRVA() + config->imageBase);
+  if (ctx.config.is64()) {
+    write64le(buf, sym->getRVA() + ctx.config.imageBase);
   } else {
-    write32le(buf, sym->getRVA() + config->imageBase);
+    write32le(buf, sym->getRVA() + ctx.config.imageBase);
   }
 }
 
@@ -927,8 +969,8 @@ void BaserelChunk::writeTo(uint8_t *buf) const {
   memcpy(buf, data.data(), data.size());
 }
 
-uint8_t Baserel::getDefaultType() {
-  switch (config->machine) {
+uint8_t Baserel::getDefaultType(llvm::COFF::MachineTypes machine) {
+  switch (machine) {
   case AMD64:
   case ARM64:
     return IMAGE_REL_BASED_DIR64;
@@ -986,10 +1028,10 @@ void MergeChunk::writeTo(uint8_t *buf) const {
 }
 
 // MinGW specific.
-size_t AbsolutePointerChunk::getSize() const { return config->wordsize; }
+size_t AbsolutePointerChunk::getSize() const { return ctx.config.wordsize; }
 
 void AbsolutePointerChunk::writeTo(uint8_t *buf) const {
-  if (config->is64()) {
+  if (ctx.config.is64()) {
     write64le(buf, value);
   } else {
     write32le(buf, value);

diff  --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index ba2f0d43b726..e08531ed4066 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -238,13 +238,13 @@ class SectionChunk final : public Chunk {
   bool isCOMDAT() const;
   void applyRelocation(uint8_t *off, const coff_relocation &rel) const;
   void applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
-                   uint64_t p) const;
+                   uint64_t p, uint64_t imageBase) const;
   void applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
-                   uint64_t p) const;
+                   uint64_t p, uint64_t imageBase) const;
   void applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
-                   uint64_t p) const;
+                   uint64_t p, uint64_t imageBase) const;
   void applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
-                     uint64_t p) const;
+                     uint64_t p, uint64_t imageBase) const;
 
   void getRuntimePseudoRelocs(std::vector<RuntimePseudoReloc> &res);
 
@@ -490,24 +490,26 @@ static const uint8_t importThunkARM64[] = {
 // contents will be a JMP instruction to some __imp_ symbol.
 class ImportThunkChunk : public NonSectionChunk {
 public:
-  ImportThunkChunk(Defined *s)
-      : NonSectionChunk(ImportThunkKind), impSymbol(s) {}
+  ImportThunkChunk(COFFLinkerContext &ctx, Defined *s)
+      : NonSectionChunk(ImportThunkKind), impSymbol(s), ctx(ctx) {}
   static bool classof(const Chunk *c) { return c->kind() == ImportThunkKind; }
 
 protected:
   Defined *impSymbol;
+  COFFLinkerContext &ctx;
 };
 
 class ImportThunkChunkX64 : public ImportThunkChunk {
 public:
-  explicit ImportThunkChunkX64(Defined *s);
+  explicit ImportThunkChunkX64(COFFLinkerContext &ctx, Defined *s);
   size_t getSize() const override { return sizeof(importThunkX86); }
   void writeTo(uint8_t *buf) const override;
 };
 
 class ImportThunkChunkX86 : public ImportThunkChunk {
 public:
-  explicit ImportThunkChunkX86(Defined *s) : ImportThunkChunk(s) {}
+  explicit ImportThunkChunkX86(COFFLinkerContext &ctx, Defined *s)
+      : ImportThunkChunk(ctx, s) {}
   size_t getSize() const override { return sizeof(importThunkX86); }
   void getBaserels(std::vector<Baserel> *res) override;
   void writeTo(uint8_t *buf) const override;
@@ -515,7 +517,8 @@ class ImportThunkChunkX86 : public ImportThunkChunk {
 
 class ImportThunkChunkARM : public ImportThunkChunk {
 public:
-  explicit ImportThunkChunkARM(Defined *s) : ImportThunkChunk(s) {
+  explicit ImportThunkChunkARM(COFFLinkerContext &ctx, Defined *s)
+      : ImportThunkChunk(ctx, s) {
     setAlignment(2);
   }
   size_t getSize() const override { return sizeof(importThunkARM); }
@@ -525,7 +528,8 @@ class ImportThunkChunkARM : public ImportThunkChunk {
 
 class ImportThunkChunkARM64 : public ImportThunkChunk {
 public:
-  explicit ImportThunkChunkARM64(Defined *s) : ImportThunkChunk(s) {
+  explicit ImportThunkChunkARM64(COFFLinkerContext &ctx, Defined *s)
+      : ImportThunkChunk(ctx, s) {
     setAlignment(4);
   }
   size_t getSize() const override { return sizeof(importThunkARM64); }
@@ -534,35 +538,46 @@ class ImportThunkChunkARM64 : public ImportThunkChunk {
 
 class RangeExtensionThunkARM : public NonSectionChunk {
 public:
-  explicit RangeExtensionThunkARM(Defined *t) : target(t) { setAlignment(2); }
+  explicit RangeExtensionThunkARM(COFFLinkerContext &ctx, Defined *t)
+      : target(t), ctx(ctx) {
+    setAlignment(2);
+  }
   size_t getSize() const override;
   void writeTo(uint8_t *buf) const override;
 
   Defined *target;
+
+private:
+  COFFLinkerContext &ctx;
 };
 
 class RangeExtensionThunkARM64 : public NonSectionChunk {
 public:
-  explicit RangeExtensionThunkARM64(Defined *t) : target(t) { setAlignment(4); }
+  explicit RangeExtensionThunkARM64(COFFLinkerContext &ctx, Defined *t)
+      : target(t), ctx(ctx) {
+    setAlignment(4);
+  }
   size_t getSize() const override;
   void writeTo(uint8_t *buf) const override;
 
   Defined *target;
+
+private:
+  COFFLinkerContext &ctx;
 };
 
 // Windows-specific.
 // See comments for DefinedLocalImport class.
 class LocalImportChunk : public NonSectionChunk {
 public:
-  explicit LocalImportChunk(Defined *s) : sym(s) {
-    setAlignment(config->wordsize);
-  }
+  explicit LocalImportChunk(COFFLinkerContext &ctx, Defined *s);
   size_t getSize() const override;
   void getBaserels(std::vector<Baserel> *res) override;
   void writeTo(uint8_t *buf) const override;
 
 private:
   Defined *sym;
+  COFFLinkerContext &ctx;
 };
 
 // Duplicate RVAs are not allowed in RVA tables, so unique symbols by chunk and
@@ -629,8 +644,9 @@ class BaserelChunk : public NonSectionChunk {
 class Baserel {
 public:
   Baserel(uint32_t v, uint8_t ty) : rva(v), type(ty) {}
-  explicit Baserel(uint32_t v) : Baserel(v, getDefaultType()) {}
-  uint8_t getDefaultType();
+  explicit Baserel(uint32_t v, llvm::COFF::MachineTypes machine)
+      : Baserel(v, getDefaultType(machine)) {}
+  uint8_t getDefaultType(llvm::COFF::MachineTypes machine);
 
   uint32_t rva;
   uint8_t type;
@@ -669,7 +685,8 @@ class PseudoRelocTableChunk : public NonSectionChunk {
 // MinGW specific. A Chunk that contains one pointer-sized absolute value.
 class AbsolutePointerChunk : public NonSectionChunk {
 public:
-  AbsolutePointerChunk(uint64_t value) : value(value) {
+  AbsolutePointerChunk(COFFLinkerContext &ctx, uint64_t value)
+      : value(value), ctx(ctx) {
     setAlignment(getSize());
   }
   size_t getSize() const override;
@@ -677,6 +694,7 @@ class AbsolutePointerChunk : public NonSectionChunk {
 
 private:
   uint64_t value;
+  COFFLinkerContext &ctx;
 };
 
 // Return true if this file has the hotpatch flag set to true in the S_COMPILE3
@@ -697,6 +715,19 @@ void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift);
 void applyArm64Imm(uint8_t *off, uint64_t imm, uint32_t rangeLimit);
 void applyArm64Branch26(uint8_t *off, int64_t v);
 
+// Convenience class for initializing a SectionChunk with specific flags.
+class FakeSectionChunk {
+public:
+  FakeSectionChunk(const coff_section *section) : chunk(nullptr, section) {
+    // Comdats from LTO files can't be fully treated as regular comdats
+    // at this point; we don't know what size or contents they are going to
+    // have, so we can't do proper checking of such aspects of them.
+    chunk.selection = llvm::COFF::IMAGE_COMDAT_SELECT_ANY;
+  }
+
+  SectionChunk chunk;
+};
+
 } // namespace lld::coff
 
 namespace llvm {

diff  --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index c7f10f253b8a..4711573a5b76 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -96,7 +96,7 @@ enum class ICFLevel {
 // Global configuration.
 struct Configuration {
   enum ManifestKind { Default, SideBySide, Embed, No };
-  bool is64() { return machine == AMD64 || machine == ARM64; }
+  bool is64() const { return machine == AMD64 || machine == ARM64; }
 
   llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN;
   size_t wordsize;
@@ -292,8 +292,6 @@ struct Configuration {
   bool writeCheckSum = false;
 };
 
-extern std::unique_ptr<Configuration> config;
-
 } // namespace lld::coff
 
 #endif

diff  --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp
index 58c3eff4a72c..b6e91ea1c197 100644
--- a/lld/COFF/DLL.cpp
+++ b/lld/COFF/DLL.cpp
@@ -61,19 +61,23 @@ class HintNameChunk : public NonSectionChunk {
 // A chunk for the import descriptor table.
 class LookupChunk : public NonSectionChunk {
 public:
-  explicit LookupChunk(Chunk *c) : hintName(c) {
-    setAlignment(config->wordsize);
+  explicit LookupChunk(COFFLinkerContext &ctx, Chunk *c)
+      : hintName(c), ctx(ctx) {
+    setAlignment(ctx.config.wordsize);
   }
-  size_t getSize() const override { return config->wordsize; }
+  size_t getSize() const override { return ctx.config.wordsize; }
 
   void writeTo(uint8_t *buf) const override {
-    if (config->is64())
+    if (ctx.config.is64())
       write64le(buf, hintName->getRVA());
     else
       write32le(buf, hintName->getRVA());
   }
 
   Chunk *hintName;
+
+private:
+  COFFLinkerContext &ctx;
 };
 
 // A chunk for the import descriptor table.
@@ -81,15 +85,16 @@ class LookupChunk : public NonSectionChunk {
 // See Microsoft PE/COFF spec 7.1. Import Header for details.
 class OrdinalOnlyChunk : public NonSectionChunk {
 public:
-  explicit OrdinalOnlyChunk(uint16_t v) : ordinal(v) {
-    setAlignment(config->wordsize);
+  explicit OrdinalOnlyChunk(COFFLinkerContext &c, uint16_t v)
+      : ordinal(v), ctx(c) {
+    setAlignment(ctx.config.wordsize);
   }
-  size_t getSize() const override { return config->wordsize; }
+  size_t getSize() const override { return ctx.config.wordsize; }
 
   void writeTo(uint8_t *buf) const override {
     // An import-by-ordinal slot has MSB 1 to indicate that
     // this is import-by-ordinal (and not import-by-name).
-    if (config->is64()) {
+    if (ctx.config.is64()) {
       write64le(buf, (1ULL << 63) | ordinal);
     } else {
       write32le(buf, (1ULL << 31) | ordinal);
@@ -97,6 +102,9 @@ class OrdinalOnlyChunk : public NonSectionChunk {
   }
 
   uint16_t ordinal;
+
+private:
+  COFFLinkerContext &ctx;
 };
 
 // A chunk for the import descriptor table.
@@ -135,14 +143,15 @@ class NullChunk : public NonSectionChunk {
 };
 
 static std::vector<std::vector<DefinedImportData *>>
-binImports(const std::vector<DefinedImportData *> &imports) {
+binImports(COFFLinkerContext &ctx,
+           const std::vector<DefinedImportData *> &imports) {
   // Group DLL-imported symbols by DLL name because that's how
   // symbols are laid out in the import descriptor table.
-  auto less = [](const std::string &a, const std::string &b) {
-    return config->dllOrder[a] < config->dllOrder[b];
+  auto less = [&ctx](const std::string &a, const std::string &b) {
+    return ctx.config.dllOrder[a] < ctx.config.dllOrder[b];
   };
-  std::map<std::string, std::vector<DefinedImportData *>,
-           bool(*)(const std::string &, const std::string &)> m(less);
+  std::map<std::string, std::vector<DefinedImportData *>, decltype(less)> m(
+      less);
   for (DefinedImportData *sym : imports)
     m[sym->getDLLName().lower()].push_back(sym);
 
@@ -325,47 +334,56 @@ class TailMergeChunkX64 : public NonSectionChunk {
 
 class ThunkChunkX86 : public NonSectionChunk {
 public:
-  ThunkChunkX86(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
+  ThunkChunkX86(COFFLinkerContext &ctx, Defined *i, Chunk *tm)
+      : imp(i), tailMerge(tm), ctx(ctx) {}
 
   size_t getSize() const override { return sizeof(thunkX86); }
 
   void writeTo(uint8_t *buf) const override {
     memcpy(buf, thunkX86, sizeof(thunkX86));
-    write32le(buf + 1, imp->getRVA() + config->imageBase);
+    write32le(buf + 1, imp->getRVA() + ctx.config.imageBase);
     write32le(buf + 6, tailMerge->getRVA() - rva - 10);
   }
 
   void getBaserels(std::vector<Baserel> *res) override {
-    res->emplace_back(rva + 1);
+    res->emplace_back(rva + 1, ctx.config.machine);
   }
 
   Defined *imp = nullptr;
   Chunk *tailMerge = nullptr;
+
+private:
+  const COFFLinkerContext &ctx;
 };
 
 class TailMergeChunkX86 : public NonSectionChunk {
 public:
-  TailMergeChunkX86(Chunk *d, Defined *h) : desc(d), helper(h) {}
+  TailMergeChunkX86(COFFLinkerContext &ctx, Chunk *d, Defined *h)
+      : desc(d), helper(h), ctx(ctx) {}
 
   size_t getSize() const override { return sizeof(tailMergeX86); }
 
   void writeTo(uint8_t *buf) const override {
     memcpy(buf, tailMergeX86, sizeof(tailMergeX86));
-    write32le(buf + 4, desc->getRVA() + config->imageBase);
+    write32le(buf + 4, desc->getRVA() + ctx.config.imageBase);
     write32le(buf + 9, helper->getRVA() - rva - 13);
   }
 
   void getBaserels(std::vector<Baserel> *res) override {
-    res->emplace_back(rva + 4);
+    res->emplace_back(rva + 4, ctx.config.machine);
   }
 
   Chunk *desc = nullptr;
   Defined *helper = nullptr;
+
+private:
+  const COFFLinkerContext &ctx;
 };
 
 class ThunkChunkARM : public NonSectionChunk {
 public:
-  ThunkChunkARM(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {
+  ThunkChunkARM(COFFLinkerContext &ctx, Defined *i, Chunk *tm)
+      : imp(i), tailMerge(tm), ctx(ctx) {
     setAlignment(2);
   }
 
@@ -373,7 +391,7 @@ class ThunkChunkARM : public NonSectionChunk {
 
   void writeTo(uint8_t *buf) const override {
     memcpy(buf, thunkARM, sizeof(thunkARM));
-    applyMOV32T(buf + 0, imp->getRVA() + config->imageBase);
+    applyMOV32T(buf + 0, imp->getRVA() + ctx.config.imageBase);
     applyBranch24T(buf + 8, tailMerge->getRVA() - rva - 12);
   }
 
@@ -383,11 +401,15 @@ class ThunkChunkARM : public NonSectionChunk {
 
   Defined *imp = nullptr;
   Chunk *tailMerge = nullptr;
+
+private:
+  const COFFLinkerContext &ctx;
 };
 
 class TailMergeChunkARM : public NonSectionChunk {
 public:
-  TailMergeChunkARM(Chunk *d, Defined *h) : desc(d), helper(h) {
+  TailMergeChunkARM(COFFLinkerContext &ctx, Chunk *d, Defined *h)
+      : desc(d), helper(h), ctx(ctx) {
     setAlignment(2);
   }
 
@@ -395,7 +417,7 @@ class TailMergeChunkARM : public NonSectionChunk {
 
   void writeTo(uint8_t *buf) const override {
     memcpy(buf, tailMergeARM, sizeof(tailMergeARM));
-    applyMOV32T(buf + 14, desc->getRVA() + config->imageBase);
+    applyMOV32T(buf + 14, desc->getRVA() + ctx.config.imageBase);
     applyBranch24T(buf + 22, helper->getRVA() - rva - 26);
   }
 
@@ -405,6 +427,9 @@ class TailMergeChunkARM : public NonSectionChunk {
 
   Chunk *desc = nullptr;
   Defined *helper = nullptr;
+
+private:
+  const COFFLinkerContext &ctx;
 };
 
 class ThunkChunkARM64 : public NonSectionChunk {
@@ -448,28 +473,32 @@ class TailMergeChunkARM64 : public NonSectionChunk {
 // A chunk for the import descriptor table.
 class DelayAddressChunk : public NonSectionChunk {
 public:
-  explicit DelayAddressChunk(Chunk *c) : thunk(c) {
-    setAlignment(config->wordsize);
+  explicit DelayAddressChunk(COFFLinkerContext &ctx, Chunk *c)
+      : thunk(c), ctx(ctx) {
+    setAlignment(ctx.config.wordsize);
   }
-  size_t getSize() const override { return config->wordsize; }
+  size_t getSize() const override { return ctx.config.wordsize; }
 
   void writeTo(uint8_t *buf) const override {
-    if (config->is64()) {
-      write64le(buf, thunk->getRVA() + config->imageBase);
+    if (ctx.config.is64()) {
+      write64le(buf, thunk->getRVA() + ctx.config.imageBase);
     } else {
       uint32_t bit = 0;
       // Pointer to thumb code must have the LSB set, so adjust it.
-      if (config->machine == ARMNT)
+      if (ctx.config.machine == ARMNT)
         bit = 1;
-      write32le(buf, (thunk->getRVA() + config->imageBase) | bit);
+      write32le(buf, (thunk->getRVA() + ctx.config.imageBase) | bit);
     }
   }
 
   void getBaserels(std::vector<Baserel> *res) override {
-    res->emplace_back(rva);
+    res->emplace_back(rva, ctx.config.machine);
   }
 
   Chunk *thunk;
+
+private:
+  const COFFLinkerContext &ctx;
 };
 
 // Export table
@@ -509,19 +538,20 @@ class ExportDirectoryChunk : public NonSectionChunk {
 
 class AddressTableChunk : public NonSectionChunk {
 public:
-  explicit AddressTableChunk(size_t maxOrdinal) : size(maxOrdinal) {}
+  explicit AddressTableChunk(COFFLinkerContext &ctx, size_t maxOrdinal)
+      : size(maxOrdinal), ctx(ctx) {}
   size_t getSize() const override { return size * 4; }
 
   void writeTo(uint8_t *buf) const override {
     memset(buf, 0, getSize());
 
-    for (const Export &e : config->exports) {
+    for (const Export &e : ctx.config.exports) {
       assert(e.ordinal != 0 && "Export symbol has invalid ordinal");
       // OrdinalBase is 1, so subtract 1 to get the index.
       uint8_t *p = buf + (e.ordinal - 1) * 4;
       uint32_t bit = 0;
       // Pointer to thumb code must have the LSB set, so adjust it.
-      if (config->machine == ARMNT && !e.data)
+      if (ctx.config.machine == ARMNT && !e.data)
         bit = 1;
       if (e.forwardChunk) {
         write32le(p, e.forwardChunk->getRVA() | bit);
@@ -535,6 +565,7 @@ class AddressTableChunk : public NonSectionChunk {
 
 private:
   size_t size;
+  const COFFLinkerContext &ctx;
 };
 
 class NamePointersChunk : public NonSectionChunk {
@@ -555,11 +586,12 @@ class NamePointersChunk : public NonSectionChunk {
 
 class ExportOrdinalChunk : public NonSectionChunk {
 public:
-  explicit ExportOrdinalChunk(size_t i) : size(i) {}
+  explicit ExportOrdinalChunk(const COFFLinkerContext &ctx, size_t i)
+      : size(i), ctx(ctx) {}
   size_t getSize() const override { return size * 2; }
 
   void writeTo(uint8_t *buf) const override {
-    for (Export &e : config->exports) {
+    for (const Export &e : ctx.config.exports) {
       if (e.noname)
         continue;
       assert(e.ordinal != 0 && "Export symbol has invalid ordinal");
@@ -571,12 +603,13 @@ class ExportOrdinalChunk : public NonSectionChunk {
 
 private:
   size_t size;
+  const COFFLinkerContext &ctx;
 };
 
 } // anonymous namespace
 
-void IdataContents::create() {
-  std::vector<std::vector<DefinedImportData *>> v = binImports(imports);
+void IdataContents::create(COFFLinkerContext &ctx) {
+  std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);
 
   // Create .idata contents for each DLL.
   for (std::vector<DefinedImportData *> &syms : v) {
@@ -588,18 +621,18 @@ void IdataContents::create() {
     for (DefinedImportData *s : syms) {
       uint16_t ord = s->getOrdinal();
       if (s->getExternalName().empty()) {
-        lookups.push_back(make<OrdinalOnlyChunk>(ord));
-        addresses.push_back(make<OrdinalOnlyChunk>(ord));
+        lookups.push_back(make<OrdinalOnlyChunk>(ctx, ord));
+        addresses.push_back(make<OrdinalOnlyChunk>(ctx, ord));
         continue;
       }
       auto *c = make<HintNameChunk>(s->getExternalName(), ord);
-      lookups.push_back(make<LookupChunk>(c));
-      addresses.push_back(make<LookupChunk>(c));
+      lookups.push_back(make<LookupChunk>(ctx, c));
+      addresses.push_back(make<LookupChunk>(ctx, c));
       hints.push_back(c);
     }
     // Terminate with null values.
-    lookups.push_back(make<NullChunk>(config->wordsize));
-    addresses.push_back(make<NullChunk>(config->wordsize));
+    lookups.push_back(make<NullChunk>(ctx.config.wordsize));
+    addresses.push_back(make<NullChunk>(ctx.config.wordsize));
 
     for (int i = 0, e = syms.size(); i < e; ++i)
       syms[i]->setLocation(addresses[base + i]);
@@ -635,9 +668,9 @@ uint64_t DelayLoadContents::getDirSize() {
   return dirs.size() * sizeof(delay_import_directory_table_entry);
 }
 
-void DelayLoadContents::create(COFFLinkerContext &ctx, Defined *h) {
+void DelayLoadContents::create(Defined *h) {
   helper = h;
-  std::vector<std::vector<DefinedImportData *>> v = binImports(imports);
+  std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);
 
   // Create .didat contents for each DLL.
   for (std::vector<DefinedImportData *> &syms : v) {
@@ -649,15 +682,15 @@ void DelayLoadContents::create(COFFLinkerContext &ctx, Defined *h) {
     Chunk *tm = newTailMergeChunk(dir);
     for (DefinedImportData *s : syms) {
       Chunk *t = newThunkChunk(s, tm);
-      auto *a = make<DelayAddressChunk>(t);
+      auto *a = make<DelayAddressChunk>(ctx, t);
       addresses.push_back(a);
       thunks.push_back(t);
       StringRef extName = s->getExternalName();
       if (extName.empty()) {
-        names.push_back(make<OrdinalOnlyChunk>(s->getOrdinal()));
+        names.push_back(make<OrdinalOnlyChunk>(ctx, s->getOrdinal()));
       } else {
         auto *c = make<HintNameChunk>(extName, 0);
-        names.push_back(make<LookupChunk>(c));
+        names.push_back(make<LookupChunk>(ctx, c));
         hintNames.push_back(c);
         // Add a syntentic symbol for this load thunk, using the "__imp___load"
         // prefix, in case this thunk needs to be added to the list of valid
@@ -692,13 +725,13 @@ void DelayLoadContents::create(COFFLinkerContext &ctx, Defined *h) {
 }
 
 Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
-  switch (config->machine) {
+  switch (ctx.config.machine) {
   case AMD64:
     return make<TailMergeChunkX64>(dir, helper);
   case I386:
-    return make<TailMergeChunkX86>(dir, helper);
+    return make<TailMergeChunkX86>(ctx, dir, helper);
   case ARMNT:
-    return make<TailMergeChunkARM>(dir, helper);
+    return make<TailMergeChunkARM>(ctx, dir, helper);
   case ARM64:
     return make<TailMergeChunkARM64>(dir, helper);
   default:
@@ -708,13 +741,13 @@ Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
 
 Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,
                                         Chunk *tailMerge) {
-  switch (config->machine) {
+  switch (ctx.config.machine) {
   case AMD64:
     return make<ThunkChunkX64>(s, tailMerge);
   case I386:
-    return make<ThunkChunkX86>(s, tailMerge);
+    return make<ThunkChunkX86>(ctx, s, tailMerge);
   case ARMNT:
-    return make<ThunkChunkARM>(s, tailMerge);
+    return make<ThunkChunkARM>(ctx, s, tailMerge);
   case ARM64:
     return make<ThunkChunkARM64>(s, tailMerge);
   default:
@@ -722,20 +755,20 @@ Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,
   }
 }
 
-EdataContents::EdataContents() {
+EdataContents::EdataContents(COFFLinkerContext &ctx) : ctx(ctx) {
   uint16_t maxOrdinal = 0;
-  for (Export &e : config->exports)
+  for (Export &e : ctx.config.exports)
     maxOrdinal = std::max(maxOrdinal, e.ordinal);
 
-  auto *dllName = make<StringChunk>(sys::path::filename(config->outputFile));
-  auto *addressTab = make<AddressTableChunk>(maxOrdinal);
+  auto *dllName = make<StringChunk>(sys::path::filename(ctx.config.outputFile));
+  auto *addressTab = make<AddressTableChunk>(ctx, maxOrdinal);
   std::vector<Chunk *> names;
-  for (Export &e : config->exports)
+  for (Export &e : ctx.config.exports)
     if (!e.noname)
       names.push_back(make<StringChunk>(e.exportName));
 
   std::vector<Chunk *> forwards;
-  for (Export &e : config->exports) {
+  for (Export &e : ctx.config.exports) {
     if (e.forwardTo.empty())
       continue;
     e.forwardChunk = make<StringChunk>(e.forwardTo);
@@ -743,7 +776,7 @@ EdataContents::EdataContents() {
   }
 
   auto *nameTab = make<NamePointersChunk>(names);
-  auto *ordinalTab = make<ExportOrdinalChunk>(names.size());
+  auto *ordinalTab = make<ExportOrdinalChunk>(ctx, names.size());
   auto *dir = make<ExportDirectoryChunk>(maxOrdinal, names.size(), dllName,
                                          addressTab, nameTab, ordinalTab);
   chunks.push_back(dir);

diff  --git a/lld/COFF/DLL.h b/lld/COFF/DLL.h
index a5de351897d2..f90fd575efc1 100644
--- a/lld/COFF/DLL.h
+++ b/lld/COFF/DLL.h
@@ -23,7 +23,7 @@ class IdataContents {
   void add(DefinedImportData *sym) { imports.push_back(sym); }
   bool empty() { return imports.empty(); }
 
-  void create();
+  void create(COFFLinkerContext &ctx);
 
   std::vector<DefinedImportData *> imports;
   std::vector<Chunk *> dirs;
@@ -37,9 +37,10 @@ class IdataContents {
 // DelayLoadContents creates all chunks for the delay-load DLL import table.
 class DelayLoadContents {
 public:
+  DelayLoadContents(COFFLinkerContext &ctx) : ctx(ctx) {}
   void add(DefinedImportData *sym) { imports.push_back(sym); }
   bool empty() { return imports.empty(); }
-  void create(COFFLinkerContext &ctx, Defined *helper);
+  void create(Defined *helper);
   std::vector<Chunk *> getChunks();
   std::vector<Chunk *> getDataChunks();
   ArrayRef<Chunk *> getCodeChunks() { return thunks; }
@@ -60,19 +61,23 @@ class DelayLoadContents {
   std::vector<Chunk *> hintNames;
   std::vector<Chunk *> thunks;
   std::vector<Chunk *> dllNames;
+
+  COFFLinkerContext &ctx;
 };
 
 // Windows-specific.
 // EdataContents creates all chunks for the DLL export table.
 class EdataContents {
 public:
-  EdataContents();
+  EdataContents(COFFLinkerContext &ctx);
   std::vector<Chunk *> chunks;
 
   uint64_t getRVA() { return chunks[0]->getRVA(); }
   uint64_t getSize() {
     return chunks.back()->getRVA() + chunks.back()->getSize() - getRVA();
   }
+
+  COFFLinkerContext &ctx;
 };
 
 } // namespace lld::coff

diff  --git a/lld/COFF/DebugTypes.cpp b/lld/COFF/DebugTypes.cpp
index 66b9b99199b2..811889c6d246 100644
--- a/lld/COFF/DebugTypes.cpp
+++ b/lld/COFF/DebugTypes.cpp
@@ -235,7 +235,7 @@ void TpiSource::remapRecord(MutableArrayRef<uint8_t> rec,
         reinterpret_cast<TypeIndex *>(contents.data() + ref.Offset), ref.Count);
     for (TypeIndex &ti : indices) {
       if (!remapTypeIndex(ti, ref.Kind)) {
-        if (config->verbose) {
+        if (ctx.config.verbose) {
           uint16_t kind =
               reinterpret_cast<const RecordPrefix *>(rec.data())->RecordKind;
           StringRef fname = file ? file->getName() : "<unknown PDB>";
@@ -305,7 +305,7 @@ getHashesFromDebugH(ArrayRef<uint8_t> debugH) {
 
 // Merge .debug$T for a generic object file.
 Error TpiSource::mergeDebugT(TypeMerger *m) {
-  assert(!config->debugGHashes &&
+  assert(!ctx.config.debugGHashes &&
          "use remapTpiWithGHashes when ghash is enabled");
 
   CVTypeArray types;
@@ -329,7 +329,7 @@ Error TpiSource::mergeDebugT(TypeMerger *m) {
   tpiMap = indexMapStorage;
   ipiMap = indexMapStorage;
 
-  if (config->showSummary) {
+  if (ctx.config.showSummary) {
     nbTypeRecords = indexMapStorage.size() - nbHeadIndices;
     nbTypeRecordsBytes = reader.getLength();
     // Count how many times we saw each type record in our input. This
@@ -356,7 +356,7 @@ Error TpiSource::mergeDebugT(TypeMerger *m) {
 
 // Merge types from a type server PDB.
 Error TypeServerSource::mergeDebugT(TypeMerger *m) {
-  assert(!config->debugGHashes &&
+  assert(!ctx.config.debugGHashes &&
          "use remapTpiWithGHashes when ghash is enabled");
 
   pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile();
@@ -385,7 +385,7 @@ Error TypeServerSource::mergeDebugT(TypeMerger *m) {
     ipiMap = ipiSrc->indexMapStorage;
   }
 
-  if (config->showSummary) {
+  if (ctx.config.showSummary) {
     nbTypeRecords = tpiMap.size() + ipiMap.size();
     nbTypeRecordsBytes =
         expectedTpi->typeArray().getUnderlyingStream().getLength() +
@@ -727,14 +727,14 @@ void TpiSource::mergeUniqueTypeRecords(ArrayRef<uint8_t> typeRecords,
 }
 
 void TpiSource::remapTpiWithGHashes(GHashState *g) {
-  assert(config->debugGHashes && "ghashes must be enabled");
+  assert(ctx.config.debugGHashes && "ghashes must be enabled");
   fillMapFromGHashes(g);
   tpiMap = indexMapStorage;
   ipiMap = indexMapStorage;
   mergeUniqueTypeRecords(file->debugTypes);
   // TODO: Free all unneeded ghash resources now that we have a full index map.
 
-  if (config->showSummary) {
+  if (ctx.config.showSummary) {
     nbTypeRecords = ghashes.size();
     nbTypeRecordsBytes = file->debugTypes.size();
   }
@@ -787,7 +787,7 @@ static ArrayRef<uint8_t> typeArrayToBytes(const CVTypeArray &types) {
 
 // Merge types from a type server PDB.
 void TypeServerSource::remapTpiWithGHashes(GHashState *g) {
-  assert(config->debugGHashes && "ghashes must be enabled");
+  assert(ctx.config.debugGHashes && "ghashes must be enabled");
 
   // IPI merging depends on TPI, so do TPI first, then do IPI.  No need to
   // propagate errors, those should've been handled during ghash loading.
@@ -805,13 +805,13 @@ void TypeServerSource::remapTpiWithGHashes(GHashState *g) {
     ipiSrc->ipiMap = ipiMap;
     ipiSrc->mergeUniqueTypeRecords(typeArrayToBytes(ipi.typeArray()));
 
-    if (config->showSummary) {
+    if (ctx.config.showSummary) {
       nbTypeRecords = ipiSrc->ghashes.size();
       nbTypeRecordsBytes = ipi.typeArray().getUnderlyingStream().getLength();
     }
   }
 
-  if (config->showSummary) {
+  if (ctx.config.showSummary) {
     nbTypeRecords += ghashes.size();
     nbTypeRecordsBytes += tpi.typeArray().getUnderlyingStream().getLength();
   }
@@ -898,7 +898,7 @@ void UsePrecompSource::remapTpiWithGHashes(GHashState *g) {
   mergeUniqueTypeRecords(file->debugTypes,
                          TypeIndex(precompDependency.getStartTypeIndex() +
                                    precompDependency.getTypesCount()));
-  if (config->showSummary) {
+  if (ctx.config.showSummary) {
     nbTypeRecords = ghashes.size();
     nbTypeRecordsBytes = file->debugTypes.size();
   }

diff  --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 8c73e25d5630..0a153c8f6cb7 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -61,9 +61,6 @@ using namespace llvm::sys;
 
 namespace lld::coff {
 
-std::unique_ptr<Configuration> config;
-std::unique_ptr<LinkerDriver> driver;
-
 bool link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
           llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) {
   // This driver-specific context will be freed later by lldMain().
@@ -74,10 +71,7 @@ bool link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
   ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now"
                                  " (use /errorlimit:0 to see all errors)";
 
-  config = std::make_unique<Configuration>();
-  driver = std::make_unique<LinkerDriver>(*ctx);
-
-  driver->linkerMain(args);
+  ctx->driver.linkerMain(args);
 
   return errorCount() == 0;
 }
@@ -98,11 +92,11 @@ static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
 
 // Drop directory components and replace extension with
 // ".exe", ".dll" or ".sys".
-static std::string getOutputPath(StringRef path) {
+static std::string getOutputPath(StringRef path, bool isDll, bool isDriver) {
   StringRef ext = ".exe";
-  if (config->dll)
+  if (isDll)
     ext = ".dll";
-  else if (config->driver)
+  else if (isDriver)
     ext = ".sys";
 
   return (sys::path::stem(path) + ext).str();
@@ -146,15 +140,15 @@ static std::future<MBErrPair> createFutureForFile(std::string path) {
 }
 
 // Symbol names are mangled by prepending "_" on x86.
-static StringRef mangle(StringRef sym) {
-  assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN);
-  if (config->machine == I386)
+StringRef LinkerDriver::mangle(StringRef sym) {
+  assert(ctx.config.machine != IMAGE_FILE_MACHINE_UNKNOWN);
+  if (ctx.config.machine == I386)
     return saver().save("_" + sym);
   return sym;
 }
 
-static llvm::Triple::ArchType getArch() {
-  switch (config->machine) {
+llvm::Triple::ArchType LinkerDriver::getArch() {
+  switch (ctx.config.machine) {
   case I386:
     return llvm::Triple::ArchType::x86;
   case AMD64:
@@ -177,9 +171,9 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
   MemoryBufferRef mbref = *mb;
   make<std::unique_ptr<MemoryBuffer>>(std::move(mb)); // take ownership
 
-  if (driver->tar)
-    driver->tar->append(relativeToRoot(mbref.getBufferIdentifier()),
-                        mbref.getBuffer());
+  if (ctx.driver.tar)
+    ctx.driver.tar->append(relativeToRoot(mbref.getBufferIdentifier()),
+                           mbref.getBuffer());
   return mbref;
 }
 
@@ -223,7 +217,7 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
     error(filename + ": is not a native COFF file. Recompile without /GL");
     break;
   case file_magic::pecoff_executable:
-    if (config->mingw) {
+    if (ctx.config.mingw) {
       ctx.symtab.addFile(make<DLLFile>(ctx, mbref));
       break;
     }
@@ -254,12 +248,12 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
       // the option `/nodefaultlib` than a reference to a file in the root
       // directory.
       std::string nearest;
-      if (optTable.findNearest(pathStr, nearest) > 1)
+      if (ctx.optTable.findNearest(pathStr, nearest) > 1)
         error(msg);
       else
         error(msg + "; did you mean '" + nearest + "'");
     } else
-      driver->addBuffer(std::move(mbOrErr.first), wholeArchive, lazy);
+      ctx.driver.addBuffer(std::move(mbOrErr.first), wholeArchive, lazy);
   });
 }
 
@@ -300,8 +294,8 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
 
   auto reportBufferError = [=](Error &&e, StringRef childName) {
     fatal("could not get the buffer for the member defining symbol " +
-          toCOFFString(sym) + ": " + parentName + "(" + childName + "): " +
-          toString(std::move(e)));
+          toCOFFString(ctx, sym) + ": " + parentName + "(" + childName +
+          "): " + toString(std::move(e)));
   };
 
   if (!c.getParent()->isThin()) {
@@ -311,16 +305,16 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
       reportBufferError(mbOrErr.takeError(), check(c.getFullName()));
     MemoryBufferRef mb = mbOrErr.get();
     enqueueTask([=]() {
-      driver->addArchiveBuffer(mb, toCOFFString(sym), parentName,
-                               offsetInArchive);
+      ctx.driver.addArchiveBuffer(mb, toCOFFString(ctx, sym), parentName,
+                                  offsetInArchive);
     });
     return;
   }
 
-  std::string childName = CHECK(
-      c.getFullName(),
-      "could not get the filename for the member defining symbol " +
-      toCOFFString(sym));
+  std::string childName =
+      CHECK(c.getFullName(),
+            "could not get the filename for the member defining symbol " +
+                toCOFFString(ctx, sym));
   auto future = std::make_shared<std::future<MBErrPair>>(
       createFutureForFile(childName));
   enqueueTask([=]() {
@@ -329,14 +323,15 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
       reportBufferError(errorCodeToError(mbOrErr.second), childName);
     // Pass empty string as archive name so that the original filename is
     // used as the buffer identifier.
-    driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)),
-                             toCOFFString(sym), "", /*OffsetInArchive=*/0);
+    ctx.driver.addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)),
+                                toCOFFString(ctx, sym), "",
+                                /*OffsetInArchive=*/0);
   });
 }
 
-static bool isDecorated(StringRef sym) {
+bool LinkerDriver::isDecorated(StringRef sym) {
   return sym.startswith("@") || sym.contains("@@") || sym.startswith("?") ||
-         (!config->mingw && sym.contains('@'));
+         (!ctx.config.mingw && sym.contains('@'));
 }
 
 // Parses .drectve section contents and returns a list of files
@@ -348,7 +343,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
 
   log("Directives: " + toString(file) + ": " + s);
 
-  ArgParser parser;
+  ArgParser parser(ctx);
   // .drectve is always tokenized using Windows shell rules.
   // /EXPORT: option can appear too many times, processing in fastpath.
   ParsedDirectives directives = parser.parseDirectives(s);
@@ -362,14 +357,14 @@ void LinkerDriver::parseDirectives(InputFile *file) {
       continue;
 
     Export exp = parseExport(e);
-    if (config->machine == I386 && config->mingw) {
+    if (ctx.config.machine == I386 && ctx.config.mingw) {
       if (!isDecorated(exp.name))
         exp.name = saver().save("_" + exp.name);
       if (!exp.extName.empty() && !isDecorated(exp.extName))
         exp.extName = saver().save("_" + exp.extName);
     }
     exp.directives = true;
-    config->exports.push_back(exp);
+    ctx.config.exports.push_back(exp);
   }
 
   // Handle /include: in bulk.
@@ -398,7 +393,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
         enqueuePath(*path, false, false);
       break;
     case OPT_entry:
-      config->entry = addUndefined(mangle(arg->getValue()));
+      ctx.config.entry = addUndefined(mangle(arg->getValue()));
       break;
     case OPT_failifmismatch:
       checkFailIfMismatch(arg->getValue(), file);
@@ -407,32 +402,32 @@ void LinkerDriver::parseDirectives(InputFile *file) {
       addUndefined(arg->getValue());
       break;
     case OPT_manifestdependency:
-      config->manifestDependencies.insert(arg->getValue());
+      ctx.config.manifestDependencies.insert(arg->getValue());
       break;
     case OPT_merge:
       parseMerge(arg->getValue());
       break;
     case OPT_nodefaultlib:
-      config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower());
+      ctx.config.noDefaultLibs.insert(doFindLib(arg->getValue()).lower());
       break;
     case OPT_release:
-      config->writeCheckSum = true;
+      ctx.config.writeCheckSum = true;
       break;
     case OPT_section:
       parseSection(arg->getValue());
       break;
     case OPT_stack:
-      parseNumbers(arg->getValue(), &config->stackReserve,
-                   &config->stackCommit);
+      parseNumbers(arg->getValue(), &ctx.config.stackReserve,
+                   &ctx.config.stackCommit);
       break;
     case OPT_subsystem: {
       bool gotVersion = false;
-      parseSubsystem(arg->getValue(), &config->subsystem,
-                     &config->majorSubsystemVersion,
-                     &config->minorSubsystemVersion, &gotVersion);
+      parseSubsystem(arg->getValue(), &ctx.config.subsystem,
+                     &ctx.config.majorSubsystemVersion,
+                     &ctx.config.minorSubsystemVersion, &gotVersion);
       if (gotVersion) {
-        config->majorOSVersion = config->majorSubsystemVersion;
-        config->minorOSVersion = config->minorSubsystemVersion;
+        ctx.config.majorOSVersion = ctx.config.majorSubsystemVersion;
+        ctx.config.minorOSVersion = ctx.config.minorSubsystemVersion;
       }
       break;
     }
@@ -451,9 +446,9 @@ void LinkerDriver::parseDirectives(InputFile *file) {
 // Find file from search paths. You can omit ".obj", this function takes
 // care of that. Note that the returned path is not guaranteed to exist.
 StringRef LinkerDriver::doFindFile(StringRef filename) {
-  auto getFilename = [](StringRef filename) -> StringRef {
-    if (config->vfs)
-      if (auto statOrErr = config->vfs->status(filename))
+  auto getFilename = [this](StringRef filename) -> StringRef {
+    if (ctx.config.vfs)
+      if (auto statOrErr = ctx.config.vfs->status(filename))
         return saver().save(statOrErr->getName());
     return filename;
   };
@@ -522,7 +517,7 @@ StringRef LinkerDriver::doFindLib(StringRef filename) {
   StringRef ret = doFindFile(filename);
   // For MinGW, if the find above didn't turn up anything, try
   // looking for a MinGW formatted library name.
-  if (config->mingw && ret == filename)
+  if (ctx.config.mingw && ret == filename)
     return doFindLibMinGW(filename);
   return ret;
 }
@@ -531,13 +526,13 @@ StringRef LinkerDriver::doFindLib(StringRef filename) {
 // consideration. This never returns the same path (in that case,
 // it returns std::nullopt).
 std::optional<StringRef> LinkerDriver::findLib(StringRef filename) {
-  if (config->noDefaultLibAll)
+  if (ctx.config.noDefaultLibAll)
     return std::nullopt;
   if (!visitedLibs.insert(filename.lower()).second)
     return std::nullopt;
 
   StringRef path = doFindLib(filename);
-  if (config->noDefaultLibs.count(path.lower()))
+  if (ctx.config.noDefaultLibs.count(path.lower()))
     return std::nullopt;
 
   if (std::optional<sys::fs::UniqueID> id = getUniqueID(path))
@@ -658,7 +653,7 @@ Symbol *LinkerDriver::addUndefined(StringRef name) {
   Symbol *b = ctx.symtab.addUndefined(name);
   if (!b->isGCRoot) {
     b->isGCRoot = true;
-    config->gcroot.push_back(b);
+    ctx.config.gcroot.push_back(b);
   }
   return b;
 }
@@ -687,15 +682,15 @@ StringRef LinkerDriver::mangleMaybe(Symbol *s) {
 // each of which corresponds to a user-defined "main" function. This function
 // infers an entry point from a user-defined "main" function.
 StringRef LinkerDriver::findDefaultEntry() {
-  assert(config->subsystem != IMAGE_SUBSYSTEM_UNKNOWN &&
+  assert(ctx.config.subsystem != IMAGE_SUBSYSTEM_UNKNOWN &&
          "must handle /subsystem before calling this");
 
-  if (config->mingw)
-    return mangle(config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI
+  if (ctx.config.mingw)
+    return mangle(ctx.config.subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI
                       ? "WinMainCRTStartup"
                       : "mainCRTStartup");
 
-  if (config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
+  if (ctx.config.subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
     if (findUnderscoreMangle("wWinMain")) {
       if (!findUnderscoreMangle("WinMain"))
         return mangle("wWinMainCRTStartup");
@@ -712,9 +707,9 @@ StringRef LinkerDriver::findDefaultEntry() {
 }
 
 WindowsSubsystem LinkerDriver::inferSubsystem() {
-  if (config->dll)
+  if (ctx.config.dll)
     return IMAGE_SUBSYSTEM_WINDOWS_GUI;
-  if (config->mingw)
+  if (ctx.config.mingw)
     return IMAGE_SUBSYSTEM_WINDOWS_CUI;
   // Note that link.exe infers the subsystem from the presence of these
   // functions even if /entry: or /nodefaultlib are passed which causes them
@@ -736,10 +731,10 @@ WindowsSubsystem LinkerDriver::inferSubsystem() {
   return IMAGE_SUBSYSTEM_UNKNOWN;
 }
 
-static uint64_t getDefaultImageBase() {
-  if (config->is64())
-    return config->dll ? 0x180000000 : 0x140000000;
-  return config->dll ? 0x10000000 : 0x400000;
+uint64_t LinkerDriver::getDefaultImageBase() {
+  if (ctx.config.is64())
+    return ctx.config.dll ? 0x180000000 : 0x140000000;
+  return ctx.config.dll ? 0x10000000 : 0x400000;
 }
 
 static std::string rewritePath(StringRef s) {
@@ -880,8 +875,9 @@ static unsigned parseDebugTypes(const opt::InputArgList &args) {
   return debugTypes;
 }
 
-static std::string getMapFile(const opt::InputArgList &args,
-                              opt::OptSpecifier os, opt::OptSpecifier osFile) {
+std::string LinkerDriver::getMapFile(const opt::InputArgList &args,
+                                     opt::OptSpecifier os,
+                                     opt::OptSpecifier osFile) {
   auto *arg = args.getLastArg(os, osFile);
   if (!arg)
     return "";
@@ -889,14 +885,14 @@ static std::string getMapFile(const opt::InputArgList &args,
     return arg->getValue();
 
   assert(arg->getOption().getID() == os.getID());
-  StringRef outFile = config->outputFile;
+  StringRef outFile = ctx.config.outputFile;
   return (outFile.substr(0, outFile.rfind('.')) + ".map").str();
 }
 
-static std::string getImplibPath() {
-  if (!config->implib.empty())
-    return std::string(config->implib);
-  SmallString<128> out = StringRef(config->outputFile);
+std::string LinkerDriver::getImplibPath() {
+  if (!ctx.config.implib.empty())
+    return std::string(ctx.config.implib);
+  SmallString<128> out = StringRef(ctx.config.outputFile);
   sys::path::replace_extension(out, ".lib");
   return std::string(out.str());
 }
@@ -908,26 +904,26 @@ static std::string getImplibPath() {
 //   LINK | {value}        | {value}.{.dll/.exe} | {output name}
 //    LIB | {value}        | {value}.dll         | {output name}.dll
 //
-static std::string getImportName(bool asLib) {
+std::string LinkerDriver::getImportName(bool asLib) {
   SmallString<128> out;
 
-  if (config->importName.empty()) {
-    out.assign(sys::path::filename(config->outputFile));
+  if (ctx.config.importName.empty()) {
+    out.assign(sys::path::filename(ctx.config.outputFile));
     if (asLib)
       sys::path::replace_extension(out, ".dll");
   } else {
-    out.assign(config->importName);
+    out.assign(ctx.config.importName);
     if (!sys::path::has_extension(out))
       sys::path::replace_extension(out,
-                                   (config->dll || asLib) ? ".dll" : ".exe");
+                                   (ctx.config.dll || asLib) ? ".dll" : ".exe");
   }
 
   return std::string(out.str());
 }
 
-static void createImportLibrary(bool asLib) {
+void LinkerDriver::createImportLibrary(bool asLib) {
   std::vector<COFFShortExport> exports;
-  for (Export &e1 : config->exports) {
+  for (Export &e1 : ctx.config.exports) {
     COFFShortExport e2;
     e2.Name = std::string(e1.name);
     e2.SymbolName = std::string(e1.symbolName);
@@ -944,9 +940,9 @@ static void createImportLibrary(bool asLib) {
   std::string libName = getImportName(asLib);
   std::string path = getImplibPath();
 
-  if (!config->incremental) {
-    checkError(writeImportLibrary(libName, path, exports, config->machine,
-                                  config->mingw));
+  if (!ctx.config.incremental) {
+    checkError(writeImportLibrary(libName, path, exports, ctx.config.machine,
+                                  ctx.config.mingw));
     return;
   }
 
@@ -955,8 +951,8 @@ static void createImportLibrary(bool asLib) {
   ErrorOr<std::unique_ptr<MemoryBuffer>> oldBuf = MemoryBuffer::getFile(
       path, /*IsText=*/false, /*RequiresNullTerminator=*/false);
   if (!oldBuf) {
-    checkError(writeImportLibrary(libName, path, exports, config->machine,
-                                  config->mingw));
+    checkError(writeImportLibrary(libName, path, exports, ctx.config.machine,
+                                  ctx.config.mingw));
     return;
   }
 
@@ -966,8 +962,8 @@ static void createImportLibrary(bool asLib) {
     fatal("cannot create temporary file for import library " + path + ": " +
           ec.message());
 
-  if (Error e = writeImportLibrary(libName, tmpName, exports, config->machine,
-                                   config->mingw)) {
+  if (Error e = writeImportLibrary(libName, tmpName, exports,
+                                   ctx.config.machine, ctx.config.mingw)) {
     checkError(std::move(e));
     return;
   }
@@ -982,39 +978,39 @@ static void createImportLibrary(bool asLib) {
   }
 }
 
-static void parseModuleDefs(StringRef path) {
+void LinkerDriver::parseModuleDefs(StringRef path) {
   std::unique_ptr<MemoryBuffer> mb =
       CHECK(MemoryBuffer::getFile(path, /*IsText=*/false,
                                   /*RequiresNullTerminator=*/false,
                                   /*IsVolatile=*/true),
             "could not open " + path);
   COFFModuleDefinition m = check(parseCOFFModuleDefinition(
-      mb->getMemBufferRef(), config->machine, config->mingw));
+      mb->getMemBufferRef(), ctx.config.machine, ctx.config.mingw));
 
   // Include in /reproduce: output if applicable.
-  driver->takeBuffer(std::move(mb));
+  ctx.driver.takeBuffer(std::move(mb));
 
-  if (config->outputFile.empty())
-    config->outputFile = std::string(saver().save(m.OutputFile));
-  config->importName = std::string(saver().save(m.ImportName));
+  if (ctx.config.outputFile.empty())
+    ctx.config.outputFile = std::string(saver().save(m.OutputFile));
+  ctx.config.importName = std::string(saver().save(m.ImportName));
   if (m.ImageBase)
-    config->imageBase = m.ImageBase;
+    ctx.config.imageBase = m.ImageBase;
   if (m.StackReserve)
-    config->stackReserve = m.StackReserve;
+    ctx.config.stackReserve = m.StackReserve;
   if (m.StackCommit)
-    config->stackCommit = m.StackCommit;
+    ctx.config.stackCommit = m.StackCommit;
   if (m.HeapReserve)
-    config->heapReserve = m.HeapReserve;
+    ctx.config.heapReserve = m.HeapReserve;
   if (m.HeapCommit)
-    config->heapCommit = m.HeapCommit;
+    ctx.config.heapCommit = m.HeapCommit;
   if (m.MajorImageVersion)
-    config->majorImageVersion = m.MajorImageVersion;
+    ctx.config.majorImageVersion = m.MajorImageVersion;
   if (m.MinorImageVersion)
-    config->minorImageVersion = m.MinorImageVersion;
+    ctx.config.minorImageVersion = m.MinorImageVersion;
   if (m.MajorOSVersion)
-    config->majorOSVersion = m.MajorOSVersion;
+    ctx.config.majorOSVersion = m.MajorOSVersion;
   if (m.MinorOSVersion)
-    config->minorOSVersion = m.MinorOSVersion;
+    ctx.config.minorOSVersion = m.MinorOSVersion;
 
   for (COFFShortExport e1 : m.Exports) {
     Export e2;
@@ -1026,7 +1022,7 @@ static void parseModuleDefs(StringRef path) {
         StringRef(e1.Name).contains('.')) {
       e2.name = saver().save(e1.ExtName);
       e2.forwardTo = saver().save(e1.Name);
-      config->exports.push_back(e2);
+      ctx.config.exports.push_back(e2);
       continue;
     }
     e2.name = saver().save(e1.Name);
@@ -1037,7 +1033,7 @@ static void parseModuleDefs(StringRef path) {
     e2.data = e1.Data;
     e2.isPrivate = e1.Private;
     e2.constant = e1.Constant;
-    config->exports.push_back(e2);
+    ctx.config.exports.push_back(e2);
   }
 }
 
@@ -1059,7 +1055,7 @@ bool LinkerDriver::run() {
 // Parse an /order file. If an option is given, the linker places
 // COMDAT sections in the same order as their names appear in the
 // given file.
-static void parseOrderFile(COFFLinkerContext &ctx, StringRef arg) {
+void LinkerDriver::parseOrderFile(StringRef arg) {
   // For some reason, the MSVC linker requires a filename to be
   // preceded by "@".
   if (!arg.startswith("@")) {
@@ -1088,22 +1084,22 @@ static void parseOrderFile(COFFLinkerContext &ctx, StringRef arg) {
   // end of an output section.
   for (StringRef arg : args::getLines(mb->getMemBufferRef())) {
     std::string s(arg);
-    if (config->machine == I386 && !isDecorated(s))
+    if (ctx.config.machine == I386 && !isDecorated(s))
       s = "_" + s;
 
     if (set.count(s) == 0) {
-      if (config->warnMissingOrderSymbol)
+      if (ctx.config.warnMissingOrderSymbol)
         warn("/order:" + arg + ": missing symbol: " + s + " [LNK4037]");
     }
     else
-      config->order[s] = INT_MIN + config->order.size();
+      ctx.config.order[s] = INT_MIN + ctx.config.order.size();
   }
 
   // Include in /reproduce: output if applicable.
-  driver->takeBuffer(std::move(mb));
+  ctx.driver.takeBuffer(std::move(mb));
 }
 
-static void parseCallGraphFile(COFFLinkerContext &ctx, StringRef path) {
+void LinkerDriver::parseCallGraphFile(StringRef path) {
   std::unique_ptr<MemoryBuffer> mb =
       CHECK(MemoryBuffer::getFile(path, /*IsText=*/false,
                                   /*RequiresNullTerminator=*/false,
@@ -1120,7 +1116,7 @@ static void parseCallGraphFile(COFFLinkerContext &ctx, StringRef path) {
   auto findSection = [&](StringRef name) -> SectionChunk * {
     Symbol *sym = map.lookup(name);
     if (!sym) {
-      if (config->warnMissingOrderSymbol)
+      if (ctx.config.warnMissingOrderSymbol)
         warn(path + ": no such symbol: " + name);
       return nullptr;
     }
@@ -1142,11 +1138,11 @@ static void parseCallGraphFile(COFFLinkerContext &ctx, StringRef path) {
 
     if (SectionChunk *from = findSection(fields[0]))
       if (SectionChunk *to = findSection(fields[1]))
-        config->callGraphProfile[{from, to}] += count;
+        ctx.config.callGraphProfile[{from, to}] += count;
   }
 
   // Include in /reproduce: output if applicable.
-  driver->takeBuffer(std::move(mb));
+  ctx.driver.takeBuffer(std::move(mb));
 }
 
 static void readCallGraphsFromObjectFiles(COFFLinkerContext &ctx) {
@@ -1172,7 +1168,7 @@ static void readCallGraphsFromObjectFiles(COFFLinkerContext &ctx) {
         auto *from = dyn_cast_or_null<SectionChunk>(fromSym->getChunk());
         auto *to = dyn_cast_or_null<SectionChunk>(toSym->getChunk());
         if (from && to)
-          config->callGraphProfile[{from, to}] += count;
+          ctx.config.callGraphProfile[{from, to}] += count;
       }
     }
   }
@@ -1187,7 +1183,7 @@ static void markAddrsig(Symbol *s) {
 static void findKeepUniqueSections(COFFLinkerContext &ctx) {
   // Exported symbols could be address-significant in other executables or DSOs,
   // so we conservatively mark them as address-significant.
-  for (Export &r : config->exports)
+  for (Export &r : ctx.config.exports)
     markAddrsig(r.sym);
 
   // Visit the address-significance table in each object file and mark each
@@ -1225,12 +1221,12 @@ static void findKeepUniqueSections(COFFLinkerContext &ctx) {
 // binary).
 // lld only supports %_PDB% and %_EXT% and warns on references to all other env
 // vars.
-static void parsePDBAltPath(StringRef altPath) {
+void LinkerDriver::parsePDBAltPath() {
   SmallString<128> buf;
   StringRef pdbBasename =
-      sys::path::filename(config->pdbPath, sys::path::Style::windows);
+      sys::path::filename(ctx.config.pdbPath, sys::path::Style::windows);
   StringRef binaryExtension =
-      sys::path::extension(config->outputFile, sys::path::Style::windows);
+      sys::path::extension(ctx.config.outputFile, sys::path::Style::windows);
   if (!binaryExtension.empty())
     binaryExtension = binaryExtension.substr(1); // %_EXT% does not include '.'.
 
@@ -1241,19 +1237,22 @@ static void parsePDBAltPath(StringRef altPath) {
   //   v   v   v
   //   a...%...%...
   size_t cursor = 0;
-  while (cursor < altPath.size()) {
+  while (cursor < ctx.config.pdbAltPath.size()) {
     size_t firstMark, secondMark;
-    if ((firstMark = altPath.find('%', cursor)) == StringRef::npos ||
-        (secondMark = altPath.find('%', firstMark + 1)) == StringRef::npos) {
+    if ((firstMark = ctx.config.pdbAltPath.find('%', cursor)) ==
+            StringRef::npos ||
+        (secondMark = ctx.config.pdbAltPath.find('%', firstMark + 1)) ==
+            StringRef::npos) {
       // Didn't find another full fragment, treat rest of string as literal.
-      buf.append(altPath.substr(cursor));
+      buf.append(ctx.config.pdbAltPath.substr(cursor));
       break;
     }
 
     // Found a full fragment. Append text in front of first %, and interpret
     // text between first and second % as variable name.
-    buf.append(altPath.substr(cursor, firstMark - cursor));
-    StringRef var = altPath.substr(firstMark, secondMark - firstMark + 1);
+    buf.append(ctx.config.pdbAltPath.substr(cursor, firstMark - cursor));
+    StringRef var =
+        ctx.config.pdbAltPath.substr(firstMark, secondMark - firstMark + 1);
     if (var.equals_insensitive("%_pdb%"))
       buf.append(pdbBasename);
     else if (var.equals_insensitive("%_ext%"))
@@ -1267,7 +1266,7 @@ static void parsePDBAltPath(StringRef altPath) {
     cursor = secondMark + 1;
   }
 
-  config->pdbAltPath = buf;
+  ctx.config.pdbAltPath = buf;
 }
 
 /// Convert resource files and potentially merge input resource object
@@ -1281,7 +1280,7 @@ void LinkerDriver::convertResources() {
       resourceObjFiles.push_back(f);
   }
 
-  if (!config->mingw &&
+  if (!ctx.config.mingw &&
       (resourceObjFiles.size() > 1 ||
        (resourceObjFiles.size() == 1 && !resources.empty()))) {
     error((!resources.empty() ? "internal .obj file created from .res files"
@@ -1312,16 +1311,16 @@ void LinkerDriver::convertResources() {
 // than MinGW in the case that nothing is explicitly exported.
 void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
   if (!args.hasArg(OPT_export_all_symbols)) {
-    if (!config->dll)
+    if (!ctx.config.dll)
       return;
 
-    if (!config->exports.empty())
+    if (!ctx.config.exports.empty())
       return;
     if (args.hasArg(OPT_exclude_all_symbols))
       return;
   }
 
-  AutoExporter exporter(excludedSymbols);
+  AutoExporter exporter(ctx, excludedSymbols);
 
   for (auto *arg : args.filtered(OPT_wholearchive_file))
     if (std::optional<StringRef> path = doFindFile(arg->getValue()))
@@ -1336,12 +1335,12 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
 
   ctx.symtab.forEachSymbol([&](Symbol *s) {
     auto *def = dyn_cast<Defined>(s);
-    if (!exporter.shouldExport(ctx, def))
+    if (!exporter.shouldExport(def))
       return;
 
     if (!def->isGCRoot) {
       def->isGCRoot = true;
-      config->gcroot.push_back(def);
+      ctx.config.gcroot.push_back(def);
     }
 
     Export e;
@@ -1351,7 +1350,7 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
       if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
         e.data = true;
     s->isUsedInRegularObj = true;
-    config->exports.push_back(e);
+    ctx.config.exports.push_back(e);
   });
 }
 
@@ -1404,6 +1403,7 @@ getVFS(const opt::InputArgList &args) {
 
 void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   ScopedTimer rootTimer(ctx.rootTimer);
+  Configuration *config = &ctx.config;
 
   // Needed for LTO.
   InitializeAllTargetInfos();
@@ -1423,7 +1423,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   }
 
   // Parse command line options.
-  ArgParser parser;
+  ArgParser parser(ctx);
   opt::InputArgList args = parser.parse(argsArr);
 
   // Parse and evaluate -mllvm options.
@@ -2036,7 +2036,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
 
   // Handle /functionpadmin
   for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
-    parseFunctionPadMin(arg, config->machine);
+    parseFunctionPadMin(arg);
 
   if (tar) {
     tar->append("response.txt",
@@ -2128,7 +2128,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   // Set default image name if neither /out or /def set it.
   if (config->outputFile.empty()) {
     config->outputFile = getOutputPath(
-        (*args.filtered(OPT_INPUT, OPT_wholearchive_file).begin())->getValue());
+        (*args.filtered(OPT_INPUT, OPT_wholearchive_file).begin())->getValue(),
+        config->dll, config->driver);
   }
 
   // Fail early if an output file is not writable.
@@ -2174,8 +2175,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       sys::fs::make_absolute(config->pdbAltPath);
       sys::path::remove_dots(config->pdbAltPath);
     } else {
-      // Don't do this earlier, so that Config->OutputFile is ready.
-      parsePDBAltPath(config->pdbAltPath);
+      // Don't do this earlier, so that ctx.OutputFile is ready.
+      parsePDBAltPath();
     }
   }
 
@@ -2317,7 +2318,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
 
   // If -thinlto-index-only is given, we should create only "index
   // files" and not object files. Index file creation is already done
-  // in compileBitcodeFiles, so we are done if that's the case.
+  // in addCombinedLTOObject, so we are done if that's the case.
   if (config->thinLTOIndexOnly)
     return;
 
@@ -2364,7 +2365,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
 
   // Handle /output-def (MinGW specific).
   if (auto *arg = args.getLastArg(OPT_output_def))
-    writeDefFile(arg->getValue());
+    writeDefFile(arg->getValue(), config->exports);
 
   // Set extra alignment for .comm symbols
   for (auto pair : config->alignComm) {
@@ -2403,14 +2404,14 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   if (auto *arg = args.getLastArg(OPT_order)) {
     if (args.hasArg(OPT_call_graph_ordering_file))
       error("/order and /call-graph-order-file may not be used together");
-    parseOrderFile(ctx, arg->getValue());
+    parseOrderFile(arg->getValue());
     config->callGraphProfileSort = false;
   }
 
   // Handle /call-graph-ordering-file and /call-graph-profile-sort (default on).
   if (config->callGraphProfileSort) {
     if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file)) {
-      parseCallGraphFile(ctx, arg->getValue());
+      parseCallGraphFile(arg->getValue());
     }
     readCallGraphsFromObjectFiles(ctx);
   }
@@ -2447,7 +2448,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   // Identify identical COMDAT sections to merge them.
   if (config->doICF != ICFLevel::None) {
     findKeepUniqueSections(ctx);
-    doICF(ctx, config->doICF);
+    doICF(ctx);
   }
 
   // Write the result.

diff  --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index 0c1ae3e013c5..28bf2cc81ef3 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -9,7 +9,6 @@
 #ifndef LLD_COFF_DRIVER_H
 #define LLD_COFF_DRIVER_H
 
-#include "COFFLinkerContext.h"
 #include "Config.h"
 #include "SymbolTable.h"
 #include "lld/Common/LLVM.h"
@@ -30,8 +29,6 @@
 
 namespace lld::coff {
 
-extern std::unique_ptr<class LinkerDriver> driver;
-
 using llvm::COFF::MachineTypes;
 using llvm::COFF::WindowsSubsystem;
 using std::optional;
@@ -41,10 +38,6 @@ class COFFOptTable : public llvm::opt::OptTable {
   COFFOptTable();
 };
 
-// Constructing the option table is expensive. Use a global table to avoid doing
-// it more than once.
-extern COFFOptTable optTable;
-
 // The result of parsing the .drective section. The /export: and /include:
 // options are handled separately because they reference symbols, and the number
 // of symbols can be quite large. The LLVM Option library will perform at least
@@ -59,6 +52,8 @@ struct ParsedDirectives {
 
 class ArgParser {
 public:
+  ArgParser(COFFLinkerContext &ctx);
+
   // Parses command line options.
   llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args);
 
@@ -75,11 +70,13 @@ class ArgParser {
   void addLINK(SmallVector<const char *, 256> &argv);
 
   std::vector<const char *> tokenize(StringRef s);
+
+  COFFLinkerContext &ctx;
 };
 
 class LinkerDriver {
 public:
-  LinkerDriver(COFFLinkerContext &c) : ctx(c) {}
+  LinkerDriver(COFFLinkerContext &ctx) : ctx(ctx) {}
 
   void linkerMain(llvm::ArrayRef<const char *> args);
 
@@ -115,6 +112,42 @@ class LinkerDriver {
   // Determines the location of the sysroot based on `args`, environment, etc.
   void detectWinSysRoot(const llvm::opt::InputArgList &args);
 
+  // Symbol names are mangled by prepending "_" on x86.
+  StringRef mangle(StringRef sym);
+
+  llvm::Triple::ArchType getArch();
+
+  uint64_t getDefaultImageBase();
+
+  bool isDecorated(StringRef sym);
+
+  std::string getMapFile(const llvm::opt::InputArgList &args,
+                         llvm::opt::OptSpecifier os,
+                         llvm::opt::OptSpecifier osFile);
+
+  std::string getImplibPath();
+
+  // The import name is calculated as follows:
+  //
+  //        | LIBRARY w/ ext |   LIBRARY w/o ext   | no LIBRARY
+  //   -----+----------------+---------------------+------------------
+  //   LINK | {value}        | {value}.{.dll/.exe} | {output name}
+  //    LIB | {value}        | {value}.dll         | {output name}.dll
+  //
+  std::string getImportName(bool asLib);
+
+  void createImportLibrary(bool asLib);
+
+  void parseModuleDefs(StringRef path);
+
+  // Parse an /order file. If an option is given, the linker places COMDAT
+  // sections int he same order as their names appear in the given file.
+  void parseOrderFile(StringRef arg);
+
+  void parseCallGraphFile(StringRef path);
+
+  void parsePDBAltPath();
+
   // Parses LIB environment which contains a list of search paths.
   void addLibSearchPaths();
 
@@ -171,61 +204,68 @@ class LinkerDriver {
   llvm::SmallString<128> universalCRTLibPath;
   int sdkMajor = 0;
   llvm::SmallString<128> windowsSdkLibPath;
-};
 
-// Functions below this line are defined in DriverUtils.cpp.
+  // Functions below this line are defined in DriverUtils.cpp.
 
-void printHelp(const char *argv0);
+  void printHelp(const char *argv0);
 
-// Parses a string in the form of "<integer>[,<integer>]".
-void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr);
+  // Parses a string in the form of "<integer>[,<integer>]".
+  void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr);
 
-void parseGuard(StringRef arg);
+  void parseGuard(StringRef arg);
 
-// Parses a string in the form of "<integer>[.<integer>]".
-// Minor's default value is 0.
-void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor);
+  // Parses a string in the form of "<integer>[.<integer>]".
+  // Minor's default value is 0.
+  void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor);
 
-// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
-void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
-                    uint32_t *minor, bool *gotVersion = nullptr);
+  // Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
+  void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
+                      uint32_t *minor, bool *gotVersion = nullptr);
 
-void parseAlternateName(StringRef);
-void parseMerge(StringRef);
-void parsePDBPageSize(StringRef);
-void parseSection(StringRef);
-void parseAligncomm(StringRef);
+  void parseAlternateName(StringRef);
+  void parseMerge(StringRef);
+  void parsePDBPageSize(StringRef);
+  void parseSection(StringRef);
+  void parseAligncomm(StringRef);
 
-// Parses a string in the form of "[:<integer>]"
-void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine);
+  // Parses a string in the form of "[:<integer>]"
+  void parseFunctionPadMin(llvm::opt::Arg *a);
 
-// Parses a string in the form of "EMBED[,=<integer>]|NO".
-void parseManifest(StringRef arg);
+  // Parses a string in the form of "EMBED[,=<integer>]|NO".
+  void parseManifest(StringRef arg);
 
-// Parses a string in the form of "level=<string>|uiAccess=<string>"
-void parseManifestUAC(StringRef arg);
+  // Parses a string in the form of "level=<string>|uiAccess=<string>"
+  void parseManifestUAC(StringRef arg);
 
-// Parses a string in the form of "cd|net[,(cd|net)]*"
-void parseSwaprun(StringRef arg);
+  // Parses a string in the form of "cd|net[,(cd|net)]*"
+  void parseSwaprun(StringRef arg);
 
-// Create a resource file containing a manifest XML.
-std::unique_ptr<MemoryBuffer> createManifestRes();
-void createSideBySideManifest();
+  // Create a resource file containing a manifest XML.
+  std::unique_ptr<MemoryBuffer> createManifestRes();
+  void createSideBySideManifest();
+  std::string createDefaultXml();
+  std::string createManifestXmlWithInternalMt(StringRef defaultXml);
+  std::string createManifestXmlWithExternalMt(StringRef defaultXml);
+  std::string createManifestXml();
 
-// Used for dllexported symbols.
-Export parseExport(StringRef arg);
-void fixupExports();
-void assignExportOrdinals();
+  std::unique_ptr<llvm::WritableMemoryBuffer>
+  createMemoryBufferForManifestRes(size_t manifestRes);
 
-// Parses a string in the form of "key=value" and check
-// if value matches previous values for the key.
-// This feature used in the directive section to reject
-// incompatible objects.
-void checkFailIfMismatch(StringRef arg, InputFile *source);
+  // Used for dllexported symbols.
+  Export parseExport(StringRef arg);
+  void fixupExports();
+  void assignExportOrdinals();
 
-// Convert Windows resource files (.res files) to a .obj file.
-MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
-                                 ArrayRef<ObjFile *> objs);
+  // Parses a string in the form of "key=value" and check
+  // if value matches previous values for the key.
+  // This feature used in the directive section to reject
+  // incompatible objects.
+  void checkFailIfMismatch(StringRef arg, InputFile *source);
+
+  // Convert Windows resource files (.res files) to a .obj file.
+  MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
+                                   ArrayRef<ObjFile *> objs);
+};
 
 // Create enum with OPT_xxx values for each option in Options.td
 enum {

diff  --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp
index d4eea0893941..782fdcdd4338 100644
--- a/lld/COFF/DriverUtils.cpp
+++ b/lld/COFF/DriverUtils.cpp
@@ -12,7 +12,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "Config.h"
+#include "COFFLinkerContext.h"
 #include "Driver.h"
 #include "Symbols.h"
 #include "lld/Common/ErrorHandler.h"
@@ -75,7 +75,7 @@ class Executor {
 } // anonymous namespace
 
 // Parses a string in the form of "<integer>[,<integer>]".
-void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) {
+void LinkerDriver::parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) {
   auto [s1, s2] = arg.split(',');
   if (s1.getAsInteger(0, *addr))
     fatal("invalid number: " + s1);
@@ -85,7 +85,8 @@ void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) {
 
 // Parses a string in the form of "<integer>[.<integer>]".
 // If second number is not present, Minor is set to 0.
-void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) {
+void LinkerDriver::parseVersion(StringRef arg, uint32_t *major,
+                                uint32_t *minor) {
   auto [s1, s2] = arg.split('.');
   if (s1.getAsInteger(10, *major))
     fatal("invalid number: " + s1);
@@ -94,28 +95,29 @@ void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) {
     fatal("invalid number: " + s2);
 }
 
-void parseGuard(StringRef fullArg) {
+void LinkerDriver::parseGuard(StringRef fullArg) {
   SmallVector<StringRef, 1> splitArgs;
   fullArg.split(splitArgs, ",");
   for (StringRef arg : splitArgs) {
     if (arg.equals_insensitive("no"))
-      config->guardCF = GuardCFLevel::Off;
+      ctx.config.guardCF = GuardCFLevel::Off;
     else if (arg.equals_insensitive("nolongjmp"))
-      config->guardCF &= ~GuardCFLevel::LongJmp;
+      ctx.config.guardCF &= ~GuardCFLevel::LongJmp;
     else if (arg.equals_insensitive("noehcont"))
-      config->guardCF &= ~GuardCFLevel::EHCont;
+      ctx.config.guardCF &= ~GuardCFLevel::EHCont;
     else if (arg.equals_insensitive("cf") || arg.equals_insensitive("longjmp"))
-      config->guardCF |= GuardCFLevel::CF | GuardCFLevel::LongJmp;
+      ctx.config.guardCF |= GuardCFLevel::CF | GuardCFLevel::LongJmp;
     else if (arg.equals_insensitive("ehcont"))
-      config->guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont;
+      ctx.config.guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont;
     else
       fatal("invalid argument to /guard: " + arg);
   }
 }
 
 // Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
-void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
-                    uint32_t *minor, bool *gotVersion) {
+void LinkerDriver::parseSubsystem(StringRef arg, WindowsSubsystem *sys,
+                                  uint32_t *major, uint32_t *minor,
+                                  bool *gotVersion) {
   auto [sysStr, ver] = arg.split(',');
   std::string sysStrLower = sysStr.lower();
   *sys = StringSwitch<WindowsSubsystem>(sysStrLower)
@@ -140,19 +142,19 @@ void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
 
 // Parse a string of the form of "<from>=<to>".
 // Results are directly written to Config.
-void parseAlternateName(StringRef s) {
+void LinkerDriver::parseAlternateName(StringRef s) {
   auto [from, to] = s.split('=');
   if (from.empty() || to.empty())
     fatal("/alternatename: invalid argument: " + s);
-  auto it = config->alternateNames.find(from);
-  if (it != config->alternateNames.end() && it->second != to)
+  auto it = ctx.config.alternateNames.find(from);
+  if (it != ctx.config.alternateNames.end() && it->second != to)
     fatal("/alternatename: conflicts: " + s);
-  config->alternateNames.insert(it, std::make_pair(from, to));
+  ctx.config.alternateNames.insert(it, std::make_pair(from, to));
 }
 
 // Parse a string of the form of "<from>=<to>".
 // Results are directly written to Config.
-void parseMerge(StringRef s) {
+void LinkerDriver::parseMerge(StringRef s) {
   auto [from, to] = s.split('=');
   if (from.empty() || to.empty())
     fatal("/merge: invalid argument: " + s);
@@ -160,7 +162,7 @@ void parseMerge(StringRef s) {
     fatal("/merge: cannot merge '.rsrc' with any section");
   if (from == ".reloc" || to == ".reloc")
     fatal("/merge: cannot merge '.reloc' with any section");
-  auto pair = config->merge.insert(std::make_pair(from, to));
+  auto pair = ctx.config.merge.insert(std::make_pair(from, to));
   bool inserted = pair.second;
   if (!inserted) {
     StringRef existing = pair.first->second;
@@ -169,7 +171,7 @@ void parseMerge(StringRef s) {
   }
 }
 
-void parsePDBPageSize(StringRef s) {
+void LinkerDriver::parsePDBPageSize(StringRef s) {
   int v;
   if (s.getAsInteger(0, v)) {
     error("/pdbpagesize: invalid argument: " + s);
@@ -180,7 +182,7 @@ void parsePDBPageSize(StringRef s) {
     return;
   }
 
-  config->pdbPageSize = v;
+  ctx.config.pdbPageSize = v;
 }
 
 static uint32_t parseSectionAttributes(StringRef s) {
@@ -216,15 +218,15 @@ static uint32_t parseSectionAttributes(StringRef s) {
 }
 
 // Parses /section option argument.
-void parseSection(StringRef s) {
+void LinkerDriver::parseSection(StringRef s) {
   auto [name, attrs] = s.split(',');
   if (name.empty() || attrs.empty())
     fatal("/section: invalid argument: " + s);
-  config->section[name] = parseSectionAttributes(attrs);
+  ctx.config.section[name] = parseSectionAttributes(attrs);
 }
 
 // Parses /aligncomm option argument.
-void parseAligncomm(StringRef s) {
+void LinkerDriver::parseAligncomm(StringRef s) {
   auto [name, align] = s.split(',');
   if (name.empty() || align.empty()) {
     error("/aligncomm: invalid argument: " + s);
@@ -235,56 +237,57 @@ void parseAligncomm(StringRef s) {
     error("/aligncomm: invalid argument: " + s);
     return;
   }
-  config->alignComm[std::string(name)] =
-      std::max(config->alignComm[std::string(name)], 1 << v);
+  ctx.config.alignComm[std::string(name)] =
+      std::max(ctx.config.alignComm[std::string(name)], 1 << v);
 }
 
 // Parses /functionpadmin option argument.
-void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine) {
+void LinkerDriver::parseFunctionPadMin(llvm::opt::Arg *a) {
   StringRef arg = a->getNumValues() ? a->getValue() : "";
   if (!arg.empty()) {
     // Optional padding in bytes is given.
-    if (arg.getAsInteger(0, config->functionPadMin))
+    if (arg.getAsInteger(0, ctx.config.functionPadMin))
       error("/functionpadmin: invalid argument: " + arg);
     return;
   }
   // No optional argument given.
   // Set default padding based on machine, similar to link.exe.
   // There is no default padding for ARM platforms.
-  if (machine == I386) {
-    config->functionPadMin = 5;
-  } else if (machine == AMD64) {
-    config->functionPadMin = 6;
+  if (ctx.config.machine == I386) {
+    ctx.config.functionPadMin = 5;
+  } else if (ctx.config.machine == AMD64) {
+    ctx.config.functionPadMin = 6;
   } else {
     error("/functionpadmin: invalid argument for this machine: " + arg);
   }
 }
 
 // Parses a string in the form of "EMBED[,=<integer>]|NO".
-// Results are directly written to Config.
-void parseManifest(StringRef arg) {
+// Results are directly written to
+// Config.
+void LinkerDriver::parseManifest(StringRef arg) {
   if (arg.equals_insensitive("no")) {
-    config->manifest = Configuration::No;
+    ctx.config.manifest = Configuration::No;
     return;
   }
   if (!arg.startswith_insensitive("embed"))
     fatal("invalid option " + arg);
-  config->manifest = Configuration::Embed;
+  ctx.config.manifest = Configuration::Embed;
   arg = arg.substr(strlen("embed"));
   if (arg.empty())
     return;
   if (!arg.startswith_insensitive(",id="))
     fatal("invalid option " + arg);
   arg = arg.substr(strlen(",id="));
-  if (arg.getAsInteger(0, config->manifestID))
+  if (arg.getAsInteger(0, ctx.config.manifestID))
     fatal("invalid option " + arg);
 }
 
 // Parses a string in the form of "level=<string>|uiAccess=<string>|NO".
 // Results are directly written to Config.
-void parseManifestUAC(StringRef arg) {
+void LinkerDriver::parseManifestUAC(StringRef arg) {
   if (arg.equals_insensitive("no")) {
-    config->manifestUAC = false;
+    ctx.config.manifestUAC = false;
     return;
   }
   for (;;) {
@@ -293,12 +296,12 @@ void parseManifestUAC(StringRef arg) {
       return;
     if (arg.startswith_insensitive("level=")) {
       arg = arg.substr(strlen("level="));
-      std::tie(config->manifestLevel, arg) = arg.split(" ");
+      std::tie(ctx.config.manifestLevel, arg) = arg.split(" ");
       continue;
     }
     if (arg.startswith_insensitive("uiaccess=")) {
       arg = arg.substr(strlen("uiaccess="));
-      std::tie(config->manifestUIAccess, arg) = arg.split(" ");
+      std::tie(ctx.config.manifestUIAccess, arg) = arg.split(" ");
       continue;
     }
     fatal("invalid option " + arg);
@@ -307,13 +310,13 @@ void parseManifestUAC(StringRef arg) {
 
 // Parses a string in the form of "cd|net[,(cd|net)]*"
 // Results are directly written to Config.
-void parseSwaprun(StringRef arg) {
+void LinkerDriver::parseSwaprun(StringRef arg) {
   do {
     auto [swaprun, newArg] = arg.split(',');
     if (swaprun.equals_insensitive("cd"))
-      config->swaprunCD = true;
+      ctx.config.swaprunCD = true;
     else if (swaprun.equals_insensitive("net"))
-      config->swaprunNet = true;
+      ctx.config.swaprunNet = true;
     else if (swaprun.empty())
       error("/swaprun: missing argument");
     else
@@ -371,7 +374,7 @@ class TemporaryFile {
 };
 }
 
-static std::string createDefaultXml() {
+std::string LinkerDriver::createDefaultXml() {
   std::string ret;
   raw_string_ostream os(ret);
 
@@ -380,17 +383,17 @@ static std::string createDefaultXml() {
   os << "<?xml version=\"1.0\" standalone=\"yes\"?>\n"
      << "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n"
      << "          manifestVersion=\"1.0\">\n";
-  if (config->manifestUAC) {
+  if (ctx.config.manifestUAC) {
     os << "  <trustInfo>\n"
        << "    <security>\n"
        << "      <requestedPrivileges>\n"
-       << "         <requestedExecutionLevel level=" << config->manifestLevel
-       << " uiAccess=" << config->manifestUIAccess << "/>\n"
+       << "         <requestedExecutionLevel level=" << ctx.config.manifestLevel
+       << " uiAccess=" << ctx.config.manifestUIAccess << "/>\n"
        << "      </requestedPrivileges>\n"
        << "    </security>\n"
        << "  </trustInfo>\n";
   }
-  for (auto manifestDependency : config->manifestDependencies) {
+  for (auto manifestDependency : ctx.config.manifestDependencies) {
     os << "  <dependency>\n"
        << "    <dependentAssembly>\n"
        << "      <assemblyIdentity " << manifestDependency << " />\n"
@@ -401,7 +404,8 @@ static std::string createDefaultXml() {
   return os.str();
 }
 
-static std::string createManifestXmlWithInternalMt(StringRef defaultXml) {
+std::string
+LinkerDriver::createManifestXmlWithInternalMt(StringRef defaultXml) {
   std::unique_ptr<MemoryBuffer> defaultXmlCopy =
       MemoryBuffer::getMemBufferCopy(defaultXml);
 
@@ -410,11 +414,11 @@ static std::string createManifestXmlWithInternalMt(StringRef defaultXml) {
     fatal("internal manifest tool failed on default xml: " +
           toString(std::move(e)));
 
-  for (StringRef filename : config->manifestInput) {
+  for (StringRef filename : ctx.config.manifestInput) {
     std::unique_ptr<MemoryBuffer> manifest =
         check(MemoryBuffer::getFile(filename));
     // Call takeBuffer to include in /reproduce: output if applicable.
-    if (auto e = merger.merge(driver->takeBuffer(std::move(manifest))))
+    if (auto e = merger.merge(takeBuffer(std::move(manifest))))
       fatal("internal manifest tool failed on file " + filename + ": " +
             toString(std::move(e)));
   }
@@ -422,7 +426,8 @@ static std::string createManifestXmlWithInternalMt(StringRef defaultXml) {
   return std::string(merger.getMergedManifest().get()->getBuffer());
 }
 
-static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
+std::string
+LinkerDriver::createManifestXmlWithExternalMt(StringRef defaultXml) {
   // Create the default manifest file as a temporary file.
   TemporaryFile Default("defaultxml", "manifest");
   std::error_code ec;
@@ -439,14 +444,14 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
   Executor e("mt.exe");
   e.add("/manifest");
   e.add(Default.path);
-  for (StringRef filename : config->manifestInput) {
+  for (StringRef filename : ctx.config.manifestInput) {
     e.add("/manifest");
     e.add(filename);
 
     // Manually add the file to the /reproduce: tar if needed.
-    if (driver->tar)
+    if (tar)
       if (auto mbOrErr = MemoryBuffer::getFile(filename))
-        driver->takeBuffer(std::move(*mbOrErr));
+        takeBuffer(std::move(*mbOrErr));
   }
   e.add("/nologo");
   e.add("/out:" + StringRef(user.path));
@@ -458,9 +463,9 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
           ->getBuffer());
 }
 
-static std::string createManifestXml() {
+std::string LinkerDriver::createManifestXml() {
   std::string defaultXml = createDefaultXml();
-  if (config->manifestInput.empty())
+  if (ctx.config.manifestInput.empty())
     return defaultXml;
 
   if (windows_manifest::isAvailable())
@@ -469,14 +474,14 @@ static std::string createManifestXml() {
   return createManifestXmlWithExternalMt(defaultXml);
 }
 
-static std::unique_ptr<WritableMemoryBuffer>
-createMemoryBufferForManifestRes(size_t manifestSize) {
+std::unique_ptr<WritableMemoryBuffer>
+LinkerDriver::createMemoryBufferForManifestRes(size_t manifestSize) {
   size_t resSize = alignTo(
       object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE +
           sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) +
           sizeof(object::WinResHeaderSuffix) + manifestSize,
       object::WIN_RES_DATA_ALIGNMENT);
-  return WritableMemoryBuffer::getNewMemBuffer(resSize, config->outputFile +
+  return WritableMemoryBuffer::getNewMemBuffer(resSize, ctx.config.outputFile +
                                                             ".manifest.res");
 }
 
@@ -487,7 +492,8 @@ static void writeResFileHeader(char *&buf) {
   buf += object::WIN_RES_NULL_ENTRY_SIZE;
 }
 
-static void writeResEntryHeader(char *&buf, size_t manifestSize) {
+static void writeResEntryHeader(char *&buf, size_t manifestSize,
+                                int manifestID) {
   // Write the prefix.
   auto *prefix = reinterpret_cast<object::WinResHeaderPrefix *>(buf);
   prefix->DataSize = manifestSize;
@@ -499,7 +505,7 @@ static void writeResEntryHeader(char *&buf, size_t manifestSize) {
   // Write the Type/Name IDs.
   auto *iDs = reinterpret_cast<object::WinResIDs *>(buf);
   iDs->setType(RT_MANIFEST);
-  iDs->setName(config->manifestID);
+  iDs->setName(manifestID);
   buf += sizeof(object::WinResIDs);
 
   // Write the suffix.
@@ -513,7 +519,7 @@ static void writeResEntryHeader(char *&buf, size_t manifestSize) {
 }
 
 // Create a resource file containing a manifest XML.
-std::unique_ptr<MemoryBuffer> createManifestRes() {
+std::unique_ptr<MemoryBuffer> LinkerDriver::createManifestRes() {
   std::string manifest = createManifestXml();
 
   std::unique_ptr<WritableMemoryBuffer> res =
@@ -521,17 +527,17 @@ std::unique_ptr<MemoryBuffer> createManifestRes() {
 
   char *buf = res->getBufferStart();
   writeResFileHeader(buf);
-  writeResEntryHeader(buf, manifest.size());
+  writeResEntryHeader(buf, manifest.size(), ctx.config.manifestID);
 
   // Copy the manifest data into the .res file.
   std::copy(manifest.begin(), manifest.end(), buf);
   return std::move(res);
 }
 
-void createSideBySideManifest() {
-  std::string path = std::string(config->manifestFile);
+void LinkerDriver::createSideBySideManifest() {
+  std::string path = std::string(ctx.config.manifestFile);
   if (path == "")
-    path = config->outputFile + ".manifest";
+    path = ctx.config.outputFile + ".manifest";
   std::error_code ec;
   raw_fd_ostream out(path, ec, sys::fs::OF_TextWithCRLF);
   if (ec)
@@ -543,7 +549,7 @@ void createSideBySideManifest() {
 // "<name>[=<internalname>][, at ordinal[,NONAME]][,DATA][,PRIVATE]"
 // or "<name>=<dllname>.<name>".
 // Used for parsing /export arguments.
-Export parseExport(StringRef arg) {
+Export LinkerDriver::parseExport(StringRef arg) {
   Export e;
   StringRef rest;
   std::tie(e.name, rest) = arg.split(",");
@@ -605,14 +611,14 @@ Export parseExport(StringRef arg) {
   fatal("invalid /export: " + arg);
 }
 
-static StringRef undecorate(StringRef sym) {
-  if (config->machine != I386)
+static StringRef undecorate(COFFLinkerContext &ctx, StringRef sym) {
+  if (ctx.config.machine != I386)
     return sym;
   // In MSVC mode, a fully decorated stdcall function is exported
   // as-is with the leading underscore (with type IMPORT_NAME).
   // In MinGW mode, a decorated stdcall function gets the underscore
   // removed, just like normal cdecl functions.
-  if (sym.startswith("_") && sym.contains('@') && !config->mingw)
+  if (sym.startswith("_") && sym.contains('@') && !ctx.config.mingw)
     return sym;
   return sym.startswith("_") ? sym.substr(1) : sym;
 }
@@ -639,26 +645,26 @@ static StringRef killAt(StringRef sym, bool prefix) {
 
 // Performs error checking on all /export arguments.
 // It also sets ordinals.
-void fixupExports() {
+void LinkerDriver::fixupExports() {
   // Symbol ordinals must be unique.
   std::set<uint16_t> ords;
-  for (Export &e : config->exports) {
+  for (Export &e : ctx.config.exports) {
     if (e.ordinal == 0)
       continue;
     if (!ords.insert(e.ordinal).second)
       fatal("duplicate export ordinal: " + e.name);
   }
 
-  for (Export &e : config->exports) {
+  for (Export &e : ctx.config.exports) {
     if (!e.forwardTo.empty()) {
-      e.exportName = undecorate(e.name);
+      e.exportName = undecorate(ctx, e.name);
     } else {
-      e.exportName = undecorate(e.extName.empty() ? e.name : e.extName);
+      e.exportName = undecorate(ctx, e.extName.empty() ? e.name : e.extName);
     }
   }
 
-  if (config->killAt && config->machine == I386) {
-    for (Export &e : config->exports) {
+  if (ctx.config.killAt && ctx.config.machine == I386) {
+    for (Export &e : ctx.config.exports) {
       e.name = killAt(e.name, true);
       e.exportName = killAt(e.exportName, false);
       e.extName = killAt(e.extName, true);
@@ -667,9 +673,9 @@ void fixupExports() {
   }
 
   // Uniquefy by name.
-  DenseMap<StringRef, Export *> map(config->exports.size());
+  DenseMap<StringRef, Export *> map(ctx.config.exports.size());
   std::vector<Export> v;
-  for (Export &e : config->exports) {
+  for (Export &e : ctx.config.exports) {
     auto pair = map.insert(std::make_pair(e.exportName, &e));
     bool inserted = pair.second;
     if (inserted) {
@@ -681,20 +687,20 @@ void fixupExports() {
       continue;
     warn("duplicate /export option: " + e.name);
   }
-  config->exports = std::move(v);
+  ctx.config.exports = std::move(v);
 
   // Sort by name.
-  llvm::sort(config->exports, [](const Export &a, const Export &b) {
+  llvm::sort(ctx.config.exports, [](const Export &a, const Export &b) {
     return a.exportName < b.exportName;
   });
 }
 
-void assignExportOrdinals() {
+void LinkerDriver::assignExportOrdinals() {
   // Assign unique ordinals if default (= 0).
   uint32_t max = 0;
-  for (Export &e : config->exports)
+  for (Export &e : ctx.config.exports)
     max = std::max(max, (uint32_t)e.ordinal);
-  for (Export &e : config->exports)
+  for (Export &e : ctx.config.exports)
     if (e.ordinal == 0)
       e.ordinal = ++max;
   if (max > std::numeric_limits<uint16_t>::max())
@@ -704,11 +710,11 @@ void assignExportOrdinals() {
 
 // Parses a string in the form of "key=value" and check
 // if value matches previous values for the same key.
-void checkFailIfMismatch(StringRef arg, InputFile *source) {
+void LinkerDriver::checkFailIfMismatch(StringRef arg, InputFile *source) {
   auto [k, v] = arg.split('=');
   if (k.empty() || v.empty())
     fatal("/failifmismatch: invalid argument: " + arg);
-  std::pair<StringRef, InputFile *> existing = config->mustMatch[k];
+  std::pair<StringRef, InputFile *> existing = ctx.config.mustMatch[k];
   if (!existing.first.empty() && v != existing.first) {
     std::string sourceStr = source ? toString(source) : "cmd-line";
     std::string existingStr =
@@ -717,14 +723,14 @@ void checkFailIfMismatch(StringRef arg, InputFile *source) {
           existingStr + " has value " + existing.first + "\n>>> " + sourceStr +
           " has value " + v);
   }
-  config->mustMatch[k] = {v, source};
+  ctx.config.mustMatch[k] = {v, source};
 }
 
 // Convert Windows resource files (.res files) to a .obj file.
 // Does what cvtres.exe does, but in-process and cross-platform.
-MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
-                                 ArrayRef<ObjFile *> objs) {
-  object::WindowsResourceParser parser(/* MinGW */ config->mingw);
+MemoryBufferRef LinkerDriver::convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
+                                               ArrayRef<ObjFile *> objs) {
+  object::WindowsResourceParser parser(/* MinGW */ ctx.config.mingw);
 
   std::vector<std::string> duplicates;
   for (MemoryBufferRef mb : mbs) {
@@ -749,18 +755,18 @@ MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
       fatal(toString(std::move(ec)));
   }
 
-  if (config->mingw)
+  if (ctx.config.mingw)
     parser.cleanUpManifests(duplicates);
 
   for (const auto &dupeDiag : duplicates)
-    if (config->forceMultipleRes)
+    if (ctx.config.forceMultipleRes)
       warn(dupeDiag);
     else
       error(dupeDiag);
 
   Expected<std::unique_ptr<MemoryBuffer>> e =
-      llvm::object::writeWindowsResourceCOFF(config->machine, parser,
-                                             config->timestamp);
+      llvm::object::writeWindowsResourceCOFF(ctx.config.machine, parser,
+                                             ctx.config.timestamp);
   if (!e)
     fatal("failed to write .res to COFF: " + toString(e.takeError()));
 
@@ -790,8 +796,6 @@ static constexpr llvm::opt::OptTable::Info infoTable[] = {
 
 COFFOptTable::COFFOptTable() : OptTable(infoTable, true) {}
 
-COFFOptTable optTable;
-
 // Set color diagnostics according to --color-diagnostics={auto,always,never}
 // or --no-color-diagnostics flags.
 static void handleColorDiagnostics(opt::InputArgList &args) {
@@ -827,6 +831,8 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) {
   return cl::TokenizeWindowsCommandLine;
 }
 
+ArgParser::ArgParser(COFFLinkerContext &c) : ctx(c) {}
+
 // Parses a given list of options.
 opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
   // Make InputArgList from string vectors.
@@ -837,7 +843,8 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
   // options so we parse here before and ignore all the options but
   // --rsp-quoting and /lldignoreenv.
   // (This means --rsp-quoting can't be added through %LINK%.)
-  opt::InputArgList args = optTable.ParseArgs(argv, missingIndex, missingCount);
+  opt::InputArgList args =
+      ctx.optTable.ParseArgs(argv, missingIndex, missingCount);
 
   // Expand response files (arguments in the form of @<filename>) and insert
   // flags from %LINK% and %_LINK_%, and then parse the argument again.
@@ -846,8 +853,8 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
   if (!args.hasArg(OPT_lldignoreenv))
     addLINK(expandedArgv);
   cl::ExpandResponseFiles(saver(), getQuotingStyle(args), expandedArgv);
-  args = optTable.ParseArgs(makeArrayRef(expandedArgv).drop_front(),
-                            missingIndex, missingCount);
+  args = ctx.optTable.ParseArgs(makeArrayRef(expandedArgv).drop_front(),
+                                missingIndex, missingCount);
 
   // Print the real command line if response files are expanded.
   if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) {
@@ -859,10 +866,10 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
 
   // Save the command line after response file expansion so we can write it to
   // the PDB if necessary. Mimic MSVC, which skips input files.
-  config->argv = {argv[0]};
+  ctx.config.argv = {argv[0]};
   for (opt::Arg *arg : args) {
     if (arg->getOption().getKind() != opt::Option::InputClass) {
-      config->argv.push_back(args.getArgString(arg->getIndex()));
+      ctx.config.argv.push_back(args.getArgString(arg->getIndex()));
     }
   }
 
@@ -876,7 +883,7 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
 
   for (opt::Arg *arg : args.filtered(OPT_UNKNOWN)) {
     std::string nearest;
-    if (optTable.findNearest(arg->getAsString(args), nearest) > 1)
+    if (ctx.optTable.findNearest(arg->getAsString(args), nearest) > 1)
       warn("ignoring unknown argument '" + arg->getAsString(args) + "'");
     else
       warn("ignoring unknown argument '" + arg->getAsString(args) +
@@ -921,7 +928,7 @@ ParsedDirectives ArgParser::parseDirectives(StringRef s) {
   unsigned missingIndex;
   unsigned missingCount;
 
-  result.args = optTable.ParseArgs(rest, missingIndex, missingCount);
+  result.args = ctx.optTable.ParseArgs(rest, missingIndex, missingCount);
 
   if (missingCount)
     fatal(Twine(result.args.getArgString(missingIndex)) + ": missing argument");
@@ -951,10 +958,10 @@ std::vector<const char *> ArgParser::tokenize(StringRef s) {
   return std::vector<const char *>(tokens.begin(), tokens.end());
 }
 
-void printHelp(const char *argv0) {
-  optTable.printHelp(lld::outs(),
-                     (std::string(argv0) + " [options] file...").c_str(),
-                     "LLVM Linker", false);
+void LinkerDriver::printHelp(const char *argv0) {
+  ctx.optTable.printHelp(lld::outs(),
+                         (std::string(argv0) + " [options] file...").c_str(),
+                         "LLVM Linker", false);
 }
 
 } // namespace coff

diff  --git a/lld/COFF/ICF.cpp b/lld/COFF/ICF.cpp
index f001225b7834..7ece725313d0 100644
--- a/lld/COFF/ICF.cpp
+++ b/lld/COFF/ICF.cpp
@@ -38,7 +38,7 @@ namespace lld::coff {
 
 class ICF {
 public:
-  ICF(COFFLinkerContext &c, ICFLevel icfLevel) : icfLevel(icfLevel), ctx(c){};
+  ICF(COFFLinkerContext &c) : ctx(c){};
   void run();
 
 private:
@@ -61,7 +61,6 @@ class ICF {
   std::vector<SectionChunk *> chunks;
   int cnt = 0;
   std::atomic<bool> repeat = {false};
-  ICFLevel icfLevel = ICFLevel::All;
 
   COFFLinkerContext &ctx;
 };
@@ -84,7 +83,7 @@ bool ICF::isEligible(SectionChunk *c) {
     return false;
 
   // Under regular (not safe) ICF, all code sections are eligible.
-  if ((icfLevel == ICFLevel::All) &&
+  if ((ctx.config.doICF == ICFLevel::All) &&
       c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
     return true;
 
@@ -317,8 +316,6 @@ void ICF::run() {
 }
 
 // Entry point to ICF.
-void doICF(COFFLinkerContext &ctx, ICFLevel icfLevel) {
-  ICF(ctx, icfLevel).run();
-}
+void doICF(COFFLinkerContext &ctx) { ICF(ctx).run(); }
 
 } // namespace lld::coff

diff  --git a/lld/COFF/ICF.h b/lld/COFF/ICF.h
index 8aafdf7b2335..f9ed8edc396f 100644
--- a/lld/COFF/ICF.h
+++ b/lld/COFF/ICF.h
@@ -15,10 +15,9 @@
 
 namespace lld::coff {
 
-class Chunk;
 class COFFLinkerContext;
 
-void doICF(COFFLinkerContext &ctx, ICFLevel);
+void doICF(COFFLinkerContext &ctx);
 
 } // namespace lld::coff
 

diff  --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index 84920aecf749..f491a225f326 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -72,7 +72,7 @@ std::string lld::toString(const coff::InputFile *file) {
 /// Checks that Source is compatible with being a weak alias to Target.
 /// If Source is Undefined and has no weak alias set, makes it a weak
 /// alias to Target.
-static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f,
+static void checkAndSetWeakAlias(COFFLinkerContext &ctx, InputFile *f,
                                  Symbol *source, Symbol *target) {
   if (auto *u = dyn_cast<Undefined>(source)) {
     if (u->weakAlias && u->weakAlias != target) {
@@ -81,9 +81,9 @@ static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f,
       // of another symbol emitted near the weak symbol.
       // Just use the definition from the first object file that defined
       // this weak symbol.
-      if (config->mingw)
+      if (ctx.config.mingw)
         return;
-      symtab->reportDuplicate(source, f);
+      ctx.symtab.reportDuplicate(source, f);
     }
     u->weakAlias = target;
   }
@@ -109,13 +109,13 @@ void ArchiveFile::parse() {
 void ArchiveFile::addMember(const Archive::Symbol &sym) {
   const Archive::Child &c =
       CHECK(sym.getMember(),
-            "could not get the member for symbol " + toCOFFString(sym));
+            "could not get the member for symbol " + toCOFFString(ctx, sym));
 
   // Return an empty buffer if we have already returned the same buffer.
   if (!seen.insert(c.getChildOffset()).second)
     return;
 
-  driver->enqueueArchiveMember(c, sym, getName());
+  ctx.driver.enqueueArchiveMember(c, sym, getName());
 }
 
 std::vector<MemoryBufferRef> lld::coff::getArchiveMembers(Archive *file) {
@@ -237,7 +237,7 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
   // and then write it to a separate .pdb file.
 
   // Ignore DWARF debug info unless /debug is given.
-  if (!config->debug && name.startswith(".debug_"))
+  if (!ctx.config.debug && name.startswith(".debug_"))
     return nullptr;
 
   if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
@@ -260,7 +260,7 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
     guardEHContChunks.push_back(c);
   else if (name == ".sxdata")
     sxDataChunks.push_back(c);
-  else if (config->tailMerge && sec->NumberOfRelocations == 0 &&
+  else if (ctx.config.tailMerge && sec->NumberOfRelocations == 0 &&
            name == ".rdata" && leaderName.startswith("??_C@"))
     // COFF sections that look like string literal sections (i.e. no
     // relocations, in .rdata, leader symbol name matches the MSVC name mangling
@@ -366,7 +366,7 @@ Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
     // everything should be fine. If something actually refers to the symbol
     // (e.g. the undefined weak alias), linking will fail due to undefined
     // references at the end.
-    if (config->mingw && name.startswith(".weak."))
+    if (ctx.config.mingw && name.startswith(".weak."))
       return nullptr;
     return ctx.symtab.addUndefined(name, this, false);
   }
@@ -400,7 +400,7 @@ void ObjFile::initializeSymbols() {
     } else if (std::optional<Symbol *> optSym =
                    createDefined(coffSym, comdatDefs, prevailingComdat)) {
       symbols[i] = *optSym;
-      if (config->mingw && prevailingComdat)
+      if (ctx.config.mingw && prevailingComdat)
         recordPrevailingSymbolForMingw(coffSym, prevailingSectionMap);
     } else {
       // createDefined() returns std::nullopt if a symbol belongs to a section
@@ -421,7 +421,7 @@ void ObjFile::initializeSymbols() {
     if (const coff_aux_section_definition *def = sym.getSectionDefinition()) {
       if (def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
         readAssociativeDefinition(sym, def);
-      else if (config->mingw)
+      else if (ctx.config.mingw)
         maybeAssociateSEHForMingw(sym, def, prevailingSectionMap);
     }
     if (sparseChunks[sym.getSectionNumber()] == pendingComdat) {
@@ -436,7 +436,7 @@ void ObjFile::initializeSymbols() {
   for (auto &kv : weakAliases) {
     Symbol *sym = kv.first;
     uint32_t idx = kv.second;
-    checkAndSetWeakAlias(&ctx.symtab, this, sym, symbols[idx]);
+    checkAndSetWeakAlias(ctx, this, sym, symbols[idx]);
   }
 
   // Free the memory used by sparseChunks now that symbol loading is finished.
@@ -496,10 +496,10 @@ void ObjFile::handleComdatSelection(
   // Clang on the other hand picks "any". To be able to link two object files
   // with a __declspec(selectany) declaration, one compiled with gcc and the
   // other with clang, we merge them as proper "same size as"
-  if (config->mingw && ((selection == IMAGE_COMDAT_SELECT_ANY &&
-                         leaderSelection == IMAGE_COMDAT_SELECT_SAME_SIZE) ||
-                        (selection == IMAGE_COMDAT_SELECT_SAME_SIZE &&
-                         leaderSelection == IMAGE_COMDAT_SELECT_ANY))) {
+  if (ctx.config.mingw && ((selection == IMAGE_COMDAT_SELECT_ANY &&
+                            leaderSelection == IMAGE_COMDAT_SELECT_SAME_SIZE) ||
+                           (selection == IMAGE_COMDAT_SELECT_SAME_SIZE &&
+                            leaderSelection == IMAGE_COMDAT_SELECT_ANY))) {
     leaderSelection = selection = IMAGE_COMDAT_SELECT_SAME_SIZE;
   }
 
@@ -511,7 +511,7 @@ void ObjFile::handleComdatSelection(
   // seems better though.
   // (This behavior matches ModuleLinker::getComdatResult().)
   if (selection != leaderSelection) {
-    log(("conflicting comdat type for " + toString(*leader) + ": " +
+    log(("conflicting comdat type for " + toString(ctx, *leader) + ": " +
          Twine((int)leaderSelection) + " in " + toString(leader->getFile()) +
          " and " + Twine((int)selection) + " in " + toString(this))
             .str());
@@ -530,7 +530,7 @@ void ObjFile::handleComdatSelection(
 
   case IMAGE_COMDAT_SELECT_SAME_SIZE:
     if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) {
-      if (!config->mingw) {
+      if (!ctx.config.mingw) {
         ctx.symtab.reportDuplicate(leader, this);
       } else {
         const coff_aux_section_definition *leaderDef = nullptr;
@@ -607,7 +607,7 @@ std::optional<Symbol *> ObjFile::createDefined(
 
     if (sym.isExternal())
       return ctx.symtab.addAbsolute(name, sym);
-    return make<DefinedAbsolute>(name, sym);
+    return make<DefinedAbsolute>(ctx, name, sym);
   }
 
   int32_t sectionNumber = sym.getSectionNumber();
@@ -751,7 +751,7 @@ void ObjFile::initializeFlags() {
 // DebugTypes.h). Both cases only happen with cl.exe: clang-cl produces regular
 // output even with /Yc and /Yu and with /Zi.
 void ObjFile::initializeDependencies() {
-  if (!config->debug)
+  if (!ctx.config.debug)
     return;
 
   bool isPCH = false;
@@ -906,7 +906,7 @@ ObjFile::getVariableLocation(StringRef var) {
     if (!dwarf)
       return std::nullopt;
   }
-  if (config->machine == I386)
+  if (ctx.config.machine == I386)
     var.consume_front("_");
   std::optional<std::pair<std::string, unsigned>> ret =
       dwarf->getVariableLoc(var);
@@ -935,9 +935,12 @@ void ObjFile::enqueuePdbFile(StringRef path, ObjFile *fromFile) {
   auto it = ctx.pdbInputFileInstances.emplace(*p, nullptr);
   if (!it.second)
     return; // already scheduled for load
-  driver->enqueuePDB(*p);
+  ctx.driver.enqueuePDB(*p);
 }
 
+ImportFile::ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m)
+    : InputFile(ctx, ImportKind, m), live(!ctx.config.doGC), thunkLive(live) {}
+
 void ImportFile::parse() {
   const char *buf = mb.getBufferStart();
   const auto *hdr = reinterpret_cast<const coff_import_header *>(buf);
@@ -993,8 +996,10 @@ BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
                          bool lazy)
     : InputFile(ctx, BitcodeKind, mb, lazy) {
   std::string path = mb.getBufferIdentifier().str();
-  if (config->thinLTOIndexOnly)
-    path = replaceThinLTOSuffix(mb.getBufferIdentifier());
+  if (ctx.config.thinLTOIndexOnly)
+    path = replaceThinLTOSuffix(mb.getBufferIdentifier(),
+                                ctx.config.thinLTOObjectSuffixReplace.first,
+                                ctx.config.thinLTOObjectSuffixReplace.second);
 
   // ThinLTO assumes that all MemoryBufferRefs given to it have a unique
   // name. If two archives define two members with the same name, this
@@ -1014,36 +1019,9 @@ BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
 
 BitcodeFile::~BitcodeFile() = default;
 
-namespace {
-// Convenience class for initializing a coff_section with specific flags.
-class FakeSection {
-public:
-  FakeSection(int c) { section.Characteristics = c; }
-
-  coff_section section;
-};
-
-// Convenience class for initializing a SectionChunk with specific flags.
-class FakeSectionChunk {
-public:
-  FakeSectionChunk(const coff_section *section) : chunk(nullptr, section) {
-    // Comdats from LTO files can't be fully treated as regular comdats
-    // at this point; we don't know what size or contents they are going to
-    // have, so we can't do proper checking of such aspects of them.
-    chunk.selection = IMAGE_COMDAT_SELECT_ANY;
-  }
-
-  SectionChunk chunk;
-};
-
-FakeSection ltoTextSection(IMAGE_SCN_MEM_EXECUTE);
-FakeSection ltoDataSection(IMAGE_SCN_CNT_INITIALIZED_DATA);
-FakeSectionChunk ltoTextSectionChunk(&ltoTextSection.section);
-FakeSectionChunk ltoDataSectionChunk(&ltoDataSection.section);
-} // namespace
-
 void BitcodeFile::parse() {
   llvm::StringSaver &saver = lld::saver();
+
   std::vector<std::pair<Symbol *, bool>> comdat(obj->getComdatTable().size());
   for (size_t i = 0; i != obj->getComdatTable().size(); ++i)
     // FIXME: Check nodeduplicate
@@ -1055,9 +1033,9 @@ void BitcodeFile::parse() {
     Symbol *sym;
     SectionChunk *fakeSC = nullptr;
     if (objSym.isExecutable())
-      fakeSC = &ltoTextSectionChunk.chunk;
+      fakeSC = &ctx.ltoTextSectionChunk->chunk;
     else
-      fakeSC = &ltoDataSectionChunk.chunk;
+      fakeSC = &ctx.ltoDataSectionChunk->chunk;
     if (objSym.isUndefined()) {
       sym = ctx.symtab.addUndefined(symName, this, false);
     } else if (objSym.isCommon()) {
@@ -1067,7 +1045,7 @@ void BitcodeFile::parse() {
       sym = ctx.symtab.addUndefined(symName, this, true);
       std::string fallback = std::string(objSym.getCOFFWeakExternalFallback());
       Symbol *alias = ctx.symtab.addUndefined(saver.save(fallback));
-      checkAndSetWeakAlias(&ctx.symtab, this, sym, alias);
+      checkAndSetWeakAlias(ctx, this, sym, alias);
     } else if (comdatIndex != -1) {
       if (symName == obj->getComdatTable()[comdatIndex].first) {
         sym = comdat[comdatIndex].first;
@@ -1084,7 +1062,7 @@ void BitcodeFile::parse() {
     }
     symbols.push_back(sym);
     if (objSym.isUsed())
-      config->gcroot.push_back(sym);
+      ctx.config.gcroot.push_back(sym);
   }
   directives = obj->getCOFFLinkerOpts();
 }
@@ -1110,10 +1088,8 @@ MachineTypes BitcodeFile::getMachineType() {
   }
 }
 
-std::string lld::coff::replaceThinLTOSuffix(StringRef path) {
-  StringRef suffix = config->thinLTOObjectSuffixReplace.first;
-  StringRef repl = config->thinLTOObjectSuffixReplace.second;
-
+std::string lld::coff::replaceThinLTOSuffix(StringRef path, StringRef suffix,
+                                            StringRef repl) {
   if (path.consume_back(suffix))
     return (path + repl).str();
   return std::string(path);

diff  --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h
index 75c6ee51c54b..e80886447fbf 100644
--- a/lld/COFF/InputFiles.h
+++ b/lld/COFF/InputFiles.h
@@ -333,8 +333,7 @@ class PDBInputFile : public InputFile {
 // for details about the format.
 class ImportFile : public InputFile {
 public:
-  explicit ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m)
-      : InputFile(ctx, ImportKind, m) {}
+  explicit ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m);
 
   static bool classof(const InputFile *f) { return f->kind() == ImportKind; }
 
@@ -358,8 +357,8 @@ class ImportFile : public InputFile {
   // symbols provided by this import library member. We also track whether the
   // imported symbol is used separately from whether the thunk is used in order
   // to avoid creating unnecessary thunks.
-  bool live = !config->doGC;
-  bool thunkLive = !config->doGC;
+  bool live;
+  bool thunkLive;
 };
 
 // Used for LTO.
@@ -408,7 +407,16 @@ inline bool isBitcode(MemoryBufferRef mb) {
   return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
 }
 
-std::string replaceThinLTOSuffix(StringRef path);
+// Convenience class for initializing a coff_section with specific flags.
+class FakeSection {
+public:
+  FakeSection(int c) { section.Characteristics = c; }
+
+  coff_section section;
+};
+
+std::string replaceThinLTOSuffix(StringRef path, StringRef suffix,
+                                 StringRef repl);
 } // namespace coff
 
 std::string toString(const coff::InputFile *file);

diff  --git a/lld/COFF/LLDMapFile.cpp b/lld/COFF/LLDMapFile.cpp
index af15af5accf1..c14480aaf821 100644
--- a/lld/COFF/LLDMapFile.cpp
+++ b/lld/COFF/LLDMapFile.cpp
@@ -73,12 +73,13 @@ static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) {
 
 // Construct a map from symbols to their stringified representations.
 static DenseMap<DefinedRegular *, std::string>
-getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
+getSymbolStrings(const COFFLinkerContext &ctx,
+                 ArrayRef<DefinedRegular *> syms) {
   std::vector<std::string> str(syms.size());
   parallelFor((size_t)0, syms.size(), [&](size_t i) {
     raw_string_ostream os(str[i]);
     writeHeader(os, syms[i]->getRVA(), 0, 0);
-    os << indent16 << toString(*syms[i]);
+    os << indent16 << toString(ctx, *syms[i]);
   });
 
   DenseMap<DefinedRegular *, std::string> ret;
@@ -88,18 +89,18 @@ getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
 }
 
 void lld::coff::writeLLDMapFile(const COFFLinkerContext &ctx) {
-  if (config->lldmapFile.empty())
+  if (ctx.config.lldmapFile.empty())
     return;
 
   std::error_code ec;
-  raw_fd_ostream os(config->lldmapFile, ec, sys::fs::OF_None);
+  raw_fd_ostream os(ctx.config.lldmapFile, ec, sys::fs::OF_None);
   if (ec)
-    fatal("cannot open " + config->lldmapFile + ": " + ec.message());
+    fatal("cannot open " + ctx.config.lldmapFile + ": " + ec.message());
 
   // Collect symbol info that we want to print out.
   std::vector<DefinedRegular *> syms = getSymbols(ctx);
   SymbolMapTy sectionSyms = getSectionSyms(syms);
-  DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
+  DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(ctx, syms);
 
   // Print out the header line.
   os << "Address  Size     Align Out     In      Symbol\n";

diff  --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index 7dcd7a2aefb4..327992890420 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "LTO.h"
+#include "COFFLinkerContext.h"
 #include "Config.h"
 #include "InputFiles.h"
 #include "Symbols.h"
@@ -53,17 +54,17 @@ static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
   return ret;
 }
 
-static std::string getThinLTOOutputFile(StringRef path) {
+std::string BitcodeCompiler::getThinLTOOutputFile(StringRef path) {
   return lto::getThinLTOOutputFile(
-      std::string(path), std::string(config->thinLTOPrefixReplace.first),
-      std::string(config->thinLTOPrefixReplace.second));
+      std::string(path), std::string(ctx.config.thinLTOPrefixReplace.first),
+      std::string(ctx.config.thinLTOPrefixReplace.second));
 }
 
-static lto::Config createConfig() {
+lto::Config BitcodeCompiler::createConfig() {
   lto::Config c;
   c.Options = initTargetOptionsFromCodeGenFlags();
   c.Options.EmitAddrsig = true;
-  for (StringRef C : config->mllvmOpts)
+  for (StringRef C : ctx.config.mllvmOpts)
     c.MllvmArgs.emplace_back(C.str());
 
   // Always emit a section per function/datum with LTO. LLVM LTO should get most
@@ -74,7 +75,7 @@ static lto::Config createConfig() {
   // Use static reloc model on 32-bit x86 because it usually results in more
   // compact code, and because there are also known code generation bugs when
   // using the PIC model (see PR34306).
-  if (config->machine == COFF::IMAGE_FILE_MACHINE_I386)
+  if (ctx.config.machine == COFF::IMAGE_FILE_MACHINE_I386)
     c.RelocModel = Reloc::Static;
   else
     c.RelocModel = Reloc::PIC_;
@@ -84,42 +85,42 @@ static lto::Config createConfig() {
   c.DisableVerify = true;
 #endif
   c.DiagHandler = diagnosticHandler;
-  c.OptLevel = config->ltoo;
+  c.OptLevel = ctx.config.ltoo;
   c.CPU = getCPUStr();
   c.MAttrs = getMAttrs();
-  c.CGOptLevel = args::getCGOptLevel(config->ltoo);
-  c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty();
-  c.DebugPassManager = config->ltoDebugPassManager;
-  c.CSIRProfile = std::string(config->ltoCSProfileFile);
-  c.RunCSIRInstr = config->ltoCSProfileGenerate;
-  c.PGOWarnMismatch = config->ltoPGOWarnMismatch;
-
-  if (config->saveTemps)
-    checkError(c.addSaveTemps(std::string(config->outputFile) + ".",
+  c.CGOptLevel = args::getCGOptLevel(ctx.config.ltoo);
+  c.AlwaysEmitRegularLTOObj = !ctx.config.ltoObjPath.empty();
+  c.DebugPassManager = ctx.config.ltoDebugPassManager;
+  c.CSIRProfile = std::string(ctx.config.ltoCSProfileFile);
+  c.RunCSIRInstr = ctx.config.ltoCSProfileGenerate;
+  c.PGOWarnMismatch = ctx.config.ltoPGOWarnMismatch;
+
+  if (ctx.config.saveTemps)
+    checkError(c.addSaveTemps(std::string(ctx.config.outputFile) + ".",
                               /*UseInputModulePath*/ true));
   return c;
 }
 
-BitcodeCompiler::BitcodeCompiler() {
+BitcodeCompiler::BitcodeCompiler(COFFLinkerContext &c) : ctx(c) {
   // Initialize indexFile.
-  if (!config->thinLTOIndexOnlyArg.empty())
-    indexFile = openFile(config->thinLTOIndexOnlyArg);
+  if (!ctx.config.thinLTOIndexOnlyArg.empty())
+    indexFile = openFile(ctx.config.thinLTOIndexOnlyArg);
 
   // Initialize ltoObj.
   lto::ThinBackend backend;
-  if (config->thinLTOIndexOnly) {
+  if (ctx.config.thinLTOIndexOnly) {
     auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
     backend = lto::createWriteIndexesThinBackend(
-        std::string(config->thinLTOPrefixReplace.first),
-        std::string(config->thinLTOPrefixReplace.second),
-        config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite);
+        std::string(ctx.config.thinLTOPrefixReplace.first),
+        std::string(ctx.config.thinLTOPrefixReplace.second),
+        ctx.config.thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite);
   } else {
     backend = lto::createInProcessThinBackend(
-        llvm::heavyweight_hardware_concurrency(config->thinLTOJobs));
+        llvm::heavyweight_hardware_concurrency(ctx.config.thinLTOJobs));
   }
 
   ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
-                                       config->ltoPartitions);
+                                      ctx.config.ltoPartitions);
 }
 
 BitcodeCompiler::~BitcodeCompiler() = default;
@@ -132,7 +133,7 @@ void BitcodeCompiler::add(BitcodeFile &f) {
   std::vector<Symbol *> symBodies = f.getSymbols();
   std::vector<lto::SymbolResolution> resols(symBodies.size());
 
-  if (config->thinLTOIndexOnly)
+  if (ctx.config.thinLTOIndexOnly)
     thinIndices.insert(obj.getName());
 
   // Provide a resolution to the LTO API for each symbol.
@@ -161,7 +162,7 @@ void BitcodeCompiler::add(BitcodeFile &f) {
 
 // Merge all the bitcode files we have seen, codegen the result
 // and return the resulting objects.
-std::vector<InputFile *> BitcodeCompiler::compile(COFFLinkerContext &ctx) {
+std::vector<InputFile *> BitcodeCompiler::compile() {
   unsigned maxTasks = ltoObj->getMaxTasks();
   buf.resize(maxTasks);
   files.resize(maxTasks);
@@ -171,8 +172,8 @@ std::vector<InputFile *> BitcodeCompiler::compile(COFFLinkerContext &ctx) {
   // native object files for ThinLTO incremental builds. If a path was
   // specified, configure LTO to use it as the cache directory.
   FileCache cache;
-  if (!config->ltoCache.empty())
-    cache = check(localCache("ThinLTO", "Thin", config->ltoCache,
+  if (!ctx.config.ltoCache.empty())
+    cache = check(localCache("ThinLTO", "Thin", ctx.config.ltoCache,
                              [&](size_t task, const Twine &moduleName,
                                  std::unique_ptr<MemoryBuffer> mb) {
                                files[task] = std::move(mb);
@@ -191,23 +192,23 @@ std::vector<InputFile *> BitcodeCompiler::compile(COFFLinkerContext &ctx) {
   for (StringRef s : thinIndices) {
     std::string path = getThinLTOOutputFile(s);
     openFile(path + ".thinlto.bc");
-    if (config->thinLTOEmitImportsFiles)
+    if (ctx.config.thinLTOEmitImportsFiles)
       openFile(path + ".imports");
   }
 
   // ThinLTO with index only option is required to generate only the index
   // files. After that, we exit from linker and ThinLTO backend runs in a
   // distributed environment.
-  if (config->thinLTOIndexOnly) {
-    if (!config->ltoObjPath.empty())
-      saveBuffer(buf[0].second, config->ltoObjPath);
+  if (ctx.config.thinLTOIndexOnly) {
+    if (!ctx.config.ltoObjPath.empty())
+      saveBuffer(buf[0].second, ctx.config.ltoObjPath);
     if (indexFile)
       indexFile->close();
     return {};
   }
 
-  if (!config->ltoCache.empty())
-    pruneCache(config->ltoCache, config->ltoCachePolicy, files);
+  if (!ctx.config.ltoCache.empty())
+    pruneCache(ctx.config.ltoCache, ctx.config.ltoCachePolicy, files);
 
   std::vector<InputFile *> ret;
   for (unsigned i = 0; i != maxTasks; ++i) {
@@ -231,19 +232,19 @@ std::vector<InputFile *> BitcodeCompiler::compile(COFFLinkerContext &ctx) {
     StringRef ltoObjName;
     if (bitcodeFilePath == "ld-temp.o") {
       ltoObjName =
-          saver().save(Twine(config->outputFile) + ".lto" +
+          saver().save(Twine(ctx.config.outputFile) + ".lto" +
                        (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj");
     } else {
       StringRef directory = sys::path::parent_path(bitcodeFilePath);
       StringRef baseName = sys::path::filename(bitcodeFilePath);
-      StringRef outputFileBaseName = sys::path::filename(config->outputFile);
+      StringRef outputFileBaseName = sys::path::filename(ctx.config.outputFile);
       SmallString<64> path;
       sys::path::append(path, directory,
                         outputFileBaseName + ".lto." + baseName);
       sys::path::remove_dots(path, true);
       ltoObjName = saver().save(path.str());
     }
-    if (config->saveTemps)
+    if (ctx.config.saveTemps)
       saveBuffer(buf[i].second, ltoObjName);
     ret.push_back(make<ObjFile>(ctx, MemoryBufferRef(objBuf, ltoObjName)));
   }

diff  --git a/lld/COFF/LTO.h b/lld/COFF/LTO.h
index 9ef304005221..6826251b5ffa 100644
--- a/lld/COFF/LTO.h
+++ b/lld/COFF/LTO.h
@@ -28,6 +28,7 @@
 #include <vector>
 
 namespace llvm::lto {
+struct Config;
 class LTO;
 }
 
@@ -39,11 +40,11 @@ class COFFLinkerContext;
 
 class BitcodeCompiler {
 public:
-  BitcodeCompiler();
+  BitcodeCompiler(COFFLinkerContext &ctx);
   ~BitcodeCompiler();
 
   void add(BitcodeFile &f);
-  std::vector<InputFile *> compile(COFFLinkerContext &ctx);
+  std::vector<InputFile *> compile();
 
 private:
   std::unique_ptr<llvm::lto::LTO> ltoObj;
@@ -52,6 +53,11 @@ class BitcodeCompiler {
   std::vector<std::string> file_names;
   std::unique_ptr<llvm::raw_fd_ostream> indexFile;
   llvm::DenseSet<StringRef> thinIndices;
+
+  std::string getThinLTOOutputFile(StringRef path);
+  llvm::lto::Config createConfig();
+
+  COFFLinkerContext &ctx;
 };
 }
 

diff  --git a/lld/COFF/MapFile.cpp b/lld/COFF/MapFile.cpp
index 797d22c8aaa8..f7a4ef961290 100644
--- a/lld/COFF/MapFile.cpp
+++ b/lld/COFF/MapFile.cpp
@@ -63,7 +63,8 @@ static void writeFormattedTimestamp(raw_ostream &os, time_t tds) {
                time->tm_sec, time->tm_year + 1900);
 }
 
-static void sortUniqueSymbols(std::vector<Defined *> &syms) {
+static void sortUniqueSymbols(std::vector<Defined *> &syms,
+                              uint64_t imageBase) {
   // Build helper vector
   using SortEntry = std::pair<Defined *, size_t>;
   std::vector<SortEntry> v;
@@ -80,11 +81,11 @@ static void sortUniqueSymbols(std::vector<Defined *> &syms) {
   v.erase(end, v.end());
 
   // Sort by RVA then original order
-  parallelSort(v, [](const SortEntry &a, const SortEntry &b) {
-    // Add config->imageBase to avoid comparing "negative" RVAs.
+  parallelSort(v, [imageBase](const SortEntry &a, const SortEntry &b) {
+    // Add config.imageBase to avoid comparing "negative" RVAs.
     // This can happen with symbols of Absolute kind
-    uint64_t rvaa = config->imageBase + a.first->getRVA();
-    uint64_t rvab = config->imageBase + b.first->getRVA();
+    uint64_t rvaa = imageBase + a.first->getRVA();
+    uint64_t rvab = imageBase + b.first->getRVA();
     return rvaa < rvab || (rvaa == rvab && a.second < b.second);
   });
 
@@ -133,8 +134,8 @@ static void getSymbols(const COFFLinkerContext &ctx,
       syms.push_back(impSym);
   }
 
-  sortUniqueSymbols(syms);
-  sortUniqueSymbols(staticSyms);
+  sortUniqueSymbols(syms, ctx.config.imageBase);
+  sortUniqueSymbols(staticSyms, ctx.config.imageBase);
 }
 
 // Construct a map from symbols to their stringified representations.
@@ -184,7 +185,7 @@ getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
     os << "       ";
     os << left_justify(sym->getName(), 26);
     os << " ";
-    os << format_hex_no_prefix((config->imageBase + sym->getRVA()), 16);
+    os << format_hex_no_prefix((ctx.config.imageBase + sym->getRVA()), 16);
     if (!fileDescr.empty()) {
       os << "     "; // FIXME : Handle "f" and "i" flags sometimes generated
                      // by link.exe in those spaces
@@ -199,13 +200,13 @@ getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
 }
 
 void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
-  if (config->mapFile.empty())
+  if (ctx.config.mapFile.empty())
     return;
 
   std::error_code ec;
-  raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);
+  raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None);
   if (ec)
-    fatal("cannot open " + config->mapFile + ": " + ec.message());
+    fatal("cannot open " + ctx.config.mapFile + ": " + ec.message());
 
   ScopedTimer t1(ctx.totalMapTimer);
 
@@ -223,24 +224,25 @@ void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
   t3.stop();
 
   ScopedTimer t4(ctx.writeTimer);
-  SmallString<128> AppName = sys::path::filename(config->outputFile);
+  SmallString<128> AppName = sys::path::filename(ctx.config.outputFile);
   sys::path::replace_extension(AppName, "");
 
   // Print out the file header
   os << " " << AppName << "\n";
   os << "\n";
 
-  os << " Timestamp is " << format_hex_no_prefix(config->timestamp, 8) << " (";
-  if (config->repro) {
+  os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8)
+     << " (";
+  if (ctx.config.repro) {
     os << "Repro mode";
   } else {
-    writeFormattedTimestamp(os, config->timestamp);
+    writeFormattedTimestamp(os, ctx.config.timestamp);
   }
   os << ")\n";
 
   os << "\n";
   os << " Preferred load address is "
-     << format_hex_no_prefix(config->imageBase, 16) << "\n";
+     << format_hex_no_prefix(ctx.config.imageBase, 16) << "\n";
   os << "\n";
 
   // Print out section table.
@@ -295,8 +297,8 @@ void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
   uint16_t entrySecIndex = 0;
   uint64_t entryAddress = 0;
 
-  if (!config->noEntry) {
-    Defined *entry = dyn_cast_or_null<Defined>(config->entry);
+  if (!ctx.config.noEntry) {
+    Defined *entry = dyn_cast_or_null<Defined>(ctx.config.entry);
     if (entry) {
       Chunk *chunk = entry->getChunk();
       entrySecIndex = chunk->getOutputSectionIdx();
@@ -316,12 +318,12 @@ void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
     os << staticSymStr[sym] << '\n';
 
   // Print out the exported functions
-  if (config->mapInfo) {
+  if (ctx.config.mapInfo) {
     os << "\n";
     os << " Exports\n";
     os << "\n";
     os << "  ordinal    name\n\n";
-    for (Export &e : config->exports) {
+    for (Export &e : ctx.config.exports) {
       os << format("  %7d", e.ordinal) << "    " << e.name << "\n";
       if (!e.extName.empty() && e.extName != e.name)
         os << "               exported name: " << e.extName << "\n";

diff  --git a/lld/COFF/MarkLive.cpp b/lld/COFF/MarkLive.cpp
index 89a3394d7d03..ad8c340f1845 100644
--- a/lld/COFF/MarkLive.cpp
+++ b/lld/COFF/MarkLive.cpp
@@ -51,7 +51,7 @@ void markLive(COFFLinkerContext &ctx) {
   };
 
   // Add GC root chunks.
-  for (Symbol *b : config->gcroot)
+  for (Symbol *b : ctx.config.gcroot)
     addSym(b);
 
   while (!worklist.empty()) {

diff  --git a/lld/COFF/MinGW.cpp b/lld/COFF/MinGW.cpp
index 0689e44cc363..01372cbdf29a 100644
--- a/lld/COFF/MinGW.cpp
+++ b/lld/COFF/MinGW.cpp
@@ -24,8 +24,9 @@ using namespace lld;
 using namespace lld::coff;
 
 AutoExporter::AutoExporter(
+    COFFLinkerContext &ctx,
     const llvm::DenseSet<StringRef> &manualExcludeSymbols)
-    : manualExcludeSymbols(manualExcludeSymbols) {
+    : manualExcludeSymbols(manualExcludeSymbols), ctx(ctx) {
   excludeLibs = {
       "libgcc",
       "libgcc_s",
@@ -80,7 +81,7 @@ AutoExporter::AutoExporter(
       "_NULL_THUNK_DATA",
   };
 
-  if (config->machine == I386) {
+  if (ctx.config.machine == I386) {
     excludeSymbols = {
         "__NULL_IMPORT_DESCRIPTOR",
         "__pei386_runtime_relocator",
@@ -128,8 +129,7 @@ void AutoExporter::addExcludedSymbol(StringRef symbol) {
   excludeSymbols.insert(symbol);
 }
 
-bool AutoExporter::shouldExport(const COFFLinkerContext &ctx,
-                                Defined *sym) const {
+bool AutoExporter::shouldExport(Defined *sym) const {
   if (!sym || !sym->getChunk())
     return false;
 
@@ -167,14 +167,15 @@ bool AutoExporter::shouldExport(const COFFLinkerContext &ctx,
   return !excludeObjects.count(fileName);
 }
 
-void lld::coff::writeDefFile(StringRef name) {
+void lld::coff::writeDefFile(StringRef name,
+                             const std::vector<Export> &exports) {
   std::error_code ec;
   raw_fd_ostream os(name, ec, sys::fs::OF_None);
   if (ec)
     fatal("cannot open " + name + ": " + ec.message());
 
   os << "EXPORTS\n";
-  for (Export &e : config->exports) {
+  for (const Export &e : exports) {
     os << "    " << e.exportName << " "
        << "@" << e.ordinal;
     if (auto *def = dyn_cast_or_null<Defined>(e.sym)) {
@@ -186,9 +187,9 @@ void lld::coff::writeDefFile(StringRef name) {
   }
 }
 
-static StringRef mangle(Twine sym) {
-  assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN);
-  if (config->machine == I386)
+static StringRef mangle(Twine sym, MachineTypes machine) {
+  assert(machine != IMAGE_FILE_MACHINE_UNKNOWN);
+  if (machine == I386)
     return saver().save("_" + sym);
   return saver().save(sym);
 }
@@ -212,8 +213,10 @@ lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
     if (!sym)
       continue;
 
-    Symbol *real = ctx.symtab.addUndefined(mangle("__real_" + name));
-    Symbol *wrap = ctx.symtab.addUndefined(mangle("__wrap_" + name));
+    Symbol *real =
+        ctx.symtab.addUndefined(mangle("__real_" + name, ctx.config.machine));
+    Symbol *wrap =
+        ctx.symtab.addUndefined(mangle("__wrap_" + name, ctx.config.machine));
     v.push_back({sym, real, wrap});
 
     // These symbols may seem undefined initially, but don't bail out
@@ -254,7 +257,7 @@ void lld::coff::wrapSymbols(COFFLinkerContext &ctx,
       // referenced it or not, though.)
       if (imp) {
         DefinedLocalImport *wrapimp = make<DefinedLocalImport>(
-            saver().save("__imp_" + w.wrap->getName()), d);
+            ctx, saver().save("__imp_" + w.wrap->getName()), d);
         ctx.symtab.localImportChunks.push_back(wrapimp->getChunk());
         map[imp] = wrapimp;
       }

diff  --git a/lld/COFF/MinGW.h b/lld/COFF/MinGW.h
index 113cd8327d28..aa5e53278ca4 100644
--- a/lld/COFF/MinGW.h
+++ b/lld/COFF/MinGW.h
@@ -25,7 +25,8 @@ class COFFLinkerContext;
 // symbols for MinGW.
 class AutoExporter {
 public:
-  AutoExporter(const llvm::DenseSet<StringRef> &manualExcludeSymbols);
+  AutoExporter(COFFLinkerContext &ctx,
+               const llvm::DenseSet<StringRef> &manualExcludeSymbols);
 
   void addWholeArchive(StringRef path);
   void addExcludedSymbol(StringRef symbol);
@@ -38,10 +39,13 @@ class AutoExporter {
 
   const llvm::DenseSet<StringRef> &manualExcludeSymbols;
 
-  bool shouldExport(const COFFLinkerContext &ctx, Defined *sym) const;
+  bool shouldExport(Defined *sym) const;
+
+private:
+  COFFLinkerContext &ctx;
 };
 
-void writeDefFile(StringRef name);
+void writeDefFile(StringRef name, const std::vector<Export> &exports);
 
 // The -wrap option is a feature to rename symbols so that you can write
 // wrappers for existing functions. If you pass `-wrap:foo`, all

diff  --git a/lld/COFF/PDB.cpp b/lld/COFF/PDB.cpp
index ad5137019ceb..da91455398e0 100644
--- a/lld/COFF/PDB.cpp
+++ b/lld/COFF/PDB.cpp
@@ -67,8 +67,6 @@ using namespace lld::coff;
 using llvm::object::coff_section;
 using llvm::pdb::StringTableFixup;
 
-static ExitOnError exitOnErr;
-
 namespace {
 class DebugSHandler;
 
@@ -146,6 +144,11 @@ class PDBLinker {
   void printStats();
 
 private:
+  void pdbMakeAbsolute(SmallVectorImpl<char> &fileName);
+  void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
+                          TpiSource *source);
+  void addCommonLinkerModuleSymbols(StringRef path,
+                                    pdb::DbiModuleDescriptorBuilder &mod);
 
   pdb::PDBFileBuilder builder;
 
@@ -241,7 +244,7 @@ class DebugSHandler {
 // Visual Studio's debugger requires absolute paths in various places in the
 // PDB to work without additional configuration:
 // https://docs.microsoft.com/en-us/visualstudio/debugger/debug-source-files-common-properties-solution-property-pages-dialog-box
-static void pdbMakeAbsolute(SmallVectorImpl<char> &fileName) {
+void PDBLinker::pdbMakeAbsolute(SmallVectorImpl<char> &fileName) {
   // The default behavior is to produce paths that are valid within the context
   // of the machine that you perform the link on.  If the linker is running on
   // a POSIX system, we will output absolute POSIX paths.  If the linker is
@@ -256,7 +259,7 @@ static void pdbMakeAbsolute(SmallVectorImpl<char> &fileName) {
   // It's not absolute in any path syntax.  Relative paths necessarily refer to
   // the local file system, so we can make it native without ending up with a
   // nonsensical path.
-  if (config->pdbSourcePath.empty()) {
+  if (ctx.config.pdbSourcePath.empty()) {
     sys::path::native(fileName);
     sys::fs::make_absolute(fileName);
     sys::path::remove_dots(fileName, true);
@@ -267,7 +270,7 @@ static void pdbMakeAbsolute(SmallVectorImpl<char> &fileName) {
   // Since PDB's are more of a Windows thing, we make this conservative and only
   // decide that it's a unix path if we're fairly certain.  Specifically, if
   // it starts with a forward slash.
-  SmallString<128> absoluteFileName = config->pdbSourcePath;
+  SmallString<128> absoluteFileName = ctx.config.pdbSourcePath;
   sys::path::Style guessedStyle = absoluteFileName.startswith("/")
                                       ? sys::path::Style::posix
                                       : sys::path::Style::windows;
@@ -338,8 +341,8 @@ static SymbolKind symbolKind(ArrayRef<uint8_t> recordData) {
 }
 
 /// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32
-static void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
-                               TypeMerger &tMerger, TpiSource *source) {
+void PDBLinker::translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
+                                   TpiSource *source) {
   RecordPrefix *prefix = reinterpret_cast<RecordPrefix *>(recordData.data());
 
   SymbolKind kind = symbolKind(recordData);
@@ -370,7 +373,7 @@ static void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
     // in both cases we just need the second type index.
     if (!ti->isSimple() && !ti->isNoneType()) {
       TypeIndex newType = TypeIndex(SimpleTypeKind::NotTranslated);
-      if (config->debugGHashes) {
+      if (ctx.config.debugGHashes) {
         auto idToType = tMerger.funcIdToType.find(*ti);
         if (idToType != tMerger.funcIdToType.end())
           newType = idToType->second;
@@ -575,7 +578,7 @@ void PDBLinker::writeSymbolRecord(SectionChunk *debugChunk,
 
   // An object file may have S_xxx_ID symbols, but these get converted to
   // "real" symbols in a PDB.
-  translateIdSymbols(recordBytes, tMerger, source);
+  translateIdSymbols(recordBytes, source);
 }
 
 void PDBLinker::analyzeSymbolSubsection(
@@ -642,6 +645,7 @@ void PDBLinker::analyzeSymbolSubsection(
 
 Error PDBLinker::writeAllModuleSymbolRecords(ObjFile *file,
                                              BinaryStreamWriter &writer) {
+  ExitOnError exitOnErr;
   std::vector<uint8_t> storage;
   SmallVector<uint32_t, 4> scopes;
 
@@ -758,6 +762,7 @@ void DebugSHandler::handleDebugS(SectionChunk *debugChunk) {
   contents = SectionChunk::consumeDebugMagic(contents, ".debug$S");
   DebugSubsectionArray subsections;
   BinaryStreamReader reader(contents, support::little);
+  ExitOnError exitOnErr;
   exitOnErr(reader.readArray(subsections, contents.size()));
   debugChunk->sortRelocations();
 
@@ -867,6 +872,7 @@ Error UnrelocatedDebugSubsection::commit(BinaryStreamWriter &writer) const {
     TpiSource *source = debugChunk->file->debugTypesObj;
     DebugInlineeLinesSubsectionRef inlineeLines;
     BinaryStreamReader storageReader(relocatedBytes, support::little);
+    ExitOnError exitOnErr;
     exitOnErr(inlineeLines.initialize(storageReader));
     for (const InlineeSourceLine &line : inlineeLines) {
       TypeIndex &inlinee = *const_cast<TypeIndex *>(&line.Header->Inlinee);
@@ -935,6 +941,8 @@ void DebugSHandler::finish() {
     return;
   }
 
+  ExitOnError exitOnErr;
+
   // Handle FPO data. Each subsection begins with a single image base
   // relocation, which is then added to the RvaStart of each frame data record
   // when it is added to the PDB. The string table indices for the FPO program
@@ -984,7 +992,7 @@ void DebugSHandler::finish() {
   for (const FileChecksumEntry &fc : checksums) {
     SmallString<128> filename =
         exitOnErr(cvStrTab.getString(fc.FileNameOffset));
-    pdbMakeAbsolute(filename);
+    linker.pdbMakeAbsolute(filename);
     exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename));
     newChecksums->addChecksum(filename, fc.Kind, fc.Checksum);
   }
@@ -995,8 +1003,8 @@ void DebugSHandler::finish() {
   file.moduleDBI->addDebugSubsection(std::move(newChecksums));
 }
 
-static void warnUnusable(InputFile *f, Error e) {
-  if (!config->warnDebugInfoUnusable) {
+static void warnUnusable(InputFile *f, Error e, bool shouldWarn) {
+  if (!shouldWarn) {
     consumeError(std::move(e));
     return;
   }
@@ -1023,6 +1031,7 @@ void PDBLinker::addDebugSymbols(TpiSource *source) {
     return;
 
   ScopedTimer t(ctx.symbolMergingTimer);
+  ExitOnError exitOnErr;
   pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
   DebugSHandler dsh(*this, *source->file, source);
   // Now do all live .debug$S and .debug$F sections.
@@ -1064,6 +1073,7 @@ void PDBLinker::addDebugSymbols(TpiSource *source) {
 void PDBLinker::createModuleDBI(ObjFile *file) {
   pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
   SmallString<128> objName;
+  ExitOnError exitOnErr;
 
   bool inArchive = !file->parentName.empty();
   objName = inArchive ? file->parentName : file->getName();
@@ -1093,11 +1103,12 @@ void PDBLinker::addDebug(TpiSource *source) {
   // the PDB first, so that we can get the map from object file type and item
   // indices to PDB type and item indices.  If we are using ghashes, types have
   // already been merged.
-  if (!config->debugGHashes) {
+  if (!ctx.config.debugGHashes) {
     ScopedTimer t(ctx.typeMergingTimer);
     if (Error e = source->mergeDebugT(&tMerger)) {
       // If type merging failed, ignore the symbols.
-      warnUnusable(source->file, std::move(e));
+      warnUnusable(source->file, std::move(e),
+                   ctx.config.warnDebugInfoUnusable);
       return;
     }
   }
@@ -1105,7 +1116,8 @@ void PDBLinker::addDebug(TpiSource *source) {
   // If type merging failed, ignore the symbols.
   Error typeError = std::move(source->typeMergingError);
   if (typeError) {
-    warnUnusable(source->file, std::move(typeError));
+    warnUnusable(source->file, std::move(typeError),
+                 ctx.config.warnDebugInfoUnusable);
     return;
   }
 
@@ -1146,7 +1158,7 @@ void PDBLinker::addObjectsToPDB() {
   tMerger.sortDependencies();
 
   // Merge type information from input files using global type hashing.
-  if (config->debugGHashes)
+  if (ctx.config.debugGHashes)
     tMerger.mergeTypesWithGHash();
 
   // Merge dependencies and then regular objects.
@@ -1160,8 +1172,9 @@ void PDBLinker::addObjectsToPDB() {
 
   // Construct TPI and IPI stream contents.
   ScopedTimer t2(ctx.tpiStreamLayoutTimer);
+
   // Collect all the merged types.
-  if (config->debugGHashes) {
+  if (ctx.config.debugGHashes) {
     addGHashTypeInfo(ctx, builder);
   } else {
     addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable());
@@ -1169,7 +1182,7 @@ void PDBLinker::addObjectsToPDB() {
   }
   t2.stop();
 
-  if (config->showSummary) {
+  if (ctx.config.showSummary) {
     for (TpiSource *source : ctx.tpiSourceList) {
       nbTypeRecords += source->nbTypeRecords;
       nbTypeRecordsBytes += source->nbTypeRecordsBytes;
@@ -1196,7 +1209,7 @@ void PDBLinker::addPublicsToPDB() {
       StringRef name = def->getName();
       if (name.data()[0] == '_' && name.data()[1] == '_') {
         // Drop the '_' prefix for x86.
-        if (config->machine == I386)
+        if (ctx.config.machine == I386)
           name = name.drop_front(1);
         if (name.startswith("__profd_") || name.startswith("__profc_") ||
             name.startswith("__covrec_")) {
@@ -1214,7 +1227,7 @@ void PDBLinker::addPublicsToPDB() {
 }
 
 void PDBLinker::printStats() {
-  if (!config->showSummary)
+  if (!ctx.config.showSummary)
     return;
 
   SmallString<256> buffer;
@@ -1282,11 +1295,11 @@ void PDBLinker::printStats() {
           << "Run llvm-pdbutil to print details about a particular record:\n";
       stream << formatv("llvm-pdbutil dump -{0}s -{0}-index {1:X} {2}\n",
                         (name == "TPI" ? "type" : "id"),
-                        tsis.back().typeIndex.getIndex(), config->pdbPath);
+                        tsis.back().typeIndex.getIndex(), ctx.config.pdbPath);
     }
   };
 
-  if (!config->debugGHashes) {
+  if (!ctx.config.debugGHashes) {
     // FIXME: Reimplement for ghash.
     printLargeInputTypeRecs("TPI", tMerger.tpiCounts, tMerger.getTypeTable());
     printLargeInputTypeRecs("IPI", tMerger.ipiCounts, tMerger.getIDTable());
@@ -1296,7 +1309,7 @@ void PDBLinker::printStats() {
 }
 
 void PDBLinker::addNatvisFiles() {
-  for (StringRef file : config->natvisFiles) {
+  for (StringRef file : ctx.config.natvisFiles) {
     ErrorOr<std::unique_ptr<MemoryBuffer>> dataOrErr =
         MemoryBuffer::getFile(file);
     if (!dataOrErr) {
@@ -1306,16 +1319,17 @@ void PDBLinker::addNatvisFiles() {
     std::unique_ptr<MemoryBuffer> data = std::move(*dataOrErr);
 
     // Can't use takeBuffer() here since addInjectedSource() takes ownership.
-    if (driver->tar)
-      driver->tar->append(relativeToRoot(data->getBufferIdentifier()),
-                          data->getBuffer());
+    if (ctx.driver.tar)
+      ctx.driver.tar->append(relativeToRoot(data->getBufferIdentifier()),
+                             data->getBuffer());
 
     builder.addInjectedSource(file, std::move(data));
   }
 }
 
 void PDBLinker::addNamedStreams() {
-  for (const auto &streamFile : config->namedStreams) {
+  ExitOnError exitOnErr;
+  for (const auto &streamFile : ctx.config.namedStreams) {
     const StringRef stream = streamFile.getKey(), file = streamFile.getValue();
     ErrorOr<std::unique_ptr<MemoryBuffer>> dataOrErr =
         MemoryBuffer::getFile(file);
@@ -1325,7 +1339,7 @@ void PDBLinker::addNamedStreams() {
     }
     std::unique_ptr<MemoryBuffer> data = std::move(*dataOrErr);
     exitOnErr(builder.addNamedStream(stream, data->getBuffer()));
-    driver->takeBuffer(std::move(data));
+    ctx.driver.takeBuffer(std::move(data));
   }
 }
 
@@ -1372,8 +1386,8 @@ static std::string quote(ArrayRef<StringRef> args) {
   return r;
 }
 
-static void fillLinkerVerRecord(Compile3Sym &cs) {
-  cs.Machine = toCodeViewMachine(config->machine);
+static void fillLinkerVerRecord(Compile3Sym &cs, MachineTypes machine) {
+  cs.Machine = toCodeViewMachine(machine);
   // Interestingly, if we set the string to 0.0.0.0, then when trying to view
   // local variables WinDbg emits an error that private symbols are not present.
   // By setting this to a valid MSVC linker version string, local variables are
@@ -1398,27 +1412,27 @@ static void fillLinkerVerRecord(Compile3Sym &cs) {
   cs.setLanguage(SourceLanguage::Link);
 }
 
-static void addCommonLinkerModuleSymbols(StringRef path,
-                                         pdb::DbiModuleDescriptorBuilder &mod) {
+void PDBLinker::addCommonLinkerModuleSymbols(
+    StringRef path, pdb::DbiModuleDescriptorBuilder &mod) {
   ObjNameSym ons(SymbolRecordKind::ObjNameSym);
   EnvBlockSym ebs(SymbolRecordKind::EnvBlockSym);
   Compile3Sym cs(SymbolRecordKind::Compile3Sym);
-  fillLinkerVerRecord(cs);
+  fillLinkerVerRecord(cs, ctx.config.machine);
 
   ons.Name = "* Linker *";
   ons.Signature = 0;
 
-  ArrayRef<StringRef> args = makeArrayRef(config->argv).drop_front();
+  ArrayRef<StringRef> args = makeArrayRef(ctx.config.argv).drop_front();
   std::string argStr = quote(args);
   ebs.Fields.push_back("cwd");
   SmallString<64> cwd;
-  if (config->pdbSourcePath.empty())
+  if (ctx.config.pdbSourcePath.empty())
     sys::fs::current_path(cwd);
   else
-    cwd = config->pdbSourcePath;
+    cwd = ctx.config.pdbSourcePath;
   ebs.Fields.push_back(cwd);
   ebs.Fields.push_back("exe");
-  SmallString<64> exe = config->argv[0];
+  SmallString<64> exe = ctx.config.argv[0];
   pdbMakeAbsolute(exe);
   ebs.Fields.push_back(exe);
   ebs.Fields.push_back("pdb");
@@ -1461,7 +1475,7 @@ static void addLinkerModuleCoffGroup(PartialSection *sec,
 }
 
 static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
-                                         OutputSection &os) {
+                                         OutputSection &os, bool isMinGW) {
   SectionSym sym(SymbolRecordKind::SectionSym);
   sym.Alignment = 12; // 2^12 = 4KB
   sym.Characteristics = os.header.Characteristics;
@@ -1474,7 +1488,7 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
 
   // Skip COFF groups in MinGW because it adds a significant footprint to the
   // PDB, due to each function being in its own section
-  if (config->mingw)
+  if (isMinGW)
     return;
 
   // Output COFF groups for individual chunks of this section.
@@ -1488,6 +1502,7 @@ void PDBLinker::addImportFilesToPDB() {
   if (ctx.importFileInstances.empty())
     return;
 
+  ExitOnError exitOnErr;
   std::map<std::string, llvm::pdb::DbiModuleDescriptorBuilder *> dllToModuleDbi;
 
   for (ImportFile *file : ctx.importFileInstances) {
@@ -1534,7 +1549,7 @@ void PDBLinker::addImportFilesToPDB() {
     ons.Name = file->dllName;
     ons.Signature = 0;
 
-    fillLinkerVerRecord(cs);
+    fillLinkerVerRecord(cs, ctx.config.machine);
 
     ts.Name = thunk->getName();
     ts.Parent = 0;
@@ -1598,7 +1613,8 @@ void lld::coff::createPDB(COFFLinkerContext &ctx,
 }
 
 void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) {
-  exitOnErr(builder.initialize(config->pdbPageSize));
+  ExitOnError exitOnErr;
+  exitOnErr(builder.initialize(ctx.config.pdbPageSize));
 
   buildId->Signature.CVSignature = OMF::Signature::PDB70;
   // Signature is set to a hash of the PDB contents when the PDB is done.
@@ -1619,7 +1635,7 @@ void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) {
   pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
   dbiBuilder.setAge(buildId->PDB70.Age);
   dbiBuilder.setVersionHeader(pdb::PdbDbiV70);
-  dbiBuilder.setMachineType(config->machine);
+  dbiBuilder.setMachineType(ctx.config.machine);
   // Technically we are not link.exe 14.11, but there are known cases where
   // debugging tools on Windows expect Microsoft-specific version numbers or
   // they fail to work at all.  Since we know we produce PDBs that are
@@ -1628,9 +1644,10 @@ void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) {
 }
 
 void PDBLinker::addSections(ArrayRef<uint8_t> sectionTable) {
+  ExitOnError exitOnErr;
   // It's not entirely clear what this is, but the * Linker * module uses it.
   pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
-  nativePath = config->pdbPath;
+  nativePath = ctx.config.pdbPath;
   pdbMakeAbsolute(nativePath);
   uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath);
   auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *"));
@@ -1639,7 +1656,7 @@ void PDBLinker::addSections(ArrayRef<uint8_t> sectionTable) {
 
   // Add section contributions. They must be ordered by ascending RVA.
   for (OutputSection *os : ctx.outputSections) {
-    addLinkerModuleSectionSymbol(linkerModule, *os);
+    addLinkerModuleSectionSymbol(linkerModule, *os, ctx.config.mingw);
     for (Chunk *c : os->chunks) {
       pdb::SectionContrib sc =
           createSectionContrib(ctx, c, linkerModule.getModuleIndex());
@@ -1669,14 +1686,14 @@ void PDBLinker::commit(codeview::GUID *guid) {
   // Print an error and continue if PDB writing fails. This is done mainly so
   // the user can see the output of /time and /summary, which is very helpful
   // when trying to figure out why a PDB file is too large.
-  if (Error e = builder.commit(config->pdbPath, guid)) {
+  if (Error e = builder.commit(ctx.config.pdbPath, guid)) {
     checkError(std::move(e));
-    error("failed to write PDB file " + Twine(config->pdbPath));
+    error("failed to write PDB file " + Twine(ctx.config.pdbPath));
   }
 }
 
-static uint32_t getSecrelReloc() {
-  switch (config->machine) {
+static uint32_t getSecrelReloc(llvm::COFF::MachineTypes machine) {
+  switch (machine) {
   case AMD64:
     return COFF::IMAGE_REL_AMD64_SECREL;
   case I386:
@@ -1701,7 +1718,7 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr,
                           DebugLinesSubsectionRef &lines,
                           uint32_t &offsetInLinetable) {
   ExitOnError exitOnErr;
-  uint32_t secrelReloc = getSecrelReloc();
+  const uint32_t secrelReloc = getSecrelReloc(c->file->ctx.config.machine);
 
   for (SectionChunk *dbgC : c->file->getDebugChunks()) {
     if (dbgC->getSectionName() != ".debug$S")

diff  --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index b46e6719ca26..2ca7b82cac4e 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -53,20 +53,20 @@ void SymbolTable::addFile(InputFile *file) {
   }
 
   MachineTypes mt = file->getMachineType();
-  if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) {
-    config->machine = mt;
-    driver->addWinSysRootLibSearchPaths();
-  } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && config->machine != mt) {
+  if (ctx.config.machine == IMAGE_FILE_MACHINE_UNKNOWN) {
+    ctx.config.machine = mt;
+    ctx.driver.addWinSysRootLibSearchPaths();
+  } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && ctx.config.machine != mt) {
     error(toString(file) + ": machine type " + machineToStr(mt) +
-          " conflicts with " + machineToStr(config->machine));
+          " conflicts with " + machineToStr(ctx.config.machine));
     return;
   }
 
-  driver->parseDirectives(file);
+  ctx.driver.parseDirectives(file);
 }
 
-static void errorOrWarn(const Twine &s) {
-  if (config->forceUnresolved)
+static void errorOrWarn(const Twine &s, bool forceUnresolved) {
+  if (forceUnresolved)
     warn(s);
   else
     error(s);
@@ -143,7 +143,7 @@ getFileLine(const SectionChunk *c, uint32_t addr) {
   std::optional<std::pair<StringRef, uint32_t>> fileLine =
       getFileLineCodeView(c, addr);
   // If codeview didn't yield any result, check dwarf in MinGW mode.
-  if (!fileLine && config->mingw)
+  if (!fileLine && c->file->ctx.config.mingw)
     fileLine = getFileLineDwarf(c, addr);
   return fileLine;
 }
@@ -201,7 +201,7 @@ getSymbolLocations(ObjFile *file, uint32_t symIndex, size_t maxStrings) {
          << "\n>>>               ";
     os << toString(file);
     if (loc.sym)
-      os << ":(" << toString(*loc.sym) << ')';
+      os << ":(" << toString(file->ctx, *loc.sym) << ')';
   }
   return std::make_pair(symbolLocations, numLocations);
 }
@@ -236,10 +236,11 @@ struct UndefinedDiag {
   std::vector<File> files;
 };
 
-static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
+static void reportUndefinedSymbol(const COFFLinkerContext &ctx,
+                                  const UndefinedDiag &undefDiag) {
   std::string out;
   llvm::raw_string_ostream os(out);
-  os << "undefined symbol: " << toString(*undefDiag.sym);
+  os << "undefined symbol: " << toString(ctx, *undefDiag.sym);
 
   const size_t maxUndefReferences = 3;
   size_t numDisplayedRefs = 0, numRefs = 0;
@@ -255,7 +256,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
   }
   if (numDisplayedRefs < numRefs)
     os << "\n>>> referenced " << numRefs - numDisplayedRefs << " more times";
-  errorOrWarn(os.str());
+  errorOrWarn(os.str(), ctx.config.forceUnresolved);
 }
 
 void SymbolTable::loadMinGWSymbols() {
@@ -269,7 +270,7 @@ void SymbolTable::loadMinGWSymbols() {
 
     StringRef name = undef->getName();
 
-    if (config->machine == I386 && config->stdcallFixup) {
+    if (ctx.config.machine == I386 && ctx.config.stdcallFixup) {
       // Check if we can resolve an undefined decorated symbol by finding
       // the intended target as an undecorated symbol (only with a leading
       // underscore).
@@ -290,7 +291,7 @@ void SymbolTable::loadMinGWSymbols() {
         }
         // If it's lazy or already defined, hook it up as weak alias.
         if (l->isLazy() || isa<Defined>(l)) {
-          if (config->warnStdcallFixup)
+          if (ctx.config.warnStdcallFixup)
             warn("Resolving " + origName + " by linking to " + newName);
           else
             log("Resolving " + origName + " by linking to " + newName);
@@ -300,7 +301,7 @@ void SymbolTable::loadMinGWSymbols() {
       }
     }
 
-    if (config->autoImport) {
+    if (ctx.config.autoImport) {
       if (name.startswith("__imp_"))
         continue;
       // If we have an undefined symbol, but we have a lazy symbol we could
@@ -358,7 +359,7 @@ bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
   // for __imp_<name> instead, and drop the whole .refptr.<name> chunk.
   DefinedRegular *refptr =
       dyn_cast_or_null<DefinedRegular>(find((".refptr." + name).str()));
-  if (refptr && refptr->getChunk()->getSize() == config->wordsize) {
+  if (refptr && refptr->getChunk()->getSize() == ctx.config.wordsize) {
     SectionChunk *sc = dyn_cast_or_null<SectionChunk>(refptr->getChunk());
     if (sc && sc->getRelocs().size() == 1 && *sc->symbols().begin() == sym) {
       log("Replacing .refptr." + name + " with " + imp->getName());
@@ -383,12 +384,13 @@ static void reportProblemSymbols(
   if (undefs.empty() && (!localImports || localImports->empty()))
     return;
 
-  for (Symbol *b : config->gcroot) {
+  for (Symbol *b : ctx.config.gcroot) {
     if (undefs.count(b))
-      errorOrWarn("<root>: undefined symbol: " + toString(*b));
+      errorOrWarn("<root>: undefined symbol: " + toString(ctx, *b),
+                  ctx.config.forceUnresolved);
     if (localImports)
       if (Symbol *imp = localImports->lookup(b))
-        warn("<root>: locally defined symbol imported: " + toString(*imp) +
+        warn("<root>: locally defined symbol imported: " + toString(ctx, *imp) +
              " (defined in " + toString(imp->getFile()) + ") [LNK4217]");
   }
 
@@ -413,7 +415,7 @@ static void reportProblemSymbols(
       if (localImports)
         if (Symbol *imp = localImports->lookup(sym))
           warn(toString(file) +
-               ": locally defined symbol imported: " + toString(*imp) +
+               ": locally defined symbol imported: " + toString(ctx, *imp) +
                " (defined in " + toString(imp->getFile()) + ") [LNK4217]");
     }
   };
@@ -426,7 +428,7 @@ static void reportProblemSymbols(
       processFile(file, file->getSymbols());
 
   for (const UndefinedDiag &undefDiag : undefDiags)
-    reportUndefinedSymbol(undefDiag);
+    reportUndefinedSymbol(ctx, undefDiag);
 }
 
 void SymbolTable::reportUnresolvable() {
@@ -446,7 +448,7 @@ void SymbolTable::reportUnresolvable() {
     }
     if (name.contains("_PchSym_"))
       continue;
-    if (config->autoImport && impSymbol(name))
+    if (ctx.config.autoImport && impSymbol(name))
       continue;
     undefs.insert(sym);
   }
@@ -491,7 +493,7 @@ void SymbolTable::resolveRemainingUndefines() {
       Symbol *imp = find(name.substr(strlen("__imp_")));
       if (imp && isa<Defined>(imp)) {
         auto *d = cast<Defined>(imp);
-        replaceSymbol<DefinedLocalImport>(sym, name, d);
+        replaceSymbol<DefinedLocalImport>(sym, ctx, name, d);
         localImportChunks.push_back(cast<DefinedLocalImport>(sym)->getChunk());
         localImports[sym] = d;
         continue;
@@ -503,19 +505,19 @@ void SymbolTable::resolveRemainingUndefines() {
     if (name.contains("_PchSym_"))
       continue;
 
-    if (config->autoImport && handleMinGWAutomaticImport(sym, name))
+    if (ctx.config.autoImport && handleMinGWAutomaticImport(sym, name))
       continue;
 
     // Remaining undefined symbols are not fatal if /force is specified.
     // They are replaced with dummy defined symbols.
-    if (config->forceUnresolved)
-      replaceSymbol<DefinedAbsolute>(sym, name, 0);
+    if (ctx.config.forceUnresolved)
+      replaceSymbol<DefinedAbsolute>(sym, ctx, name, 0);
     undefs.insert(sym);
   }
 
   reportProblemSymbols(
-      ctx, undefs, config->warnLocallyDefinedImported ? &localImports : nullptr,
-      false);
+      ctx, undefs,
+      ctx.config.warnLocallyDefinedImported ? &localImports : nullptr, false);
 }
 
 std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
@@ -642,7 +644,7 @@ void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile,
                                   uint32_t newSectionOffset) {
   std::string msg;
   llvm::raw_string_ostream os(msg);
-  os << "duplicate symbol: " << toString(*existing);
+  os << "duplicate symbol: " << toString(ctx, *existing);
 
   DefinedRegular *d = dyn_cast<DefinedRegular>(existing);
   if (d && isa<ObjFile>(d->getFile())) {
@@ -654,7 +656,7 @@ void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile,
   os << getSourceLocation(newFile, newSc, newSectionOffset,
                           existing->getName());
 
-  if (config->forceMultiple)
+  if (ctx.config.forceMultiple)
     warn(os.str());
   else
     error(os.str());
@@ -664,7 +666,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
   auto [s, wasInserted] = insert(n, nullptr);
   s->isUsedInRegularObj = true;
   if (wasInserted || isa<Undefined>(s) || s->isLazy())
-    replaceSymbol<DefinedAbsolute>(s, n, sym);
+    replaceSymbol<DefinedAbsolute>(s, ctx, n, sym);
   else if (auto *da = dyn_cast<DefinedAbsolute>(s)) {
     if (da->getVA() != sym.getValue())
       reportDuplicate(s, nullptr);
@@ -677,7 +679,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) {
   auto [s, wasInserted] = insert(n, nullptr);
   s->isUsedInRegularObj = true;
   if (wasInserted || isa<Undefined>(s) || s->isLazy())
-    replaceSymbol<DefinedAbsolute>(s, n, va);
+    replaceSymbol<DefinedAbsolute>(s, ctx, n, va);
   else if (auto *da = dyn_cast<DefinedAbsolute>(s)) {
     if (da->getVA() != va)
       reportDuplicate(s, nullptr);
@@ -751,7 +753,7 @@ Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id,
   auto [s, wasInserted] = insert(name, nullptr);
   s->isUsedInRegularObj = true;
   if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
-    replaceSymbol<DefinedImportThunk>(s, name, id, machine);
+    replaceSymbol<DefinedImportThunk>(s, ctx, name, id, machine);
     return s;
   }
 
@@ -788,7 +790,7 @@ Symbol *SymbolTable::find(StringRef name) const {
 }
 
 Symbol *SymbolTable::findUnderscore(StringRef name) const {
-  if (config->machine == I386)
+  if (ctx.config.machine == I386)
     return find(("_" + name).str());
   return find(name);
 }
@@ -835,7 +837,7 @@ Symbol *SymbolTable::findMangle(StringRef name) {
   };
 
   // For non-x86, just look for C++ functions.
-  if (config->machine != I386)
+  if (ctx.config.machine != I386)
     return findByPrefix("?" + name + "@@Y");
 
   if (!name.startswith("_"))
@@ -862,10 +864,10 @@ void SymbolTable::compileBitcodeFiles() {
     return;
 
   ScopedTimer t(ctx.ltoTimer);
-  lto.reset(new BitcodeCompiler());
+  lto.reset(new BitcodeCompiler(ctx));
   for (BitcodeFile *f : ctx.bitcodeFileInstances)
     lto->add(*f);
-  for (InputFile *newObj : lto->compile(ctx)) {
+  for (InputFile *newObj : lto->compile()) {
     ObjFile *obj = cast<ObjFile>(newObj);
     obj->parse();
     ctx.objFileInstances.push_back(obj);

diff  --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h
index 15cb5334df48..33ed65c45807 100644
--- a/lld/COFF/SymbolTable.h
+++ b/lld/COFF/SymbolTable.h
@@ -47,7 +47,7 @@ class Symbol;
 // There is one add* function per symbol type.
 class SymbolTable {
 public:
-  SymbolTable(COFFLinkerContext &ctx) : ctx(ctx) {}
+  SymbolTable(COFFLinkerContext &c) : ctx(c) {}
 
   void addFile(InputFile *file);
 

diff  --git a/lld/COFF/Symbols.cpp b/lld/COFF/Symbols.cpp
index 5a038b38f8ba..c042386e0106 100644
--- a/lld/COFF/Symbols.cpp
+++ b/lld/COFF/Symbols.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Symbols.h"
+#include "COFFLinkerContext.h"
 #include "InputFiles.h"
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
@@ -27,14 +28,15 @@ static_assert(sizeof(SymbolUnion) <= 48,
               "symbols should be optimized for memory usage");
 
 // Returns a symbol name for an error message.
-static std::string maybeDemangleSymbol(StringRef symName) {
-  if (config->demangle) {
+static std::string maybeDemangleSymbol(const COFFLinkerContext &ctx,
+                                       StringRef symName) {
+  if (ctx.config.demangle) {
     std::string prefix;
     StringRef prefixless = symName;
     if (prefixless.consume_front("__imp_"))
       prefix = "__declspec(dllimport) ";
     StringRef demangleInput = prefixless;
-    if (config->machine == I386)
+    if (ctx.config.machine == I386)
       demangleInput.consume_front("_");
     std::string demangled = demangle(demangleInput.str());
     if (demangled != demangleInput)
@@ -43,11 +45,12 @@ static std::string maybeDemangleSymbol(StringRef symName) {
   }
   return std::string(symName);
 }
-std::string toString(coff::Symbol &b) {
-  return maybeDemangleSymbol(b.getName());
+std::string toString(const COFFLinkerContext &ctx, coff::Symbol &b) {
+  return maybeDemangleSymbol(ctx, b.getName());
 }
-std::string toCOFFString(const Archive::Symbol &b) {
-  return maybeDemangleSymbol(b.getName());
+std::string toCOFFString(const COFFLinkerContext &ctx,
+                         const Archive::Symbol &b) {
+  return maybeDemangleSymbol(ctx, b.getName());
 }
 
 namespace coff {
@@ -102,23 +105,24 @@ COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
   return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(sym));
 }
 
-uint16_t DefinedAbsolute::numOutputSections;
+uint64_t DefinedAbsolute::getRVA() { return va - ctx.config.imageBase; }
 
-static Chunk *makeImportThunk(DefinedImportData *s, uint16_t machine) {
+static Chunk *makeImportThunk(COFFLinkerContext &ctx, DefinedImportData *s,
+                              uint16_t machine) {
   if (machine == AMD64)
-    return make<ImportThunkChunkX64>(s);
+    return make<ImportThunkChunkX64>(ctx, s);
   if (machine == I386)
-    return make<ImportThunkChunkX86>(s);
+    return make<ImportThunkChunkX86>(ctx, s);
   if (machine == ARM64)
-    return make<ImportThunkChunkARM64>(s);
+    return make<ImportThunkChunkARM64>(ctx, s);
   assert(machine == ARMNT);
-  return make<ImportThunkChunkARM>(s);
+  return make<ImportThunkChunkARM>(ctx, s);
 }
 
-DefinedImportThunk::DefinedImportThunk(StringRef name, DefinedImportData *s,
-                                       uint16_t machine)
+DefinedImportThunk::DefinedImportThunk(COFFLinkerContext &ctx, StringRef name,
+                                       DefinedImportData *s, uint16_t machine)
     : Defined(DefinedImportThunkKind, name), wrappedSym(s),
-      data(makeImportThunk(s, machine)) {}
+      data(makeImportThunk(ctx, s, machine)) {}
 
 Defined *Undefined::getWeakAlias() {
   // A weak alias may be a weak alias to another symbol, so check recursively.
@@ -130,11 +134,11 @@ Defined *Undefined::getWeakAlias() {
 
 MemoryBufferRef LazyArchive::getMemberBuffer() {
   Archive::Child c =
-    CHECK(sym.getMember(),
-          "could not get the member for symbol " + toCOFFString(sym));
+      CHECK(sym.getMember(), "could not get the member for symbol " +
+                                 toCOFFString(file->ctx, sym));
   return CHECK(c.getMemoryBufferRef(),
-      "could not get the buffer for the member defining symbol " +
-      toCOFFString(sym));
+               "could not get the buffer for the member defining symbol " +
+                   toCOFFString(file->ctx, sym));
 }
 } // namespace coff
 } // namespace lld

diff  --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h
index 7e99b02629df..750269fd0bbb 100644
--- a/lld/COFF/Symbols.h
+++ b/lld/COFF/Symbols.h
@@ -22,13 +22,6 @@
 
 namespace lld {
 
-std::string toString(coff::Symbol &b);
-
-// There are two 
diff erent ways to convert an Archive::Symbol to a string:
-// One for Microsoft name mangling and one for Itanium name mangling.
-// Call the functions toCOFFString and toELFString, not just toString.
-std::string toCOFFString(const coff::Archive::Symbol &b);
-
 namespace coff {
 
 using llvm::object::Archive;
@@ -37,6 +30,7 @@ using llvm::object::coff_import_header;
 using llvm::object::coff_symbol_generic;
 
 class ArchiveFile;
+class COFFLinkerContext;
 class InputFile;
 class ObjFile;
 class SymbolTable;
@@ -250,29 +244,25 @@ class DefinedCommon : public DefinedCOFF {
 // Absolute symbols.
 class DefinedAbsolute : public Defined {
 public:
-  DefinedAbsolute(StringRef n, COFFSymbolRef s)
-      : Defined(DefinedAbsoluteKind, n), va(s.getValue()) {
+  DefinedAbsolute(const COFFLinkerContext &c, StringRef n, COFFSymbolRef s)
+      : Defined(DefinedAbsoluteKind, n), va(s.getValue()), ctx(c) {
     isExternal = s.isExternal();
   }
 
-  DefinedAbsolute(StringRef n, uint64_t v)
-      : Defined(DefinedAbsoluteKind, n), va(v) {}
+  DefinedAbsolute(const COFFLinkerContext &c, StringRef n, uint64_t v)
+      : Defined(DefinedAbsoluteKind, n), va(v), ctx(c) {}
 
   static bool classof(const Symbol *s) {
     return s->kind() == DefinedAbsoluteKind;
   }
 
-  uint64_t getRVA() { return va - config->imageBase; }
+  uint64_t getRVA();
   void setVA(uint64_t v) { va = v; }
   uint64_t getVA() const { return va; }
 
-  // Section index relocations against absolute symbols resolve to
-  // this 16 bit number, and it is the largest valid section index
-  // plus one. This variable keeps it.
-  static uint16_t numOutputSections;
-
 private:
   uint64_t va;
+  const COFFLinkerContext &ctx;
 };
 
 // This symbol is used for linker-synthesized symbols like __ImageBase and
@@ -393,7 +383,8 @@ class DefinedImportData : public Defined {
 // a regular name. A function pointer is given as a DefinedImportData.
 class DefinedImportThunk : public Defined {
 public:
-  DefinedImportThunk(StringRef name, DefinedImportData *s, uint16_t machine);
+  DefinedImportThunk(COFFLinkerContext &ctx, StringRef name,
+                     DefinedImportData *s, uint16_t machine);
 
   static bool classof(const Symbol *s) {
     return s->kind() == DefinedImportThunkKind;
@@ -415,8 +406,9 @@ class DefinedImportThunk : public Defined {
 // This is here just for compatibility with MSVC.
 class DefinedLocalImport : public Defined {
 public:
-  DefinedLocalImport(StringRef n, Defined *s)
-      : Defined(DefinedLocalImportKind, n), data(make<LocalImportChunk>(s)) {}
+  DefinedLocalImport(COFFLinkerContext &ctx, StringRef n, Defined *s)
+      : Defined(DefinedLocalImportKind, n),
+        data(make<LocalImportChunk>(ctx, s)) {}
 
   static bool classof(const Symbol *s) {
     return s->kind() == DefinedLocalImportKind;
@@ -511,6 +503,10 @@ void replaceSymbol(Symbol *s, ArgT &&... arg) {
 }
 } // namespace coff
 
+std::string toString(const coff::COFFLinkerContext &ctx, coff::Symbol &b);
+std::string toCOFFString(const coff::COFFLinkerContext &ctx,
+                         const llvm::object::Archive::Symbol &b);
+
 } // namespace lld
 
 #endif

diff  --git a/lld/COFF/TypeMerger.h b/lld/COFF/TypeMerger.h
index 17f019758e82..b4e3d6e7d9fd 100644
--- a/lld/COFF/TypeMerger.h
+++ b/lld/COFF/TypeMerger.h
@@ -32,13 +32,13 @@ class TypeMerger {
 
   /// Get the type table or the global type table if /DEBUG:GHASH is enabled.
   inline llvm::codeview::TypeCollection &getTypeTable() {
-    assert(!config->debugGHashes);
+    assert(!ctx.config.debugGHashes);
     return typeTable;
   }
 
   /// Get the ID table or the global ID table if /DEBUG:GHASH is enabled.
   inline llvm::codeview::TypeCollection &getIDTable() {
-    assert(!config->debugGHashes);
+    assert(!ctx.config.debugGHashes);
     return idTable;
   }
 

diff  --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index ccadc26d27e8..09cca5667a47 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -85,7 +85,7 @@ namespace {
 
 class DebugDirectoryChunk : public NonSectionChunk {
 public:
-  DebugDirectoryChunk(COFFLinkerContext &c,
+  DebugDirectoryChunk(const COFFLinkerContext &c,
                       const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,
                       bool writeRepro)
       : records(r), writeRepro(writeRepro), ctx(c) {}
@@ -99,7 +99,7 @@ class DebugDirectoryChunk : public NonSectionChunk {
 
     for (const std::pair<COFF::DebugType, Chunk *>& record : records) {
       Chunk *c = record.second;
-      OutputSection *os = ctx.getOutputSection(c);
+      const OutputSection *os = ctx.getOutputSection(c);
       uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA());
       fillEntry(d, record.first, c->getSize(), c->getRVA(), offs);
       ++d;
@@ -138,14 +138,15 @@ class DebugDirectoryChunk : public NonSectionChunk {
   mutable std::vector<support::ulittle32_t *> timeDateStamps;
   const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;
   bool writeRepro;
-
-  COFFLinkerContext &ctx;
+  const COFFLinkerContext &ctx;
 };
 
 class CVDebugRecordChunk : public NonSectionChunk {
 public:
+  CVDebugRecordChunk(const COFFLinkerContext &c) : ctx(c) {}
+
   size_t getSize() const override {
-    return sizeof(codeview::DebugInfo) + config->pdbAltPath.size() + 1;
+    return sizeof(codeview::DebugInfo) + ctx.config.pdbAltPath.size() + 1;
   }
 
   void writeTo(uint8_t *b) const override {
@@ -155,12 +156,15 @@ class CVDebugRecordChunk : public NonSectionChunk {
 
     // variable sized field (PDB Path)
     char *p = reinterpret_cast<char *>(b + sizeof(*buildId));
-    if (!config->pdbAltPath.empty())
-      memcpy(p, config->pdbAltPath.data(), config->pdbAltPath.size());
-    p[config->pdbAltPath.size()] = '\0';
+    if (!ctx.config.pdbAltPath.empty())
+      memcpy(p, ctx.config.pdbAltPath.data(), ctx.config.pdbAltPath.size());
+    p[ctx.config.pdbAltPath.size()] = '\0';
   }
 
   mutable codeview::DebugInfo *buildId = nullptr;
+
+private:
+  const COFFLinkerContext &ctx;
 };
 
 class ExtendedDllCharacteristicsChunk : public NonSectionChunk {
@@ -195,7 +199,8 @@ class PartialSectionKey {
 // The writer writes a SymbolTable result to a file.
 class Writer {
 public:
-  Writer(COFFLinkerContext &c) : buffer(errorHandler().outputBuffer), ctx(c) {}
+  Writer(COFFLinkerContext &c)
+      : buffer(errorHandler().outputBuffer), delayIdata(c), edata(c), ctx(c) {}
   void run();
 
 private:
@@ -208,6 +213,12 @@ class Writer {
   void mergeSections();
   void removeUnusedSections();
   void assignAddresses();
+  bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin);
+  std::pair<Defined *, bool> getThunk(DenseMap<uint64_t, Defined *> &lastThunks,
+                                      Defined *target, uint64_t p,
+                                      uint16_t type, int margin);
+  bool createThunks(OutputSection *os, int margin);
+  bool verifyRanges(const std::vector<Chunk *> chunks);
   void finalizeAddresses();
   void removeEmptySections();
   void assignOutputSectionIndices();
@@ -217,6 +228,7 @@ class Writer {
   void createSEHTable();
   void createRuntimePseudoRelocs();
   void insertCtorDtorSymbols();
+  void markSymbolsWithRelocations(ObjFile *file, SymbolRVASet &usedSymbols);
   void createGuardCFTables();
   void markSymbolsForRVATable(ObjFile *file,
                               ArrayRef<SectionChunk *> symIdxChunks,
@@ -234,6 +246,7 @@ class Writer {
   void sortExceptionTable();
   void sortCRTSectionChunks(std::vector<Chunk *> &chunks);
   void addSyntheticIdata();
+  void sortBySectionOrder(std::vector<Chunk *> &chunks);
   void fixPartialSectionChars(StringRef name, uint32_t chars);
   bool fixGnuImportChunks();
   void fixTlsAlignment();
@@ -331,14 +344,14 @@ void OutputSection::merge(OutputSection *other) {
 }
 
 // Write the section header to a given buffer.
-void OutputSection::writeHeaderTo(uint8_t *buf) {
+void OutputSection::writeHeaderTo(uint8_t *buf, bool isDebug) {
   auto *hdr = reinterpret_cast<coff_section *>(buf);
   *hdr = header;
   if (stringTableOff) {
     // If name is too long, write offset into the string table as a name.
     encodeSectionName(hdr->Name, stringTableOff);
   } else {
-    assert(!config->debug || name.size() <= COFF::NameSize ||
+    assert(!isDebug || name.size() <= COFF::NameSize ||
            (hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0);
     strncpy(hdr->Name, name.data(),
             std::min(name.size(), (size_t)COFF::NameSize));
@@ -351,8 +364,8 @@ void OutputSection::addContributingPartialSection(PartialSection *sec) {
 
 // Check whether the target address S is in range from a relocation
 // of type relType at address P.
-static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
-  if (config->machine == ARMNT) {
+bool Writer::isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
+  if (ctx.config.machine == ARMNT) {
     int64_t 
diff  = AbsoluteDifference(s, p + 4) + margin;
     switch (relType) {
     case IMAGE_REL_ARM_BRANCH20T:
@@ -363,7 +376,7 @@ static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
     default:
       return true;
     }
-  } else if (config->machine == ARM64) {
+  } else if (ctx.config.machine == ARM64) {
     int64_t 
diff  = AbsoluteDifference(s, p) + margin;
     switch (relType) {
     case IMAGE_REL_ARM64_BRANCH26:
@@ -382,19 +395,19 @@ static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
 
 // Return the last thunk for the given target if it is in range,
 // or create a new one.
-static std::pair<Defined *, bool>
-getThunk(DenseMap<uint64_t, Defined *> &lastThunks, Defined *target, uint64_t p,
-         uint16_t type, int margin) {
+std::pair<Defined *, bool>
+Writer::getThunk(DenseMap<uint64_t, Defined *> &lastThunks, Defined *target,
+                 uint64_t p, uint16_t type, int margin) {
   Defined *&lastThunk = lastThunks[target->getRVA()];
   if (lastThunk && isInRange(type, lastThunk->getRVA(), p, margin))
     return {lastThunk, false};
   Chunk *c;
-  switch (config->machine) {
+  switch (ctx.config.machine) {
   case ARMNT:
-    c = make<RangeExtensionThunkARM>(target);
+    c = make<RangeExtensionThunkARM>(ctx, target);
     break;
   case ARM64:
-    c = make<RangeExtensionThunkARM64>(target);
+    c = make<RangeExtensionThunkARM64>(ctx, target);
     break;
   default:
     llvm_unreachable("Unexpected architecture");
@@ -415,7 +428,7 @@ getThunk(DenseMap<uint64_t, Defined *> &lastThunks, Defined *target, uint64_t p,
 // After adding thunks, we verify that all relocations are in range (with
 // no extra margin requirements). If this failed, we restart (throwing away
 // the previously created thunks) and retry with a wider margin.
-static bool createThunks(OutputSection *os, int margin) {
+bool Writer::createThunks(OutputSection *os, int margin) {
   bool addressesChanged = false;
   DenseMap<uint64_t, Defined *> lastThunks;
   DenseMap<std::pair<ObjFile *, Defined *>, uint32_t> thunkSymtabIndices;
@@ -511,7 +524,7 @@ static bool createThunks(OutputSection *os, int margin) {
 }
 
 // Verify that all relocations are in range, with no extra margin requirements.
-static bool verifyRanges(const std::vector<Chunk *> chunks) {
+bool Writer::verifyRanges(const std::vector<Chunk *> chunks) {
   for (Chunk *c : chunks) {
     SectionChunk *sc = dyn_cast_or_null<SectionChunk>(c);
     if (!sc)
@@ -539,7 +552,7 @@ static bool verifyRanges(const std::vector<Chunk *> chunks) {
 // Assign addresses and add thunks if necessary.
 void Writer::finalizeAddresses() {
   assignAddresses();
-  if (config->machine != ARMNT && config->machine != ARM64)
+  if (ctx.config.machine != ARMNT && ctx.config.machine != ARM64)
     return;
 
   size_t origNumChunks = 0;
@@ -600,7 +613,7 @@ void Writer::finalizeAddresses() {
 }
 
 void Writer::writePEChecksum() {
-  if (!config->writeCheckSum) {
+  if (!ctx.config.writeCheckSum) {
     return;
   }
 
@@ -658,8 +671,8 @@ void Writer::run() {
     fatal("image size (" + Twine(fileSize) + ") " +
         "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")");
 
-  openFile(config->outputFile);
-  if (config->is64()) {
+  openFile(ctx.config.outputFile);
+  if (ctx.config.is64()) {
     writeHeader<pe32plus_header>();
   } else {
     writeHeader<pe32_header>();
@@ -675,7 +688,7 @@ void Writer::run() {
 
   t1.stop();
 
-  if (!config->pdbPath.empty() && config->debug) {
+  if (!ctx.config.pdbPath.empty() && ctx.config.debug) {
     assert(buildId);
     createPDB(ctx, sectionTable, buildId->buildId);
   }
@@ -704,11 +717,11 @@ static StringRef getOutputSectionName(StringRef name) {
 }
 
 // For /order.
-static void sortBySectionOrder(std::vector<Chunk *> &chunks) {
-  auto getPriority = [](const Chunk *c) {
+void Writer::sortBySectionOrder(std::vector<Chunk *> &chunks) {
+  auto getPriority = [&ctx = ctx](const Chunk *c) {
     if (auto *sec = dyn_cast<SectionChunk>(c))
       if (sec->sym)
-        return config->order.lookup(sec->sym->getName());
+        return ctx.config.order.lookup(sec->sym->getName());
     return 0;
   };
 
@@ -787,7 +800,7 @@ bool Writer::fixGnuImportChunks() {
 // terminator in .idata$2.
 void Writer::addSyntheticIdata() {
   uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
-  idata.create();
+  idata.create(ctx);
 
   // Add the .idata content in the right section groups, to allow
   // chunks from other linked in object files to be grouped together.
@@ -830,7 +843,8 @@ void Writer::locateImportTables() {
 // Return whether a SectionChunk's suffix (the dollar and any trailing
 // suffix) should be removed and sorted into the main suffixless
 // PartialSection.
-static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) {
+static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name,
+                                     bool isMinGW) {
   // On MinGW, comdat groups are formed by putting the comdat group name
   // after the '$' in the section name. For .eh_frame$<symbol>, that must
   // still be sorted before the .eh_frame trailer from crtend.o, thus just
@@ -840,7 +854,7 @@ static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) {
   // hypothetical case of comdat .CRT$XCU, we definitely need to keep the
   // suffix for sorting. Thus, to play it safe, only strip the suffix for
   // the standard sections.
-  if (!config->mingw)
+  if (!isMinGW)
     return false;
   if (!sc || !sc->isCOMDAT())
     return false;
@@ -850,15 +864,15 @@ static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) {
 }
 
 void Writer::sortSections() {
-  if (!config->callGraphProfile.empty()) {
+  if (!ctx.config.callGraphProfile.empty()) {
     DenseMap<const SectionChunk *, int> order =
         computeCallGraphProfileOrder(ctx);
     for (auto it : order) {
       if (DefinedRegular *sym = it.first->sym)
-        config->order[sym->getName()] = it.second;
+        ctx.config.order[sym->getName()] = it.second;
     }
   }
-  if (!config->order.empty())
+  if (!ctx.config.order.empty())
     for (auto it : partialSections)
       sortBySectionOrder(it.second->chunks);
 }
@@ -903,12 +917,12 @@ void Writer::createSections() {
   for (Chunk *c : ctx.symtab.getChunks()) {
     auto *sc = dyn_cast<SectionChunk>(c);
     if (sc && !sc->live) {
-      if (config->verbose)
+      if (ctx.config.verbose)
         sc->printDiscardedMessage();
       continue;
     }
     StringRef name = c->getSectionName();
-    if (shouldStripSectionSuffix(sc, name))
+    if (shouldStripSectionSuffix(sc, name, ctx.config.mingw))
       name = name.split('$').first;
 
     if (name.startswith(".tls"))
@@ -990,6 +1004,8 @@ void Writer::createSections() {
 }
 
 void Writer::createMiscChunks() {
+  Configuration *config = &ctx.config;
+
   for (MergeChunk *p : ctx.mergeChunkInstances) {
     if (p) {
       p->finalizeContents();
@@ -1017,7 +1033,7 @@ void Writer::createMiscChunks() {
     // output a PDB no matter what, and this chunk provides the only means of
     // allowing a debugger to match a PDB and an executable.  So we need it even
     // if we're ultimately not going to write CodeView data to the PDB.
-    buildId = make<CVDebugRecordChunk>();
+    buildId = make<CVDebugRecordChunk>(ctx);
     debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId});
   }
 
@@ -1061,16 +1077,16 @@ void Writer::createImportTables() {
       continue;
 
     std::string dll = StringRef(file->dllName).lower();
-    if (config->dllOrder.count(dll) == 0)
-      config->dllOrder[dll] = config->dllOrder.size();
+    if (ctx.config.dllOrder.count(dll) == 0)
+      ctx.config.dllOrder[dll] = ctx.config.dllOrder.size();
 
     if (file->impSym && !isa<DefinedImportData>(file->impSym))
-      fatal(toString(*file->impSym) + " was replaced");
+      fatal(toString(ctx, *file->impSym) + " was replaced");
     DefinedImportData *impSym = cast_or_null<DefinedImportData>(file->impSym);
-    if (config->delayLoads.count(StringRef(file->dllName).lower())) {
+    if (ctx.config.delayLoads.count(StringRef(file->dllName).lower())) {
       if (!file->thunkSym)
         fatal("cannot delay-load " + toString(file) +
-              " due to import of data: " + toString(*impSym));
+              " due to import of data: " + toString(ctx, *impSym));
       delayIdata.add(impSym);
     } else {
       idata.add(impSym);
@@ -1090,15 +1106,15 @@ void Writer::appendImportThunks() {
       continue;
 
     if (!isa<DefinedImportThunk>(file->thunkSym))
-      fatal(toString(*file->thunkSym) + " was replaced");
+      fatal(toString(ctx, *file->thunkSym) + " was replaced");
     DefinedImportThunk *thunk = cast<DefinedImportThunk>(file->thunkSym);
     if (file->thunkLive)
       textSec->addChunk(thunk->getChunk());
   }
 
   if (!delayIdata.empty()) {
-    Defined *helper = cast<Defined>(config->delayLoadHelper);
-    delayIdata.create(ctx, helper);
+    Defined *helper = cast<Defined>(ctx.config.delayLoadHelper);
+    delayIdata.create(helper);
     for (Chunk *c : delayIdata.getChunks())
       didatSec->addChunk(c);
     for (Chunk *c : delayIdata.getDataChunks())
@@ -1112,9 +1128,9 @@ void Writer::createExportTable() {
   if (!edataSec->chunks.empty()) {
     // Allow using a custom built export table from input object files, instead
     // of having the linker synthesize the tables.
-    if (config->hadExplicitExports)
+    if (ctx.config.hadExplicitExports)
       warn("literal .edata sections override exports");
-  } else if (!config->exports.empty()) {
+  } else if (!ctx.config.exports.empty()) {
     for (Chunk *c : edata.chunks)
       edataSec->addChunk(c);
   }
@@ -1123,9 +1139,9 @@ void Writer::createExportTable() {
     edataEnd = edataSec->chunks.back();
   }
   // Warn on exported deleting destructor.
-  for (auto e : config->exports)
+  for (auto e : ctx.config.exports)
     if (e.sym && e.sym->getName().startswith("??_G"))
-      warn("export of deleting dtor: " + toString(*e.sym));
+      warn("export of deleting dtor: " + toString(ctx, *e.sym));
 }
 
 void Writer::removeUnusedSections() {
@@ -1252,7 +1268,7 @@ void Writer::createSymbolAndStringTable() {
       continue;
     if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0)
       continue;
-    if (config->warnLongSectionNames) {
+    if (ctx.config.warnLongSectionNames) {
       warn("section name " + sec->name +
            " is longer than 8 characters and will use a non-standard string "
            "table");
@@ -1260,7 +1276,7 @@ void Writer::createSymbolAndStringTable() {
     sec->setStringTableOff(addEntryToStringTable(sec->name));
   }
 
-  if (config->debugDwarf || config->debugSymtab) {
+  if (ctx.config.debugDwarf || ctx.config.debugSymtab) {
     for (ObjFile *file : ctx.objFileInstances) {
       for (Symbol *b : file->getSymbols()) {
         auto *d = dyn_cast_or_null<Defined>(b);
@@ -1297,7 +1313,7 @@ void Writer::createSymbolAndStringTable() {
   pointerToSymbolTable = fileOff;
   fileOff += outputSymtab.size() * sizeof(coff_symbol16);
   fileOff += 4 + strtab.size();
-  fileSize = alignTo(fileOff, config->fileAlign);
+  fileSize = alignTo(fileOff, ctx.config.fileAlign);
 }
 
 void Writer::mergeSections() {
@@ -1306,7 +1322,7 @@ void Writer::mergeSections() {
     lastPdata = pdataSec->chunks.back();
   }
 
-  for (auto &p : config->merge) {
+  for (auto &p : ctx.config.merge) {
     StringRef toName = p.second;
     if (p.first == toName)
       continue;
@@ -1314,8 +1330,8 @@ void Writer::mergeSections() {
     while (true) {
       if (!names.insert(toName).second)
         fatal("/merge: cycle found for section '" + p.first + "'");
-      auto i = config->merge.find(toName);
-      if (i == config->merge.end())
+      auto i = ctx.config.merge.find(toName);
+      if (i == ctx.config.merge.end())
         break;
       toName = i->second;
     }
@@ -1334,6 +1350,8 @@ void Writer::mergeSections() {
 // Visits all sections to assign incremental, non-overlapping RVAs and
 // file offsets.
 void Writer::assignAddresses() {
+  Configuration *config = &ctx.config;
+
   sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
                   sizeof(data_directory) * numberOfDataDirectory +
                   sizeof(coff_section) * ctx.outputSections.size();
@@ -1391,6 +1409,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
   // under DOS, that program gets run (usually to just print an error message).
   // When run under Windows, the loader looks at AddressOfNewExeHeader and uses
   // the PE header instead.
+  Configuration *config = &ctx.config;
   uint8_t *buf = buffer->getBufferStart();
   auto *dos = reinterpret_cast<dos_header *>(buf);
   buf += sizeof(dos_header);
@@ -1565,7 +1584,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
 
   // Write section table
   for (OutputSection *sec : ctx.outputSections) {
-    sec->writeHeaderTo(buf);
+    sec->writeHeaderTo(buf, config->debug);
     buf += sizeof(coff_section);
   }
   sectionTable = ArrayRef<uint8_t>(
@@ -1674,8 +1693,8 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms,
 
 // Visit all relocations from all section contributions of this object file and
 // mark the relocation target as address-taken.
-static void markSymbolsWithRelocations(ObjFile *file,
-                                       SymbolRVASet &usedSymbols) {
+void Writer::markSymbolsWithRelocations(ObjFile *file,
+                                        SymbolRVASet &usedSymbols) {
   for (Chunk *c : file->getChunks()) {
     // We only care about live section chunks. Common chunks and other chunks
     // don't generally contain relocations.
@@ -1684,7 +1703,8 @@ static void markSymbolsWithRelocations(ObjFile *file,
       continue;
 
     for (const coff_relocation &reloc : sc->getRelocs()) {
-      if (config->machine == I386 && reloc.Type == COFF::IMAGE_REL_I386_REL32)
+      if (ctx.config.machine == I386 &&
+          reloc.Type == COFF::IMAGE_REL_I386_REL32)
         // Ignore relative relocations on x86. On x86_64 they can't be ignored
         // since they're also used to compute absolute addresses.
         continue;
@@ -1699,6 +1719,8 @@ static void markSymbolsWithRelocations(ObjFile *file,
 // address-taken functions. It is sorted and uniqued, just like the safe SEH
 // table.
 void Writer::createGuardCFTables() {
+  Configuration *config = &ctx.config;
+
   SymbolRVASet addressTakenSyms;
   SymbolRVASet giatsRVASet;
   std::vector<Symbol *> giatsSymbols;
@@ -1862,7 +1884,7 @@ void Writer::createRuntimePseudoRelocs() {
     sc->getRuntimePseudoRelocs(rels);
   }
 
-  if (!config->pseudoRelocs) {
+  if (!ctx.config.pseudoRelocs) {
     // Not writing any pseudo relocs; if some were needed, error out and
     // indicate what required them.
     for (const RuntimePseudoReloc &rpr : rels)
@@ -1891,10 +1913,10 @@ void Writer::createRuntimePseudoRelocs() {
 // There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__
 // and __DTOR_LIST__ respectively.
 void Writer::insertCtorDtorSymbols() {
-  AbsolutePointerChunk *ctorListHead = make<AbsolutePointerChunk>(-1);
-  AbsolutePointerChunk *ctorListEnd = make<AbsolutePointerChunk>(0);
-  AbsolutePointerChunk *dtorListHead = make<AbsolutePointerChunk>(-1);
-  AbsolutePointerChunk *dtorListEnd = make<AbsolutePointerChunk>(0);
+  AbsolutePointerChunk *ctorListHead = make<AbsolutePointerChunk>(ctx, -1);
+  AbsolutePointerChunk *ctorListEnd = make<AbsolutePointerChunk>(ctx, 0);
+  AbsolutePointerChunk *dtorListHead = make<AbsolutePointerChunk>(ctx, -1);
+  AbsolutePointerChunk *dtorListEnd = make<AbsolutePointerChunk>(ctx, 0);
   ctorsSec->insertChunkAtStart(ctorListHead);
   ctorsSec->addChunk(ctorListEnd);
   dtorsSec->insertChunkAtStart(dtorListHead);
@@ -1911,7 +1933,7 @@ void Writer::insertCtorDtorSymbols() {
 // Handles /section options to allow users to overwrite
 // section attributes.
 void Writer::setSectionPermissions() {
-  for (auto &p : config->section) {
+  for (auto &p : ctx.config.section) {
     StringRef name = p.first;
     uint32_t perm = p.second;
     for (OutputSection *sec : ctx.outputSections)
@@ -1922,10 +1944,6 @@ void Writer::setSectionPermissions() {
 
 // Write section contents to a mmap'ed file.
 void Writer::writeSections() {
-  // Record the number of sections to apply section index relocations
-  // against absolute symbols. See applySecIdx in Chunks.cpp..
-  DefinedAbsolute::numOutputSections = ctx.outputSections.size();
-
   uint8_t *buf = buffer->getBufferStart();
   for (OutputSection *sec : ctx.outputSections) {
     uint8_t *secBuf = buf + sec->getFileOff();
@@ -1947,6 +1965,8 @@ void Writer::writeBuildId() {
   // 2) In all cases, the PE COFF file header also contains a timestamp.
   // For reproducibility, instead of a timestamp we want to use a hash of the
   // PE contents.
+  Configuration *config = &ctx.config;
+
   if (config->debug) {
     assert(buildId && "BuildId is not set!");
     // BuildId->BuildId was filled in when the PDB was written.
@@ -2003,7 +2023,7 @@ void Writer::sortExceptionTable() {
   };
   uint8_t *begin = bufAddr(firstPdata);
   uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize();
-  if (config->machine == AMD64) {
+  if (ctx.config.machine == AMD64) {
     struct Entry { ulittle32_t begin, end, unwind; };
     if ((end - begin) % sizeof(Entry) != 0) {
       fatal("unexpected .pdata size: " + Twine(end - begin) +
@@ -2014,7 +2034,7 @@ void Writer::sortExceptionTable() {
         [](const Entry &a, const Entry &b) { return a.begin < b.begin; });
     return;
   }
-  if (config->machine == ARMNT || config->machine == ARM64) {
+  if (ctx.config.machine == ARMNT || ctx.config.machine == ARM64) {
     struct Entry { ulittle32_t begin, unwind; };
     if ((end - begin) % sizeof(Entry) != 0) {
       fatal("unexpected .pdata size: " + Twine(end - begin) +
@@ -2055,7 +2075,7 @@ void Writer::sortCRTSectionChunks(std::vector<Chunk *> &chunks) {
   };
   llvm::stable_sort(chunks, sectionChunkOrder);
 
-  if (config->verbose) {
+  if (ctx.config.verbose) {
     for (auto &c : chunks) {
       auto sc = dyn_cast<SectionChunk>(c);
       log("  " + sc->file->mb.getBufferIdentifier().str() +
@@ -2081,7 +2101,7 @@ uint32_t Writer::getSizeOfInitializedData() {
 
 // Add base relocations to .reloc section.
 void Writer::addBaserels() {
-  if (!config->relocatable)
+  if (!ctx.config.relocatable)
     return;
   relocSec->chunks.clear();
   std::vector<Baserel> v;
@@ -2144,14 +2164,14 @@ void Writer::fixTlsAlignment() {
 
   uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff();
   uint64_t tlsOffset = tlsSym->getRVA() - sec->getRVA();
-  uint64_t directorySize = config->is64()
+  uint64_t directorySize = ctx.config.is64()
                                ? sizeof(object::coff_tls_directory64)
                                : sizeof(object::coff_tls_directory32);
 
   if (tlsOffset + directorySize > sec->getRawSize())
     fatal("_tls_used sym is malformed");
 
-  if (config->is64()) {
+  if (ctx.config.is64()) {
     object::coff_tls_directory64 *tlsDir =
         reinterpret_cast<object::coff_tls_directory64 *>(&secBuf[tlsOffset]);
     tlsDir->setAlignment(tlsAlignment);
@@ -2166,7 +2186,7 @@ void Writer::checkLoadConfig() {
   Symbol *sym = ctx.symtab.findUnderscore("_load_config_used");
   auto *b = cast_if_present<DefinedRegular>(sym);
   if (!b) {
-    if (config->guardCF != GuardCFLevel::Off)
+    if (ctx.config.guardCF != GuardCFLevel::Off)
       warn("Control Flow Guard is enabled but '_load_config_used' is missing");
     return;
   }
@@ -2175,7 +2195,7 @@ void Writer::checkLoadConfig() {
   uint8_t *buf = buffer->getBufferStart();
   uint8_t *secBuf = buf + sec->getFileOff();
   uint8_t *symBuf = secBuf + (b->getRVA() - sec->getRVA());
-  uint32_t expectedAlign = config->is64() ? 8 : 4;
+  uint32_t expectedAlign = ctx.config.is64() ? 8 : 4;
   if (b->getChunk()->getAlignment() < expectedAlign)
     warn("'_load_config_used' is misaligned (expected alignment to be " +
          Twine(expectedAlign) + " bytes, got " +
@@ -2185,7 +2205,7 @@ void Writer::checkLoadConfig() {
          Twine::utohexstr(b->getRVA()) + " not aligned to " +
          Twine(expectedAlign) + " bytes)");
 
-  if (config->is64())
+  if (ctx.config.is64())
     checkLoadConfigGuardData(
         reinterpret_cast<const coff_load_configuration64 *>(symBuf));
   else
@@ -2208,7 +2228,7 @@ void Writer::checkLoadConfigGuardData(const T *loadConfig) {
 
 #define CHECK_VA(field, sym)                                                   \
   if (auto *s = dyn_cast<DefinedSynthetic>(ctx.symtab.findUnderscore(sym)))    \
-    if (loadConfig->field != config->imageBase + s->getRVA())                  \
+    if (loadConfig->field != ctx.config.imageBase + s->getRVA())               \
       warn(#field " not set correctly in '_load_config_used'");
 
 #define CHECK_ABSOLUTE(field, sym)                                             \
@@ -2216,7 +2236,7 @@ void Writer::checkLoadConfigGuardData(const T *loadConfig) {
     if (loadConfig->field != s->getVA())                                       \
       warn(#field " not set correctly in '_load_config_used'");
 
-  if (config->guardCF == GuardCFLevel::Off)
+  if (ctx.config.guardCF == GuardCFLevel::Off)
     return;
   RETURN_IF_NOT_CONTAINS(GuardFlags)
   CHECK_VA(GuardCFFunctionTable, "__guard_fids_table")
@@ -2227,13 +2247,13 @@ void Writer::checkLoadConfigGuardData(const T *loadConfig) {
     CHECK_ABSOLUTE(GuardAddressTakenIatEntryCount, "__guard_iat_count")
   }
 
-  if (!(config->guardCF & GuardCFLevel::LongJmp))
+  if (!(ctx.config.guardCF & GuardCFLevel::LongJmp))
     return;
   RETURN_IF_NOT_CONTAINS(GuardLongJumpTargetCount)
   CHECK_VA(GuardLongJumpTargetTable, "__guard_longjmp_table")
   CHECK_ABSOLUTE(GuardLongJumpTargetCount, "__guard_longjmp_count")
 
-  if (!(config->guardCF & GuardCFLevel::EHCont))
+  if (!(ctx.config.guardCF & GuardCFLevel::EHCont))
     return;
   RETURN_IF_NOT_CONTAINS(GuardEHContinuationCount)
   CHECK_VA(GuardEHContinuationTable, "__guard_eh_cont_table")

diff  --git a/lld/COFF/Writer.h b/lld/COFF/Writer.h
index d2c05e4ae0b5..4a74aa7ada59 100644
--- a/lld/COFF/Writer.h
+++ b/lld/COFF/Writer.h
@@ -45,9 +45,9 @@ class OutputSection {
   void insertChunkAtStart(Chunk *c);
   void merge(OutputSection *other);
   void setPermissions(uint32_t c);
-  uint64_t getRVA() { return header.VirtualAddress; }
-  uint64_t getFileOff() { return header.PointerToRawData; }
-  void writeHeaderTo(uint8_t *buf);
+  uint64_t getRVA() const { return header.VirtualAddress; }
+  uint64_t getFileOff() const { return header.PointerToRawData; }
+  void writeHeaderTo(uint8_t *buf, bool isDebug);
   void addContributingPartialSection(PartialSection *sec);
 
   // Returns the size of this section in an executable memory image.


        


More information about the llvm-commits mailing list