//===- lib/ReaderWriter/PECOFF/WriterPECOFF.cpp ---------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// PE/COFF file consists of DOS Header, PE Header, COFF Header and Section
/// Tables followed by raw section data.
///
/// This writer is responsible for writing Core Linker results to an Windows
/// executable file.
///
/// This writer currently supports 32 bit PE/COFF for x86 processor only.
///
//===----------------------------------------------------------------------===//

#include "Atoms.h"
#include "WriterImportLibrary.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/File.h"
#include "lld/Core/Writer.h"
#include "lld/ReaderWriter/AtomLayout.h"
#include "lld/ReaderWriter/PECOFFLinkingContext.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/COFF.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/Format.h"
#include <algorithm>
#include <cstdlib>
#include <map>
#include <time.h>
#include <vector>

#define DEBUG_TYPE "WriterPECOFF"

using namespace llvm::support::endian;

using llvm::COFF::DataDirectoryIndex;
using llvm::object::coff_runtime_function_x64;
using llvm::support::ulittle16_t;
using llvm::support::ulittle32_t;
using llvm::support::ulittle64_t;

namespace lld {
namespace pecoff {

// Disk sector size. Some data needs to be aligned at disk sector boundary in
// file.
static const int SECTOR_SIZE = 512;

namespace {
class SectionChunk;

/// A Chunk is an abstract contiguous range in an output file.
class Chunk {
public:
  enum Kind {
    kindHeader,
    kindSection,
    kindStringTable,
    kindAtomChunk
  };

  explicit Chunk(Kind kind) : _kind(kind), _size(0) {}
  virtual ~Chunk() {}
  virtual void write(uint8_t *buffer) = 0;
  virtual uint64_t size() const { return _size; }
  virtual uint64_t onDiskSize() const { return size(); }
  virtual uint64_t align() const { return 1; }

  uint64_t fileOffset() const { return _fileOffset; }
  void setFileOffset(uint64_t fileOffset) { _fileOffset = fileOffset; }
  Kind getKind() const { return _kind; }

protected:
  Kind _kind;
  uint64_t _size;
  uint64_t _fileOffset;
};

/// A HeaderChunk is an abstract class to represent a file header for
/// PE/COFF. The data in the header chunk is metadata about program and will
/// be consumed by the windows loader. HeaderChunks are not mapped to memory
/// when executed.
class HeaderChunk : public Chunk {
public:
  HeaderChunk() : Chunk(kindHeader) {}

  static bool classof(const Chunk *c) { return c->getKind() == kindHeader; }
};

/// A DOSStubChunk represents the DOS compatible header at the beginning
/// of PE/COFF files.
class DOSStubChunk : public HeaderChunk {
public:
  explicit DOSStubChunk(const PECOFFLinkingContext &ctx)
      : HeaderChunk(), _ctx(ctx) {
    // Minimum size of DOS stub is 64 bytes. The next block (PE header) needs to
    // be aligned on 8 byte boundary.
    size_t size = std::max(_ctx.getDosStub().size(), (size_t)64);
    _size = llvm::RoundUpToAlignment(size, 8);
  }

  void write(uint8_t *buffer) override {
    ArrayRef<uint8_t> array = _ctx.getDosStub();
    std::memcpy(buffer, array.data(), array.size());
    auto *header = reinterpret_cast<llvm::object::dos_header *>(buffer);
    header->AddressOfRelocationTable = sizeof(llvm::object::dos_header);
    header->AddressOfNewExeHeader = _size;
  }

private:
  const PECOFFLinkingContext &_ctx;
};

/// A PEHeaderChunk represents PE header including COFF header.
template <class PEHeader>
class PEHeaderChunk : public HeaderChunk {
public:
  explicit PEHeaderChunk(const PECOFFLinkingContext &ctx);

  void write(uint8_t *buffer) override;

  void setSizeOfHeaders(uint64_t size) {
    // Must be multiple of FileAlignment.
    _peHeader.SizeOfHeaders = llvm::RoundUpToAlignment(size, SECTOR_SIZE);
  }

  void setSizeOfCode(uint64_t size) { _peHeader.SizeOfCode = size; }
  void setBaseOfCode(uint32_t rva) { _peHeader.BaseOfCode = rva; }
  void setBaseOfData(uint32_t rva);
  void setSizeOfImage(uint32_t size) { _peHeader.SizeOfImage = size; }

  void setSizeOfInitializedData(uint64_t size) {
    _peHeader.SizeOfInitializedData = size;
  }

  void setSizeOfUninitializedData(uint64_t size) {
    _peHeader.SizeOfUninitializedData = size;
  }

  void setNumberOfSections(uint32_t num) { _coffHeader.NumberOfSections = num; }
  void setNumberOfSymbols(uint32_t num) { _coffHeader.NumberOfSymbols = num; }

  void setAddressOfEntryPoint(uint32_t address) {
    _peHeader.AddressOfEntryPoint = address;
  }

  void setPointerToSymbolTable(uint32_t rva) {
    _coffHeader.PointerToSymbolTable = rva;
  }

private:
  llvm::object::coff_file_header _coffHeader;
  PEHeader _peHeader;
};

/// A SectionHeaderTableChunk represents Section Table Header of PE/COFF
/// format, which is a list of section headers.
class SectionHeaderTableChunk : public HeaderChunk {
public:
  SectionHeaderTableChunk() : HeaderChunk() {}
  void addSection(SectionChunk *chunk);
  uint64_t size() const override;
  void write(uint8_t *buffer) override;

private:
  static llvm::object::coff_section createSectionHeader(SectionChunk *chunk);

  std::vector<SectionChunk *> _sections;
};

class StringTableChunk : public Chunk {
public:
  StringTableChunk() : Chunk(kindStringTable) {}

  static bool classof(const Chunk *c) {
    return c->getKind() == kindStringTable;
  }

  uint32_t addSectionName(StringRef sectionName) {
    if (_stringTable.empty()) {
      // The string table immediately follows the symbol table.
      // We don't really need a symbol table, but some tools (e.g. dumpbin)
      // don't like zero-length symbol table.
      // Make room for the empty symbol slot, which occupies 18 byte.
      // We also need to reserve 4 bytes for the string table header.
      int size = sizeof(llvm::object::coff_symbol16) + 4;
      _stringTable.insert(_stringTable.begin(), size, 0);
      // Set the name of the dummy symbol to the first string table entry.
      // It's better than letting dumpbin print out a garabage as a symbol name.
      char *off = _stringTable.data() + 4;
      write32le(off, 4);
    }
    uint32_t offset = _stringTable.size();
    _stringTable.insert(_stringTable.end(), sectionName.begin(),
                        sectionName.end());
    _stringTable.push_back('\0');
    return offset - sizeof(llvm::object::coff_symbol16);
  }

  uint64_t size() const override { return _stringTable.size(); }

  void write(uint8_t *buffer) override {
    if (_stringTable.empty())
      return;
    char *off = _stringTable.data() + sizeof(llvm::object::coff_symbol16);
    write32le(off, _stringTable.size());
    std::memcpy(buffer, _stringTable.data(), _stringTable.size());
  }

private:
  std::vector<char> _stringTable;
};

class SectionChunk : public Chunk {
public:
  uint64_t onDiskSize() const override {
    if (_characteristics & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)
      return 0;
    return llvm::RoundUpToAlignment(size(), SECTOR_SIZE);
  }

  uint64_t align() const override { return SECTOR_SIZE; }
  uint32_t getCharacteristics() const { return _characteristics; }
  StringRef getSectionName() const { return _sectionName; }
  virtual uint64_t memAlign() const { return _memAlign; }

  static bool classof(const Chunk *c) {
    Kind kind = c->getKind();
    return kind == kindSection || kind == kindAtomChunk;
  }

  uint64_t getVirtualAddress() { return _virtualAddress; }
  virtual void setVirtualAddress(uint32_t rva) { _virtualAddress = rva; }

  uint32_t getStringTableOffset() const { return _stringTableOffset; }
  void setStringTableOffset(uint32_t offset) { _stringTableOffset = offset; }

protected:
  SectionChunk(Kind kind, StringRef sectionName, uint32_t characteristics,
               const PECOFFLinkingContext &ctx)
      : Chunk(kind), _sectionName(sectionName),
        _characteristics(characteristics), _virtualAddress(0),
        _stringTableOffset(0), _memAlign(ctx.getPageSize()) {}

private:
  StringRef _sectionName;
  const uint32_t _characteristics;
  uint64_t _virtualAddress;
  uint32_t _stringTableOffset;
  uint64_t _memAlign;
};

struct BaseReloc {
  BaseReloc(uint64_t a, llvm::COFF::BaseRelocationType t) : addr(a), type(t) {}
  uint64_t addr;
  llvm::COFF::BaseRelocationType type;
};

/// An AtomChunk represents a section containing atoms.
class AtomChunk : public SectionChunk {
public:
  AtomChunk(const PECOFFLinkingContext &ctx, StringRef name,
            const std::vector<const DefinedAtom *> &atoms);

  void write(uint8_t *buffer) override;

  uint64_t memAlign() const override;
  void appendAtom(const DefinedAtom *atom);
  void buildAtomRvaMap(std::map<const Atom *, uint64_t> &atomRva) const;

  void applyRelocationsARM(uint8_t *buffer,
                           std::map<const Atom *, uint64_t> &atomRva,
                           std::vector<uint64_t> &sectionRva,
                           uint64_t imageBaseAddress);
  void applyRelocationsX86(uint8_t *buffer,
                           std::map<const Atom *, uint64_t> &atomRva,
                           std::vector<uint64_t> &sectionRva,
                           uint64_t imageBaseAddress);
  void applyRelocationsX64(uint8_t *buffer,
                           std::map<const Atom *, uint64_t> &atomRva,
                           std::vector<uint64_t> &sectionRva,
                           uint64_t imageBaseAddress);

  void printAtomAddresses(uint64_t baseAddr) const;
  void addBaseRelocations(std::vector<BaseReloc> &relocSites) const;

  void setVirtualAddress(uint32_t rva) override;
  uint64_t getAtomVirtualAddress(StringRef name) const;

  static bool classof(const Chunk *c) { return c->getKind() == kindAtomChunk; }

protected:
  std::vector<AtomLayout *> _atomLayouts;
  uint64_t _virtualAddress;

private:
  uint32_t
  computeCharacteristics(const PECOFFLinkingContext &ctx, StringRef name,
                         const std::vector<const DefinedAtom *> &atoms) const {
    return ctx.getSectionAttributes(name,
                                    getDefaultCharacteristics(name, atoms));
  }

  uint32_t getDefaultCharacteristics(
      StringRef name, const std::vector<const DefinedAtom *> &atoms) const;

  mutable llvm::BumpPtrAllocator _alloc;
  llvm::COFF::MachineTypes _machineType;
  const PECOFFLinkingContext &_ctx;
};

/// A DataDirectoryChunk represents data directory entries that follows the PE
/// header in the output file. An entry consists of an 8 byte field that
/// indicates a relative virtual address (the starting address of the entry data
/// in memory) and 8 byte entry data size.
class DataDirectoryChunk : public HeaderChunk {
public:
  DataDirectoryChunk()
      : HeaderChunk(), _data(std::vector<llvm::object::data_directory>(16)) {}

  uint64_t size() const override {
    return sizeof(llvm::object::data_directory) * _data.size();
  }

  void setField(DataDirectoryIndex index, uint32_t addr, uint32_t size);
  void write(uint8_t *buffer) override;

private:
  std::vector<llvm::object::data_directory> _data;
};

/// A BaseRelocChunk represents ".reloc" section.
///
/// .reloc section contains a list of addresses. If the PE/COFF loader decides
/// to load the binary at a memory address different from its preferred base
/// address, which is specified by ImageBase field in the COFF header, the
/// loader needs to relocate the binary, so that all the addresses in the binary
/// point to new locations. The loader will do that by fixing up the addresses
/// specified by .reloc section.
///
/// The executable is almost always loaded at the preferred base address because
/// it's loaded into an empty address space. The DLL is however an subject of
/// load-time relocation because it may conflict with other DLLs or the
/// executable.
class BaseRelocChunk : public SectionChunk {
  typedef std::vector<std::unique_ptr<Chunk> > ChunkVectorT;

public:
  BaseRelocChunk(ChunkVectorT &chunks, const PECOFFLinkingContext &ctx)
      : SectionChunk(kindSection, ".reloc", characteristics, ctx),
        _ctx(ctx), _contents(createContents(chunks)) {}

  void write(uint8_t *buffer) override {
    std::memcpy(buffer, &_contents[0], _contents.size());
  }

  uint64_t size() const override { return _contents.size(); }

private:
  // When loaded into memory, reloc section should be readable and writable.
  static const uint32_t characteristics =
      llvm::COFF::IMAGE_SCN_MEM_READ |
      llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
      llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE;

  std::vector<uint8_t> createContents(ChunkVectorT &chunks) const;

  // Returns a list of RVAs that needs to be relocated if the binary is loaded
  // at an address different from its preferred one.
  std::vector<BaseReloc> listRelocSites(ChunkVectorT &chunks) const;

  // Create the content of a relocation block.
  std::vector<uint8_t>
  createBaseRelocBlock(uint64_t pageAddr, const BaseReloc *begin,
                       const BaseReloc *end) const;

  const PECOFFLinkingContext &_ctx;
  std::vector<uint8_t> _contents;
};

template <class PEHeader>
PEHeaderChunk<PEHeader>::PEHeaderChunk(const PECOFFLinkingContext &ctx)
    : HeaderChunk() {
  // Set the size of the chunk and initialize the header with null bytes.
  _size = sizeof(llvm::COFF::PEMagic) + sizeof(_coffHeader) + sizeof(_peHeader);
  std::memset(&_coffHeader, 0, sizeof(_coffHeader));
  std::memset(&_peHeader, 0, sizeof(_peHeader));

  _coffHeader.Machine = ctx.getMachineType();
  _coffHeader.TimeDateStamp = time(nullptr);

  // Attributes of the executable.
  uint16_t characteristics = llvm::COFF::IMAGE_FILE_EXECUTABLE_IMAGE;
  if (!ctx.is64Bit())
    characteristics |= llvm::COFF::IMAGE_FILE_32BIT_MACHINE;
  if (ctx.isDll())
    characteristics |= llvm::COFF::IMAGE_FILE_DLL;
  if (ctx.getLargeAddressAware() || ctx.is64Bit())
    characteristics |= llvm::COFF::IMAGE_FILE_LARGE_ADDRESS_AWARE;
  if (ctx.getSwapRunFromCD())
    characteristics |= llvm::COFF::IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP;
  if (ctx.getSwapRunFromNet())
    characteristics |= llvm::COFF::IMAGE_FILE_NET_RUN_FROM_SWAP;
  if (!ctx.getBaseRelocationEnabled())
    characteristics |= llvm::COFF::IMAGE_FILE_RELOCS_STRIPPED;

  _coffHeader.Characteristics = characteristics;

  _peHeader.Magic = ctx.is64Bit() ? llvm::COFF::PE32Header::PE32_PLUS
                                  : llvm::COFF::PE32Header::PE32;

  // The address of the executable when loaded into memory. The default for
  // DLLs is 0x10000000. The default for executables is 0x400000.
  _peHeader.ImageBase = ctx.getBaseAddress();

  // Sections should be page-aligned when loaded into memory, which is 4KB on
  // x86.
  _peHeader.SectionAlignment = ctx.getSectionDefaultAlignment();

  // Sections in an executable file on disk should be sector-aligned (512 byte).
  _peHeader.FileAlignment = SECTOR_SIZE;

  // The version number of the resultant executable/DLL. The number is purely
  // informative, and neither the linker nor the loader won't use it. User can
  // set the value using /version command line option. Default is 0.0.
  PECOFFLinkingContext::Version imageVersion = ctx.getImageVersion();
  _peHeader.MajorImageVersion = imageVersion.majorVersion;
  _peHeader.MinorImageVersion = imageVersion.minorVersion;

  // The required Windows version number. This is the internal version and
  // shouldn't be confused with product name. Windows 7 is version 6.1 and
  // Windows 8 is 6.2, for example.
  PECOFFLinkingContext::Version minOSVersion = ctx.getMinOSVersion();
  _peHeader.MajorOperatingSystemVersion = minOSVersion.majorVersion;
  _peHeader.MinorOperatingSystemVersion = minOSVersion.minorVersion;
  _peHeader.MajorSubsystemVersion = minOSVersion.majorVersion;
  _peHeader.MinorSubsystemVersion = minOSVersion.minorVersion;

  _peHeader.Subsystem = ctx.getSubsystem();

  // Despite its name, DLL characteristics field has meaning both for
  // executables and DLLs. We are not very sure if the following bits must
  // be set, but regular binaries seem to have these bits, so we follow
  // them.
  uint16_t dllCharacteristics = 0;
  if (ctx.noSEH())
    dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_SEH;
  if (ctx.isTerminalServerAware())
    dllCharacteristics |=
        llvm::COFF::IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
  if (ctx.isNxCompat())
    dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
  if (ctx.getDynamicBaseEnabled())
    dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
  if (!ctx.getAllowBind())
    dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_BIND;
  if (!ctx.getAllowIsolation())
    dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;
  if (ctx.getHighEntropyVA() && ctx.is64Bit())
    dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA;
  _peHeader.DLLCharacteristics = dllCharacteristics;

  _peHeader.SizeOfStackReserve = ctx.getStackReserve();
  _peHeader.SizeOfStackCommit = ctx.getStackCommit();
  _peHeader.SizeOfHeapReserve = ctx.getHeapReserve();
  _peHeader.SizeOfHeapCommit = ctx.getHeapCommit();

  // The number of data directory entries. We always have 16 entries.
  _peHeader.NumberOfRvaAndSize = 16;

  // The size of PE header including optional data directory.
  _coffHeader.SizeOfOptionalHeader = sizeof(PEHeader) +
      _peHeader.NumberOfRvaAndSize * sizeof(llvm::object::data_directory);
}

template <>
void PEHeaderChunk<llvm::object::pe32_header>::setBaseOfData(uint32_t rva) {
  _peHeader.BaseOfData = rva;
}

template <>
void PEHeaderChunk<llvm::object::pe32plus_header>::setBaseOfData(uint32_t rva) {
  // BaseOfData field does not exist in PE32+ header.
}

template <class PEHeader>
void PEHeaderChunk<PEHeader>::write(uint8_t *buffer) {
  std::memcpy(buffer, llvm::COFF::PEMagic, sizeof(llvm::COFF::PEMagic));
  buffer += sizeof(llvm::COFF::PEMagic);
  std::memcpy(buffer, &_coffHeader, sizeof(_coffHeader));
  buffer += sizeof(_coffHeader);
  std::memcpy(buffer, &_peHeader, sizeof(_peHeader));
}

AtomChunk::AtomChunk(const PECOFFLinkingContext &ctx, StringRef sectionName,
                     const std::vector<const DefinedAtom *> &atoms)
    : SectionChunk(kindAtomChunk, sectionName,
                   computeCharacteristics(ctx, sectionName, atoms), ctx),
      _virtualAddress(0), _machineType(ctx.getMachineType()), _ctx(ctx) {
  for (auto *a : atoms)
    appendAtom(a);
}

void AtomChunk::write(uint8_t *buffer) {
  if (_atomLayouts.empty())
    return;
  if (getCharacteristics() & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)
    return;
  if (getCharacteristics() & llvm::COFF::IMAGE_SCN_CNT_CODE) {
    // Fill the section with INT 3 (0xCC) rather than NUL, so that the
    // disassembler will not interpret a garbage between atoms as the beginning
    // of multi-byte machine code. This does not change the behavior of
    // resulting binary but help debugging.
    uint8_t *start = buffer + _atomLayouts.front()->_fileOffset;
    uint8_t *end = buffer + _atomLayouts.back()->_fileOffset;
    memset(start, 0xCC, end - start);
  }

  for (const auto *layout : _atomLayouts) {
    const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
    ArrayRef<uint8_t> rawContent = atom->rawContent();
    std::memcpy(buffer + layout->_fileOffset, rawContent.data(),
                rawContent.size());
  }
}

// Add all atoms to the given map. This data will be used to do relocation.
void
AtomChunk::buildAtomRvaMap(std::map<const Atom *, uint64_t> &atomRva) const {
  for (const auto *layout : _atomLayouts)
    atomRva[layout->_atom] = layout->_virtualAddr;
}

static int getSectionIndex(uint64_t targetAddr,
                           const std::vector<uint64_t> &sectionRva) {
  int i = 1;
  for (uint64_t rva : sectionRva) {
    if (targetAddr < rva)
      return i;
    ++i;
  }
  return i;
}

static uint32_t getSectionStartAddr(uint64_t targetAddr,
                                    const std::vector<uint64_t> &sectionRva) {
  // Scan the list of section start addresses to find the section start address
  // for the given RVA.
  for (int i = 0, e = sectionRva.size(); i < e; ++i)
    if (i == e - 1 || (sectionRva[i] <= targetAddr && targetAddr < sectionRva[i + 1]))
      return sectionRva[i];
  llvm_unreachable("Section missing");
}

static void applyThumbMoveImmediate(ulittle16_t *mov, uint16_t imm) {
  // MOVW(T3): |11110|i|10|0|1|0|0|imm4|0|imm3|Rd|imm8|
  //            imm32 = zext imm4:i:imm3:imm8
  // MOVT(T1): |11110|i|10|1|1|0|0|imm4|0|imm3|Rd|imm8|
  //            imm16 = imm4:i:imm3:imm8
  mov[0] =
      mov[0] | (((imm & 0x0800) >> 11) << 10) | (((imm & 0xf000) >> 12) << 0);
  mov[1] =
      mov[1] | (((imm & 0x0700) >> 8) << 12) | (((imm & 0x00ff) >> 0) << 0);
}

static void applyThumbBranchImmediate(ulittle16_t *bl, int32_t imm) {
  // BL(T1):  |11110|S|imm10|11|J1|1|J2|imm11|
  //          imm32 = sext S:I1:I2:imm10:imm11:'0'
  // B.W(T4): |11110|S|imm10|10|J1|1|J2|imm11|
  //          imm32 = sext S:I1:I2:imm10:imm11:'0'
  //
  //          I1 = ~(J1 ^ S), I2 = ~(J2 ^ S)

  assert((~abs(imm) & (~0ULL << 24)) && "bl/b.w out of range");

  uint32_t S = (imm < 0 ? 1 : 0);
  uint32_t J1 = ((~imm & 0x00800000) >> 23) ^ S;
  uint32_t J2 = ((~imm & 0x00400000) >> 22) ^ S;

  bl[0] = bl[0] | (((imm & 0x003ff000) >> 12) << 0) | (S << 10);
  bl[1] = bl[1] | (((imm & 0x00000ffe) >>  1) << 0) | (J2 << 11) | (J1 << 13);
}

void AtomChunk::applyRelocationsARM(uint8_t *Buffer,
                                    std::map<const Atom *, uint64_t> &AtomRVA,
                                    std::vector<uint64_t> &SectionRVA,
                                    uint64_t ImageBase) {
  Buffer = Buffer + _fileOffset;
  parallel_for_each(_atomLayouts.begin(), _atomLayouts.end(),
                    [&](const AtomLayout *layout) {
    const DefinedAtom *Atom = cast<DefinedAtom>(layout->_atom);
    for (const Reference *R : *Atom) {
      if (R->kindNamespace() != Reference::KindNamespace::COFF)
        continue;

      bool AssumeTHUMBCode = false;
      if (auto Target = dyn_cast<DefinedAtom>(R->target()))
        AssumeTHUMBCode = Target->permissions() == DefinedAtom::permR_X ||
                          Target->permissions() == DefinedAtom::permRWX;

      const auto AtomOffset = R->offsetInAtom();
      const auto FileOffset = layout->_fileOffset;
      const auto TargetAddr = AtomRVA[R->target()] | (AssumeTHUMBCode ? 1 : 0);
      auto RelocSite16 =
          reinterpret_cast<ulittle16_t *>(Buffer + FileOffset + AtomOffset);
      auto RelocSite32 =
          reinterpret_cast<ulittle32_t *>(Buffer + FileOffset + AtomOffset);

      switch (R->kindValue()) {
      default: llvm_unreachable("unsupported relocation type");
      case llvm::COFF::IMAGE_REL_ARM_ADDR32:
        *RelocSite32 = *RelocSite32 + TargetAddr + ImageBase;
        break;
      case llvm::COFF::IMAGE_REL_ARM_ADDR32NB:
        *RelocSite32 = *RelocSite32 + TargetAddr;
        break;
      case llvm::COFF::IMAGE_REL_ARM_MOV32T:
        applyThumbMoveImmediate(&RelocSite16[0], (TargetAddr + ImageBase) >>  0);
        applyThumbMoveImmediate(&RelocSite16[2], (TargetAddr + ImageBase) >> 16);
        break;
      case llvm::COFF::IMAGE_REL_ARM_BRANCH24T:
        // NOTE: the thumb bit will implicitly be truncated properly
        applyThumbBranchImmediate(RelocSite16,
                                  TargetAddr - AtomRVA[Atom] - AtomOffset - 4);
        break;
      case llvm::COFF::IMAGE_REL_ARM_BLX23T:
        // NOTE: the thumb bit will implicitly be truncated properly
        applyThumbBranchImmediate(RelocSite16,
                                  TargetAddr - AtomRVA[Atom] - AtomOffset - 4);
        break;
      }
    }
  });
}

void AtomChunk::applyRelocationsX86(uint8_t *buffer,
                                    std::map<const Atom *, uint64_t> &atomRva,
                                    std::vector<uint64_t> &sectionRva,
                                    uint64_t imageBaseAddress) {
  buffer += _fileOffset;
  parallel_for_each(_atomLayouts.begin(), _atomLayouts.end(),
                    [&](const AtomLayout *layout) {
    const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
    for (const Reference *ref : *atom) {
      // Skip if this reference is not for COFF relocation.
      if (ref->kindNamespace() != Reference::KindNamespace::COFF)
        continue;
      auto relocSite32 = reinterpret_cast<ulittle32_t *>(
          buffer + layout->_fileOffset + ref->offsetInAtom());
      auto relocSite16 = reinterpret_cast<ulittle16_t *>(relocSite32);
      const Atom *target = ref->target();
      uint64_t targetAddr = atomRva[target];
      // Also account for whatever offset is already stored at the relocation
      // site.
      switch (ref->kindValue()) {
      case llvm::COFF::IMAGE_REL_I386_ABSOLUTE:
        // This relocation is no-op.
        break;
      case llvm::COFF::IMAGE_REL_I386_DIR32:
        // Set target's 32-bit VA.
        if (auto *abs = dyn_cast<AbsoluteAtom>(target))
          *relocSite32 += abs->value();
        else
          *relocSite32 += targetAddr + imageBaseAddress;
        break;
      case llvm::COFF::IMAGE_REL_I386_DIR32NB:
        // Set target's 32-bit RVA.
        *relocSite32 += targetAddr;
        break;
      case llvm::COFF::IMAGE_REL_I386_REL32: {
        // Set 32-bit relative address of the target. This relocation is
        // usually used for relative branch or call instruction.
        uint32_t disp = atomRva[atom] + ref->offsetInAtom() + 4;
        *relocSite32 += targetAddr - disp;
        break;
      }
      case llvm::COFF::IMAGE_REL_I386_SECTION:
        // The 16-bit section index that contains the target symbol.
        *relocSite16 += getSectionIndex(targetAddr, sectionRva);
        break;
      case llvm::COFF::IMAGE_REL_I386_SECREL:
        // The 32-bit relative address from the beginning of the section that
        // contains the target symbol.
        *relocSite32 +=
            targetAddr - getSectionStartAddr(targetAddr, sectionRva);
        break;
      default:
        llvm::report_fatal_error("Unsupported relocation kind");
      }
    }
  });
}

void AtomChunk::applyRelocationsX64(uint8_t *buffer,
                                    std::map<const Atom *, uint64_t> &atomRva,
                                    std::vector<uint64_t> &sectionRva,
                                    uint64_t imageBase) {
  buffer += _fileOffset;
  parallel_for_each(_atomLayouts.begin(), _atomLayouts.end(),
                    [&](const AtomLayout *layout) {
    const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
    for (const Reference *ref : *atom) {
      if (ref->kindNamespace() != Reference::KindNamespace::COFF)
        continue;

      uint8_t *loc = buffer + layout->_fileOffset + ref->offsetInAtom();
      auto relocSite16 = reinterpret_cast<ulittle16_t *>(loc);
      auto relocSite32 = reinterpret_cast<ulittle32_t *>(loc);
      auto relocSite64 = reinterpret_cast<ulittle64_t *>(loc);
      uint64_t targetAddr = atomRva[ref->target()];

      switch (ref->kindValue()) {
      case llvm::COFF::IMAGE_REL_AMD64_ADDR64:
        *relocSite64 += targetAddr + imageBase;
        break;
      case llvm::COFF::IMAGE_REL_AMD64_ADDR32:
        *relocSite32 += targetAddr + imageBase;
        break;
      case llvm::COFF::IMAGE_REL_AMD64_ADDR32NB:
        *relocSite32 += targetAddr;
        break;
      case llvm::COFF::IMAGE_REL_AMD64_REL32:
        *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 4;
        break;
      case llvm::COFF::IMAGE_REL_AMD64_REL32_1:
        *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 5;
        break;
      case llvm::COFF::IMAGE_REL_AMD64_REL32_2:
        *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 6;
        break;
      case llvm::COFF::IMAGE_REL_AMD64_REL32_3:
        *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 7;
        break;
      case llvm::COFF::IMAGE_REL_AMD64_REL32_4:
        *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 8;
        break;
      case llvm::COFF::IMAGE_REL_AMD64_REL32_5:
        *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 9;
        break;
      case llvm::COFF::IMAGE_REL_AMD64_SECTION:
        *relocSite16 += getSectionIndex(targetAddr, sectionRva) - 1;
        break;
      case llvm::COFF::IMAGE_REL_AMD64_SECREL:
        *relocSite32 +=
            targetAddr - getSectionStartAddr(targetAddr, sectionRva);
        break;
      default:
        llvm::errs() << "Kind: " << (int)ref->kindValue() << "\n";
        llvm::report_fatal_error("Unsupported relocation kind");
      }
    }
  });
}

/// Print atom VAs. Used only for debugging.
void AtomChunk::printAtomAddresses(uint64_t baseAddr) const {
  for (const auto *layout : _atomLayouts) {
    const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
    uint64_t addr = layout->_virtualAddr;
    llvm::dbgs() << llvm::format("0x%08llx: ", addr + baseAddr)
                 << (atom->name().empty() ? "(anonymous)" : atom->name())
                 << "\n";
  }
}

/// List all virtual addresses (and not relative virtual addresses) that need
/// to be fixed up if image base is relocated. The only relocation type that
/// needs to be fixed is DIR32 on i386. REL32 is not (and should not be)
/// fixed up because it's PC-relative.
void AtomChunk::addBaseRelocations(std::vector<BaseReloc> &relocSites) const {
  for (const auto *layout : _atomLayouts) {
    const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
    for (const Reference *ref : *atom) {
      if (ref->kindNamespace() != Reference::KindNamespace::COFF)
        continue;

      // An absolute symbol points to a fixed location in memory. Their
      // address should not be fixed at load time. One exception is ImageBase
      // because that's relative to run-time image base address.
      if (auto *abs = dyn_cast<AbsoluteAtom>(ref->target()))
        if (!abs->name().equals("__ImageBase") &&
            !abs->name().equals("___ImageBase"))
          continue;

      uint64_t address = layout->_virtualAddr + ref->offsetInAtom();
      switch (_machineType) {
      default: llvm_unreachable("unsupported machine type");
      case llvm::COFF::IMAGE_FILE_MACHINE_I386:
        if (ref->kindValue() == llvm::COFF::IMAGE_REL_I386_DIR32)
          relocSites.push_back(
              BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_HIGHLOW));
        break;
      case llvm::COFF::IMAGE_FILE_MACHINE_AMD64:
        if (ref->kindValue() == llvm::COFF::IMAGE_REL_AMD64_ADDR64)
          relocSites.push_back(
              BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_DIR64));
        break;
      case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT:
        if (ref->kindValue() == llvm::COFF::IMAGE_REL_ARM_ADDR32)
          relocSites.push_back(
              BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_HIGHLOW));
        else if (ref->kindValue() == llvm::COFF::IMAGE_REL_ARM_MOV32T)
          relocSites.push_back(
              BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_ARM_MOV32T));
        break;
      }
    }
  }
}

void AtomChunk::setVirtualAddress(uint32_t rva) {
  SectionChunk::setVirtualAddress(rva);
  for (AtomLayout *layout : _atomLayouts)
    layout->_virtualAddr += rva;
}

uint64_t AtomChunk::getAtomVirtualAddress(StringRef name) const {
  for (auto atomLayout : _atomLayouts)
    if (atomLayout->_atom->name() == name)
      return atomLayout->_virtualAddr;
  return 0;
}

void DataDirectoryChunk::setField(DataDirectoryIndex index, uint32_t addr,
                                  uint32_t size) {
  llvm::object::data_directory &dir = _data[index];
  dir.RelativeVirtualAddress = addr;
  dir.Size = size;
}

void DataDirectoryChunk::write(uint8_t *buffer) {
  std::memcpy(buffer, &_data[0], size());
}

uint64_t AtomChunk::memAlign() const {
  // ReaderCOFF propagated the section alignment to the first atom in
  // the section. We restore that here.
  if (_atomLayouts.empty())
    return _ctx.getPageSize();
  unsigned align = _ctx.getPageSize();
  for (auto atomLayout : _atomLayouts) {
    auto *atom = cast<const DefinedAtom>(atomLayout->_atom);
    align = std::max(align, (unsigned)atom->alignment().value);
  }
  return align;
}

void AtomChunk::appendAtom(const DefinedAtom *atom) {
  // Atom may have to be at a proper alignment boundary. If so, move the
  // pointer to make a room after the last atom before adding new one.
  _size = llvm::RoundUpToAlignment(_size, atom->alignment().value);

  // Create an AtomLayout and move the current pointer.
  auto *layout = new (_alloc) AtomLayout(atom, _size, _size);
  _atomLayouts.push_back(layout);
  _size += atom->size();
}

uint32_t AtomChunk::getDefaultCharacteristics(
    StringRef name, const std::vector<const DefinedAtom *> &atoms) const {
  const uint32_t code = llvm::COFF::IMAGE_SCN_CNT_CODE;
  const uint32_t execute = llvm::COFF::IMAGE_SCN_MEM_EXECUTE;
  const uint32_t read = llvm::COFF::IMAGE_SCN_MEM_READ;
  const uint32_t write = llvm::COFF::IMAGE_SCN_MEM_WRITE;
  const uint32_t data = llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
  const uint32_t bss = llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA;
  if (name == ".text")
    return code | execute | read;
  if (name == ".data")
    return data | read | write;
  if (name == ".rdata")
    return data | read;
  if (name == ".bss")
    return bss | read | write;
  assert(atoms.size() > 0);
  switch (atoms[0]->permissions()) {
  case DefinedAtom::permR__:
    return data | read;
  case DefinedAtom::permRW_:
    return data | read | write;
  case DefinedAtom::permR_X:
    return code | execute | read;
  case DefinedAtom::permRWX:
    return code | execute | read | write;
  default:
    llvm_unreachable("Unsupported permission");
  }
}

void SectionHeaderTableChunk::addSection(SectionChunk *chunk) {
  _sections.push_back(chunk);
}

uint64_t SectionHeaderTableChunk::size() const {
  return _sections.size() * sizeof(llvm::object::coff_section);
}

void SectionHeaderTableChunk::write(uint8_t *buffer) {
  uint64_t offset = 0;
  for (SectionChunk *chunk : _sections) {
    llvm::object::coff_section header = createSectionHeader(chunk);
    std::memcpy(buffer + offset, &header, sizeof(header));
    offset += sizeof(header);
  }
}

llvm::object::coff_section
SectionHeaderTableChunk::createSectionHeader(SectionChunk *chunk) {
  llvm::object::coff_section header;

  // We have extended the COFF specification by allowing section names to be
  // greater than eight characters.  We achieve this by adding the section names
  // to the string table.  Binutils' linker, ld, performs the same trick.
  StringRef sectionName = chunk->getSectionName();
  std::memset(header.Name, 0, llvm::COFF::NameSize);
  if (uint32_t stringTableOffset = chunk->getStringTableOffset())
    sprintf(header.Name, "/%u", stringTableOffset);
  else
    std::strncpy(header.Name, sectionName.data(), sectionName.size());

  uint32_t characteristics = chunk->getCharacteristics();
  header.VirtualSize = chunk->size();
  header.VirtualAddress = chunk->getVirtualAddress();
  header.SizeOfRawData = chunk->onDiskSize();
  header.PointerToRelocations = 0;
  header.PointerToLinenumbers = 0;
  header.NumberOfRelocations = 0;
  header.NumberOfLinenumbers = 0;
  header.Characteristics = characteristics;

  if (characteristics & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
    header.PointerToRawData = 0;
  } else {
    header.PointerToRawData = chunk->fileOffset();
  }
  return header;
}

/// Creates .reloc section content from the other sections. The content of
/// .reloc is basically a list of relocation sites. The relocation sites are
/// divided into blocks. Each block represents the base relocation for a 4K
/// page.
///
/// By dividing 32 bit RVAs into blocks, COFF saves disk and memory space for
/// the base relocation. A block consists of a 32 bit page RVA and 16 bit
/// relocation entries which represent offsets in the page. That is a more
/// compact representation than a simple vector of 32 bit RVAs.
std::vector<uint8_t>
BaseRelocChunk::createContents(ChunkVectorT &chunks) const {
  std::vector<uint8_t> contents;
  std::vector<BaseReloc> relocSites = listRelocSites(chunks);

  uint64_t mask = _ctx.getPageSize() - 1;
  parallel_sort(relocSites.begin(), relocSites.end(),
                [=](const BaseReloc &a, const BaseReloc &b) {
                  return (a.addr & ~mask) < (b.addr & ~mask);
                });

  // Base relocations for the same memory page are grouped together
  // and passed to createBaseRelocBlock.
  for (auto it = relocSites.begin(), e = relocSites.end(); it != e;) {
    auto beginIt = it;
    uint64_t pageAddr = (beginIt->addr & ~mask);
    for (++it; it != e; ++it)
      if ((it->addr & ~mask) != pageAddr)
        break;
    const BaseReloc *begin = &*beginIt;
    const BaseReloc *end = begin + (it - beginIt);
    std::vector<uint8_t> block = createBaseRelocBlock(pageAddr, begin, end);
    contents.insert(contents.end(), block.begin(), block.end());
  }
  return contents;
}

// Returns a list of RVAs that needs to be relocated if the binary is loaded
// at an address different from its preferred one.
std::vector<BaseReloc>
BaseRelocChunk::listRelocSites(ChunkVectorT &chunks) const {
  std::vector<BaseReloc> ret;
  for (auto &cp : chunks)
    if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp))
      chunk->addBaseRelocations(ret);
  return ret;
}

// Create the content of a relocation block.
std::vector<uint8_t>
BaseRelocChunk::createBaseRelocBlock(uint64_t pageAddr,
                                     const BaseReloc *begin,
                                     const BaseReloc *end) const {
  // Relocation blocks should be padded with IMAGE_REL_I386_ABSOLUTE to be
  // aligned to a DWORD size boundary.
  uint32_t size = llvm::RoundUpToAlignment(
      sizeof(ulittle32_t) * 2 + sizeof(ulittle16_t) * (end - begin),
      sizeof(ulittle32_t));
  std::vector<uint8_t> contents(size);
  uint8_t *ptr = &contents[0];

  // The first four bytes is the page RVA.
  write32le(ptr, pageAddr);
  ptr += sizeof(ulittle32_t);

  // The second four bytes is the size of the block, including the the page
  // RVA and this size field.
  write32le(ptr, size);
  ptr += sizeof(ulittle32_t);

  uint64_t mask = _ctx.getPageSize() - 1;
  for (const BaseReloc *i = begin; i < end; ++i) {
    write16le(ptr, (i->type << 12) | (i->addr & mask));
    ptr += sizeof(ulittle16_t);
  }
  return contents;
}

} // end anonymous namespace

class PECOFFWriter : public Writer {
public:
  explicit PECOFFWriter(const PECOFFLinkingContext &context)
      : _ctx(context), _numSections(0), _imageSizeInMemory(_ctx.getPageSize()),
        _imageSizeOnDisk(0) {}

  template <class PEHeader> void build(const File &linkedFile);
  std::error_code writeFile(const File &linkedFile, StringRef path) override;

private:
  void applyAllRelocations(uint8_t *bufferStart);
  void printAllAtomAddresses() const;
  void reorderSEHTableEntries(uint8_t *bufferStart);
  void reorderSEHTableEntriesX86(uint8_t *bufferStart);
  void reorderSEHTableEntriesX64(uint8_t *bufferStart);

  void addChunk(Chunk *chunk);
  void addSectionChunk(std::unique_ptr<SectionChunk> chunk,
                       SectionHeaderTableChunk *table,
                       StringTableChunk *stringTable);
  void setImageSizeOnDisk();
  uint64_t
  calcSectionSize(llvm::COFF::SectionCharacteristics sectionType) const;

  uint64_t calcSizeOfInitializedData() const {
    return calcSectionSize(llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA);
  }

  uint64_t calcSizeOfUninitializedData() const {
    return calcSectionSize(llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA);
  }

  uint64_t calcSizeOfCode() const {
    return calcSectionSize(llvm::COFF::IMAGE_SCN_CNT_CODE);
  }

  std::vector<std::unique_ptr<Chunk> > _chunks;
  const PECOFFLinkingContext &_ctx;
  uint32_t _numSections;

  // The size of the image in memory. This is initialized with
  // _ctx.getPageSize(), as the first page starting at ImageBase is usually left
  // unmapped. IIUC there's no technical reason to do so, but we'll follow that
  // convention so that we don't produce odd-looking binary.
  uint32_t _imageSizeInMemory;

  // The size of the image on disk. This is basically the sum of all chunks in
  // the output file with paddings between them.
  uint32_t _imageSizeOnDisk;

  // The map from atom to its relative virtual address.
  std::map<const Atom *, uint64_t> _atomRva;
};

StringRef customSectionName(const DefinedAtom *atom) {
  assert(atom->sectionChoice() == DefinedAtom::sectionCustomRequired);
  StringRef s = atom->customSectionName();
  size_t pos = s.find('$');
  return (pos == StringRef::npos) ? s : s.substr(0, pos);
}

StringRef chooseSectionByContent(const DefinedAtom *atom) {
  switch (atom->contentType()) {
  case DefinedAtom::typeCode:
    return ".text";
  case DefinedAtom::typeZeroFill:
    return ".bss";
  case DefinedAtom::typeData:
    if (atom->permissions() == DefinedAtom::permR__)
      return ".rdata";
    if (atom->permissions() == DefinedAtom::permRW_)
      return ".data";
    break;
  default:
    break;
  }
  llvm::errs() << "Atom: contentType=" << atom->contentType()
               << " permission=" << atom->permissions() << "\n";
  llvm::report_fatal_error("Failed to choose section based on content");
}

typedef std::map<StringRef, std::vector<const DefinedAtom *> > AtomVectorMap;

void groupAtoms(const PECOFFLinkingContext &ctx, const File &file,
                AtomVectorMap &result) {
  for (const DefinedAtom *atom : file.defined()) {
    if (atom->sectionChoice() == DefinedAtom::sectionCustomRequired) {
      StringRef section = customSectionName(atom);
      result[ctx.getOutputSectionName(section)].push_back(atom);
      continue;
    }
    if (atom->sectionChoice() == DefinedAtom::sectionBasedOnContent) {
      StringRef section = chooseSectionByContent(atom);
      result[ctx.getOutputSectionName(section)].push_back(atom);
      continue;
    }
    llvm_unreachable("Unknown section choice");
  }
}

static const DefinedAtom *findTLSUsedSymbol(const PECOFFLinkingContext &ctx,
                                            const File &file) {
  StringRef sym = ctx.decorateSymbol("_tls_used");
  for (const DefinedAtom *atom : file.defined())
    if (atom->name() == sym)
      return atom;
  return nullptr;
}

// Create all chunks that consist of the output file.
template <class PEHeader>
void PECOFFWriter::build(const File &linkedFile) {
  AtomVectorMap atoms;
  groupAtoms(_ctx, linkedFile, atoms);

  // Create file chunks and add them to the list.
  auto *dosStub = new DOSStubChunk(_ctx);
  auto *peHeader = new PEHeaderChunk<PEHeader>(_ctx);
  auto *dataDirectory = new DataDirectoryChunk();
  auto *sectionTable = new SectionHeaderTableChunk();
  auto *stringTable = new StringTableChunk();
  addChunk(dosStub);
  addChunk(peHeader);
  addChunk(dataDirectory);
  addChunk(sectionTable);
  addChunk(stringTable);

  // Create sections and add the atoms to them.
  for (auto i : atoms) {
    StringRef sectionName = i.first;
    std::vector<const DefinedAtom *> &contents = i.second;
    std::unique_ptr<SectionChunk> section(
        new AtomChunk(_ctx, sectionName, contents));
    if (section->size() > 0)
      addSectionChunk(std::move(section), sectionTable, stringTable);
  }

  // Build atom to its RVA map.
  for (std::unique_ptr<Chunk> &cp : _chunks)
    if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp))
      chunk->buildAtomRvaMap(_atomRva);

  // We know the addresses of all defined atoms that needs to be
  // relocated. So we can create the ".reloc" section which contains
  // all the relocation sites.
  if (_ctx.getBaseRelocationEnabled()) {
    std::unique_ptr<SectionChunk> baseReloc(new BaseRelocChunk(_chunks, _ctx));
    if (baseReloc->size()) {
      SectionChunk &ref = *baseReloc;
      addSectionChunk(std::move(baseReloc), sectionTable, stringTable);
      dataDirectory->setField(DataDirectoryIndex::BASE_RELOCATION_TABLE,
                              ref.getVirtualAddress(), ref.size());
    }
  }

  setImageSizeOnDisk();

  if (stringTable->size()) {
    peHeader->setPointerToSymbolTable(stringTable->fileOffset());
    peHeader->setNumberOfSymbols(1);
  }

  for (std::unique_ptr<Chunk> &chunk : _chunks) {
    SectionChunk *section = dyn_cast<SectionChunk>(chunk.get());
    if (!section)
      continue;
    if (section->getSectionName() == ".text") {
      peHeader->setBaseOfCode(section->getVirtualAddress());

      // Find the virtual address of the entry point symbol if any.  PECOFF spec
      // says that entry point for dll images is optional, in which case it must
      // be set to 0.
      if (_ctx.hasEntry()) {
        AtomChunk *atom = cast<AtomChunk>(section);
        uint64_t entryPointAddress =
            atom->getAtomVirtualAddress(_ctx.getEntrySymbolName());

        if (entryPointAddress) {
          // NOTE: ARM NT assumes a pure THUMB execution, so adjust the entry
          // point accordingly
          if (_ctx.getMachineType() == llvm::COFF::IMAGE_FILE_MACHINE_ARMNT)
            entryPointAddress |= 1;
          peHeader->setAddressOfEntryPoint(entryPointAddress);
        }
      } else {
        peHeader->setAddressOfEntryPoint(0);
      }
    }
    StringRef name = section->getSectionName();
    if (name == ".data") {
      peHeader->setBaseOfData(section->getVirtualAddress());
      continue;
    }
    DataDirectoryIndex ignore = DataDirectoryIndex(-1);
    DataDirectoryIndex idx = llvm::StringSwitch<DataDirectoryIndex>(name)
        .Case(".pdata", DataDirectoryIndex::EXCEPTION_TABLE)
        .Case(".rsrc", DataDirectoryIndex::RESOURCE_TABLE)
        .Case(".idata.a", DataDirectoryIndex::IAT)
        .Case(".idata.d", DataDirectoryIndex::IMPORT_TABLE)
        .Case(".edata", DataDirectoryIndex::EXPORT_TABLE)
        .Case(".loadcfg", DataDirectoryIndex::LOAD_CONFIG_TABLE)
        .Case(".didat.d", DataDirectoryIndex::DELAY_IMPORT_DESCRIPTOR)
        .Default(ignore);
    if (idx == ignore)
      continue;
    dataDirectory->setField(idx, section->getVirtualAddress(), section->size());
  }

  if (const DefinedAtom *atom = findTLSUsedSymbol(_ctx, linkedFile)) {
    dataDirectory->setField(DataDirectoryIndex::TLS_TABLE, _atomRva[atom],
                            0x18);
  }

  // Now that we know the size and file offset of sections. Set the file
  // header accordingly.
  peHeader->setSizeOfCode(calcSizeOfCode());
  peHeader->setSizeOfInitializedData(calcSizeOfInitializedData());
  peHeader->setSizeOfUninitializedData(calcSizeOfUninitializedData());
  peHeader->setNumberOfSections(_numSections);
  peHeader->setSizeOfImage(_imageSizeInMemory);
  peHeader->setSizeOfHeaders(sectionTable->fileOffset() + sectionTable->size());
}

std::error_code PECOFFWriter::writeFile(const File &linkedFile,
                                        StringRef path) {
  if (_ctx.is64Bit()) {
    this->build<llvm::object::pe32plus_header>(linkedFile);
  } else {
    this->build<llvm::object::pe32_header>(linkedFile);
  }

  uint64_t totalSize =
      _chunks.back()->fileOffset() + _chunks.back()->onDiskSize();
  std::unique_ptr<llvm::FileOutputBuffer> buffer;
  std::error_code ec = llvm::FileOutputBuffer::create(
      path, totalSize, buffer, llvm::FileOutputBuffer::F_executable);
  if (ec)
    return ec;

  for (std::unique_ptr<Chunk> &chunk : _chunks)
    chunk->write(buffer->getBufferStart() + chunk->fileOffset());
  applyAllRelocations(buffer->getBufferStart());
  reorderSEHTableEntries(buffer->getBufferStart());
  DEBUG(printAllAtomAddresses());

  if (_ctx.isDll())
    writeImportLibrary(_ctx);

  return buffer->commit();
}

/// Apply relocations to the output file buffer. This two pass. In the first
/// pass, we visit all atoms to create a map from atom to its virtual
/// address. In the second pass, we visit all relocation references to fix
/// up addresses in the buffer.
void PECOFFWriter::applyAllRelocations(uint8_t *bufferStart) {
  // Create the list of section start addresses. It's needed for
  // relocations of SECREL type.
  std::vector<uint64_t> sectionRva;
  for (auto &cp : _chunks)
    if (SectionChunk *section = dyn_cast<SectionChunk>(&*cp))
      sectionRva.push_back(section->getVirtualAddress());

  uint64_t base = _ctx.getBaseAddress();
  for (auto &cp : _chunks) {
    if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp)) {
      switch (_ctx.getMachineType()) {
      default: llvm_unreachable("unsupported machine type");
      case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT:
        chunk->applyRelocationsARM(bufferStart, _atomRva, sectionRva, base);
        break;
      case llvm::COFF::IMAGE_FILE_MACHINE_I386:
        chunk->applyRelocationsX86(bufferStart, _atomRva, sectionRva, base);
        break;
      case llvm::COFF::IMAGE_FILE_MACHINE_AMD64:
        chunk->applyRelocationsX64(bufferStart, _atomRva, sectionRva, base);
        break;
      }
    }
  }
}

/// Print atom VAs. Used only for debugging.
void PECOFFWriter::printAllAtomAddresses() const {
  for (auto &cp : _chunks)
    if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp))
      chunk->printAtomAddresses(_ctx.getBaseAddress());
}

void PECOFFWriter::reorderSEHTableEntries(uint8_t *bufferStart) {
  auto machineType = _ctx.getMachineType();
  if (machineType == llvm::COFF::IMAGE_FILE_MACHINE_I386)
    reorderSEHTableEntriesX86(bufferStart);
  if (machineType == llvm::COFF::IMAGE_FILE_MACHINE_AMD64)
    reorderSEHTableEntriesX64(bufferStart);
}

/// It seems that the entries in .sxdata must be sorted. This function is called
/// after a COFF file image is created in memory and before it is written to
/// disk. It is safe to reorder entries at this stage because the contents of
/// the entries are RVAs and there's no reference to a .sxdata entry other than
/// to the beginning of the section.
void PECOFFWriter::reorderSEHTableEntriesX86(uint8_t *bufferStart) {
  for (std::unique_ptr<Chunk> &chunk : _chunks) {
    if (SectionChunk *section = dyn_cast<SectionChunk>(chunk.get())) {
      if (section->getSectionName() == ".sxdata") {
        int numEntries = section->size() / sizeof(ulittle32_t);
        ulittle32_t *begin = reinterpret_cast<ulittle32_t *>(bufferStart + section->fileOffset());
        ulittle32_t *end = begin + numEntries;
        std::sort(begin, end);
      }
    }
  }
}

/// The entries in .pdata must be sorted according to its BeginAddress field
/// value. It's safe to do it because of the same reason as .sxdata.
void PECOFFWriter::reorderSEHTableEntriesX64(uint8_t *bufferStart) {
  for (std::unique_ptr<Chunk> &chunk : _chunks) {
    if (SectionChunk *section = dyn_cast<SectionChunk>(chunk.get())) {
      if (section->getSectionName() != ".pdata")
        continue;
      int numEntries = section->size() / sizeof(coff_runtime_function_x64);
      coff_runtime_function_x64 *begin =
          (coff_runtime_function_x64 *)(bufferStart + section->fileOffset());
      coff_runtime_function_x64 *end = begin + numEntries;
      std::sort(begin, end, [](const coff_runtime_function_x64 &lhs,
                               const coff_runtime_function_x64 &rhs) {
        return lhs.BeginAddress < rhs.BeginAddress;
      });
    }
  }
}

void PECOFFWriter::addChunk(Chunk *chunk) {
  _chunks.push_back(std::unique_ptr<Chunk>(chunk));
}

void PECOFFWriter::addSectionChunk(std::unique_ptr<SectionChunk> chunk,
                                   SectionHeaderTableChunk *table,
                                   StringTableChunk *stringTable) {
  table->addSection(chunk.get());
  _numSections++;

  StringRef sectionName = chunk->getSectionName();
  if (sectionName.size() > llvm::COFF::NameSize) {
    uint32_t stringTableOffset = stringTable->addSectionName(sectionName);
    chunk->setStringTableOffset(stringTableOffset);
  }

  // Compute and set the starting address of sections when loaded in
  // memory. They are different from positions on disk because sections need
  // to be sector-aligned on disk but page-aligned in memory.
  _imageSizeInMemory = llvm::RoundUpToAlignment(
      _imageSizeInMemory, chunk->memAlign());
  chunk->setVirtualAddress(_imageSizeInMemory);
  _imageSizeInMemory = llvm::RoundUpToAlignment(
      _imageSizeInMemory + chunk->size(), _ctx.getPageSize());
  _chunks.push_back(std::move(chunk));
}

void PECOFFWriter::setImageSizeOnDisk() {
  for (auto &chunk : _chunks) {
    // Compute and set the offset of the chunk in the output file.
    _imageSizeOnDisk =
        llvm::RoundUpToAlignment(_imageSizeOnDisk, chunk->align());
    chunk->setFileOffset(_imageSizeOnDisk);
    _imageSizeOnDisk += chunk->onDiskSize();
  }
}

uint64_t PECOFFWriter::calcSectionSize(
    llvm::COFF::SectionCharacteristics sectionType) const {
  uint64_t ret = 0;
  for (auto &cp : _chunks)
    if (SectionChunk *chunk = dyn_cast<SectionChunk>(&*cp))
      if (chunk->getCharacteristics() & sectionType)
        ret += chunk->onDiskSize();
  return ret;
}

} // end namespace pecoff

std::unique_ptr<Writer> createWriterPECOFF(const PECOFFLinkingContext &info) {
  return std::unique_ptr<Writer>(new pecoff::PECOFFWriter(info));
}

} // end namespace lld
