[lld] r195159 - Do not inline large member functions.

Rui Ueyama ruiu at google.com
Tue Nov 19 14:12:27 PST 2013


Author: ruiu
Date: Tue Nov 19 16:12:26 2013
New Revision: 195159

URL: http://llvm.org/viewvc/llvm-project?rev=195159&view=rev
Log:
Do not inline large member functions.

This should improve code readability as the class definitions are now
more readable than before.

Modified:
    lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp

Modified: lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp?rev=195159&r1=195158&r2=195159&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp (original)
+++ lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp Tue Nov 19 16:12:26 2013
@@ -129,105 +129,9 @@ private:
 /// A PEHeaderChunk represents PE header including COFF header.
 class PEHeaderChunk : public HeaderChunk {
 public:
-  explicit PEHeaderChunk(const PECOFFLinkingContext &context) : 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 = context.getMachineType();
-    _coffHeader.TimeDateStamp = time(NULL);
-
-    // The size of PE header including optional data directory is always 224.
-    _coffHeader.SizeOfOptionalHeader = 224;
-
-    // Attributes of the executable.
-    uint16_t characteristics = llvm::COFF::IMAGE_FILE_32BIT_MACHINE |
-                               llvm::COFF::IMAGE_FILE_EXECUTABLE_IMAGE;
-    if (context.getLargeAddressAware())
-      characteristics |= llvm::COFF::IMAGE_FILE_LARGE_ADDRESS_AWARE;
-    if (context.getSwapRunFromCD())
-      characteristics |= llvm::COFF::IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP;
-    if (context.getSwapRunFromNet())
-      characteristics |= llvm::COFF::IMAGE_FILE_NET_RUN_FROM_SWAP;
-    if (!context.getBaseRelocationEnabled())
-      characteristics |= llvm::COFF::IMAGE_FILE_RELOCS_STRIPPED;
-
-    _coffHeader.Characteristics = characteristics;
-
-    // 0x10b indicates a normal PE32 executable. For PE32+ it should be 0x20b.
-    _peHeader.Magic = 0x10b;
-
-    // The address of entry point relative to ImageBase. Windows executable
-    // usually starts at address 0x401000.
-    _peHeader.AddressOfEntryPoint = 0x1000;
-
-    // The address of the executable when loaded into memory. The default for
-    // DLLs is 0x10000000. The default for executables is 0x400000.
-    _peHeader.ImageBase = context.getBaseAddress();
-
-    // Sections should be page-aligned when loaded into memory, which is 4KB on
-    // x86.
-    _peHeader.SectionAlignment = context.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 = context.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 = context.getMinOSVersion();
-    _peHeader.MajorOperatingSystemVersion = minOSVersion.majorVersion;
-    _peHeader.MinorOperatingSystemVersion = minOSVersion.minorVersion;
-    _peHeader.MajorSubsystemVersion = minOSVersion.majorVersion;
-    _peHeader.MinorSubsystemVersion = minOSVersion.minorVersion;
-
-    _peHeader.Subsystem = context.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 =
-        llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_SEH;
-    if (context.isTerminalServerAware())
-      dllCharacteristics |=
-          llvm::COFF::IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
-    if (context.isNxCompat())
-      dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
-    if (context.getDynamicBaseEnabled())
-      dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
-    if (!context.getAllowBind())
-      dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_BIND;
-    if (!context.getAllowIsolation())
-      dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;
-    _peHeader.DLLCharacteristics = dllCharacteristics;
-
-    _peHeader.SizeOfStackReserve = context.getStackReserve();
-    _peHeader.SizeOfStackCommit = context.getStackCommit();
-    _peHeader.SizeOfHeapReserve = context.getHeapReserve();
-    _peHeader.SizeOfHeapCommit = context.getHeapCommit();
-
-    // The number of data directory entries. We always have 16 entries.
-    _peHeader.NumberOfRvaAndSize = 16;
-  }
-
-  virtual void write(uint8_t *fileBuffer) {
-    fileBuffer += fileOffset();
-    std::memcpy(fileBuffer, llvm::COFF::PEMagic, sizeof(llvm::COFF::PEMagic));
-    fileBuffer += sizeof(llvm::COFF::PEMagic);
-    std::memcpy(fileBuffer, &_coffHeader, sizeof(_coffHeader));
-    fileBuffer += sizeof(_coffHeader);
-    std::memcpy(fileBuffer, &_peHeader, sizeof(_peHeader));
-  }
+  explicit PEHeaderChunk(const PECOFFLinkingContext &context);
+
+  virtual void write(uint8_t *fileBuffer);
 
   virtual void setSizeOfHeaders(uint64_t size) {
     // Must be multiple of FileAlignment.
@@ -281,14 +185,7 @@ private:
 /// An AtomChunk represents a section containing atoms.
 class AtomChunk : public Chunk {
 public:
-  virtual void write(uint8_t *fileBuffer) {
-    for (const auto *layout : _atomLayouts) {
-      const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
-      ArrayRef<uint8_t> rawContent = atom->rawContent();
-      std::memcpy(fileBuffer + layout->_fileOffset, rawContent.data(),
-                  rawContent.size());
-    }
-  }
+  virtual void write(uint8_t *fileBuffer);
 
   /// Add all atoms to the given map. This data will be used to do relocation.
   void buildAtomToVirtualAddr(std::map<const Atom *, uint64_t> &atomRva) {
@@ -298,74 +195,9 @@ public:
 
   void applyRelocations(uint8_t *fileBuffer,
                         std::map<const Atom *, uint64_t> &atomRva,
-                        uint64_t imageBaseAddress) {
-    for (const auto *layout : _atomLayouts) {
-      const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
-      for (const Reference *ref : *atom) {
-        auto relocSite = reinterpret_cast<ulittle32_t *>(
-            fileBuffer + layout->_fileOffset + ref->offsetInAtom());
-        uint64_t targetAddr = atomRva[ref->target()];
-        // Also account for whatever offset is already stored at the relocation site.
-        targetAddr += *relocSite;
-
-        // Skip if this reference is not for relocation.
-        if (ref->kind() < lld::Reference::kindTargetLow)
-          continue;
-
-        switch (ref->kind()) {
-        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.
-          *relocSite = targetAddr + imageBaseAddress;
-          break;
-        case llvm::COFF::IMAGE_REL_I386_DIR32NB:
-          // Set target's 32-bit RVA.
-          *relocSite = 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;
-          *relocSite = targetAddr - disp;
-          break;
-        }
-        default:
-          llvm_unreachable("Unsupported relocation kind");
-        }
-      }
-    }
-  }
-
-  /// Print atom VAs. Used only for debugging.
-  void printAtomAddresses(uint64_t baseAddr) {
-    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 addBaseRelocations(std::vector<uint64_t> &relocSites) {
-    // TODO: llvm-objdump doesn't support parsing the base relocation table, so
-    // we can't really test this at the moment. As a temporary solution, we
-    // should output debug messages with atom names and addresses so that we
-    // can inspect relocations, and fix the tests (base-reloc.test, maybe
-    // others) to use those messages.
-    for (const auto *layout : _atomLayouts) {
-      const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
-      for (const Reference *ref : *atom)
-        if (ref->kind() == llvm::COFF::IMAGE_REL_I386_DIR32)
-          relocSites.push_back(layout->_virtualAddr + ref->offsetInAtom());
-    }
-  }
+                        uint64_t imageBaseAddress);
+  void printAtomAddresses(uint64_t baseAddr);
+  void addBaseRelocations(std::vector<uint64_t> &relocSites);
 
   // Set the file offset of the beginning of this section.
   virtual void setFileOffset(uint64_t fileOffset) {
@@ -426,22 +258,7 @@ public:
     _baseRelocSize = size;
   }
 
-  virtual void write(uint8_t *fileBuffer) {
-    if (!_atomLayouts.empty()) {
-      assert(_atomLayouts.size() == 1);
-      const AtomLayout *layout = _atomLayouts[0];
-      ArrayRef<uint8_t> content = static_cast<const DefinedAtom *>(layout->_atom)->rawContent();
-      std::memcpy(fileBuffer + _fileOffset, content.data(), content.size());
-    }
-
-    // Write base relocation table entry.
-    int baseRelocOffset = llvm::COFF::DataDirectoryIndex::BASE_RELOCATION_TABLE
-        * sizeof(llvm::object::data_directory);
-    auto *baseReloc = reinterpret_cast<llvm::object::data_directory *>(
-        fileBuffer + _fileOffset + baseRelocOffset);
-    baseReloc->RelativeVirtualAddress = _baseRelocAddr;
-    baseReloc->Size = _baseRelocSize;
-  }
+  virtual void write(uint8_t *fileBuffer);
 
 private:
   uint32_t _baseRelocAddr;
@@ -478,123 +295,32 @@ public:
 
   virtual uint32_t getVirtualAddress() { return _sectionHeader.VirtualAddress; }
 
-  virtual llvm::object::coff_section &getSectionHeader() {
-    // Fix up section size before returning it. VirtualSize should be the size
-    // of the actual content, and SizeOfRawData should be aligned to the section
-    // alignment.
-    _sectionHeader.VirtualSize = _size;
-    _sectionHeader.SizeOfRawData = size();
-    return _sectionHeader;
-  }
-
-  ulittle32_t getSectionCharacteristics() {
-    return _sectionHeader.Characteristics;
-  }
-
-  void 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, 1 << atom->alignment().powerOf2);
-
-    // Create an AtomLayout and move the current pointer.
-    auto *layout = new (_alloc) AtomLayout(atom, _size, _size);
-    _atomLayouts.push_back(layout);
-    _size += atom->size();
-  }
+  virtual llvm::object::coff_section &getSectionHeader();
+
+  ulittle32_t getSectionCharacteristics();
+
+  void appendAtom(const DefinedAtom *atom);
 
   static bool classof(const Chunk *c) { return c->getKind() == kindSection; }
 
 protected:
-  SectionChunk(StringRef sectionName, uint32_t characteristics)
-      : AtomChunk(kindSection),
-        _sectionHeader(createSectionHeader(sectionName, characteristics)) {
-    // The section should be aligned to disk sector.
-    _align = SECTOR_SIZE;
-  }
+  SectionChunk(StringRef sectionName, uint32_t characteristics);
 
   void buildContents(const File &linkedFile,
-                     bool (*isEligible)(const DefinedAtom *)) {
-    // Extract atoms from the linked file and append them to this section.
-    for (const DefinedAtom *atom : linkedFile.defined()) {
-      assert(atom->sectionChoice() == DefinedAtom::sectionBasedOnContent);
-      if (isEligible(atom))
-        appendAtom(atom);
-    }
-
-    // Now that we have a list of atoms that to be written in this section,
-    // and we know the size of the section. Let's write them to the section
-    // header. VirtualSize should be the size of the actual content, and
-    // SizeOfRawData should be aligned to the section alignment.
-    _sectionHeader.VirtualSize = _size;
-    _sectionHeader.SizeOfRawData = size();
-  }
+                     bool (*isEligible)(const DefinedAtom *));
 
 private:
   llvm::object::coff_section
-  createSectionHeader(StringRef sectionName, uint32_t characteristics) const {
-    llvm::object::coff_section header;
-
-    // Section name equal to or shorter than 8 byte fits in the section
-    // header. Longer names should be stored to string table, which is not
-    // implemented yet.
-    if (sizeof(header.Name) < sectionName.size())
-      llvm_unreachable("Cannot handle section name longer than 8 byte");
-
-    // Name field must be NUL-padded. If the name is exactly 8 byte long,
-    // there's no terminating NUL.
-    std::memset(header.Name, 0, sizeof(header.Name));
-    std::strncpy(header.Name, sectionName.data(), sizeof(header.Name));
-
-    header.VirtualSize = 0;
-    header.VirtualAddress = 0;
-    header.SizeOfRawData = 0;
-    header.PointerToRawData = 0;
-    header.PointerToRelocations = 0;
-    header.PointerToLinenumbers = 0;
-    header.NumberOfRelocations = 0;
-    header.NumberOfLinenumbers = 0;
-    header.Characteristics = characteristics;
-    return header;
-  }
+  createSectionHeader(StringRef sectionName, uint32_t characteristics) const;
 
   llvm::object::coff_section _sectionHeader;
   mutable llvm::BumpPtrAllocator _alloc;
 };
 
-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 *fileBuffer) {
-  uint64_t offset = 0;
-  fileBuffer += fileOffset();
-  for (const auto &chunk : _sections) {
-    const llvm::object::coff_section &header = chunk->getSectionHeader();
-    std::memcpy(fileBuffer + offset, &header, sizeof(header));
-    offset += sizeof(header);
-  }
-}
-
 // \brief A TextSectionChunk represents a .text section.
 class TextSectionChunk : public SectionChunk {
 public:
-  virtual void write(uint8_t *fileBuffer) {
-    if (_atomLayouts.empty())
-      return;
-    // 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 = fileBuffer + _atomLayouts.front()->_fileOffset;
-    uint8_t *end = fileBuffer + _atomLayouts.back()->_fileOffset;
-    memset(start, 0xCC, end - start);
-
-    SectionChunk::write(fileBuffer);
-  }
+  virtual void write(uint8_t *fileBuffer);
 
   TextSectionChunk(const File &linkedFile)
       : SectionChunk(".text", characteristics) {
@@ -716,24 +442,7 @@ public:
   BaseRelocChunk(const File &linkedFile)
       : SectionChunk(".reloc", characteristics), _file(linkedFile) {}
 
-  /// 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.
-  void setContents(ChunkVectorT &chunks) {
-    std::vector<uint64_t> relocSites = listRelocSites(chunks);
-    PageOffsetT blocks = groupByPage(relocSites);
-    for (auto &i : blocks) {
-      uint64_t pageAddr = i.first;
-      const std::vector<uint16_t> &offsetsInPage = i.second;
-      appendAtom(createBaseRelocBlock(_file, pageAddr, offsetsInPage));
-    }
-  }
+  void setContents(ChunkVectorT &chunks);
 
 private:
   // When loaded into memory, reloc section should be readable and writable.
@@ -744,222 +453,413 @@ private:
 
   // 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<uint64_t> listRelocSites(ChunkVectorT &chunks) {
-    std::vector<uint64_t> ret;
-    for (auto &cp : chunks)
-      if (SectionChunk *chunk = dyn_cast<SectionChunk>(&*cp))
-        chunk->addBaseRelocations(ret);
-    return ret;
-  }
+  std::vector<uint64_t> listRelocSites(ChunkVectorT &chunks);
 
   // Divide the given RVAs into blocks.
-  PageOffsetT groupByPage(std::vector<uint64_t> relocSites) {
-    PageOffsetT blocks;
-    uint64_t mask = static_cast<uint64_t>(PAGE_SIZE) - 1;
-    for (uint64_t addr : relocSites)
-      blocks[addr & ~mask].push_back(addr & mask);
-    return blocks;
-  }
+  PageOffsetT groupByPage(std::vector<uint64_t> relocSites);
 
   // Create the content of a relocation block.
   DefinedAtom *createBaseRelocBlock(const File &file, uint64_t pageAddr,
-                                    const std::vector<uint16_t> &offsets) {
-    // 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) * offsets.size(), sizeof(ulittle32_t));
-    std::vector<uint8_t> contents(size);
-    uint8_t *ptr = &contents[0];
-
-    // The first four bytes is the page RVA.
-    *reinterpret_cast<ulittle32_t *>(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.
-    *reinterpret_cast<ulittle32_t *>(ptr) = size;
-    ptr += sizeof(ulittle32_t);
-
-    // The rest of the block consists of offsets in the page.
-    for (uint16_t offset : offsets) {
-      assert(offset < PAGE_SIZE);
-      uint16_t val = (llvm::COFF::IMAGE_REL_BASED_HIGHLOW << 12) | offset;
-      *reinterpret_cast<ulittle16_t *>(ptr) = val;
-      ptr += sizeof(ulittle16_t);
-    }
-    return new (_alloc) BaseRelocAtom(file, std::move(contents));
-  }
+                                    const std::vector<uint16_t> &offsets);
 
   mutable llvm::BumpPtrAllocator _alloc;
   const File &_file;
 };
 
-}  // end anonymous namespace
+PEHeaderChunk::PEHeaderChunk(const PECOFFLinkingContext &context)
+    : 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 = context.getMachineType();
+  _coffHeader.TimeDateStamp = time(NULL);
+
+  // The size of PE header including optional data directory is always 224.
+  _coffHeader.SizeOfOptionalHeader = 224;
+
+  // Attributes of the executable.
+  uint16_t characteristics = llvm::COFF::IMAGE_FILE_32BIT_MACHINE |
+                             llvm::COFF::IMAGE_FILE_EXECUTABLE_IMAGE;
+  if (context.getLargeAddressAware())
+    characteristics |= llvm::COFF::IMAGE_FILE_LARGE_ADDRESS_AWARE;
+  if (context.getSwapRunFromCD())
+    characteristics |= llvm::COFF::IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP;
+  if (context.getSwapRunFromNet())
+    characteristics |= llvm::COFF::IMAGE_FILE_NET_RUN_FROM_SWAP;
+  if (!context.getBaseRelocationEnabled())
+    characteristics |= llvm::COFF::IMAGE_FILE_RELOCS_STRIPPED;
+
+  _coffHeader.Characteristics = characteristics;
+
+  // 0x10b indicates a normal PE32 executable. For PE32+ it should be 0x20b.
+  _peHeader.Magic = 0x10b;
+
+  // The address of entry point relative to ImageBase. Windows executable
+  // usually starts at address 0x401000.
+  _peHeader.AddressOfEntryPoint = 0x1000;
+
+  // The address of the executable when loaded into memory. The default for
+  // DLLs is 0x10000000. The default for executables is 0x400000.
+  _peHeader.ImageBase = context.getBaseAddress();
+
+  // Sections should be page-aligned when loaded into memory, which is 4KB on
+  // x86.
+  _peHeader.SectionAlignment = context.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 = context.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 = context.getMinOSVersion();
+  _peHeader.MajorOperatingSystemVersion = minOSVersion.majorVersion;
+  _peHeader.MinorOperatingSystemVersion = minOSVersion.minorVersion;
+  _peHeader.MajorSubsystemVersion = minOSVersion.majorVersion;
+  _peHeader.MinorSubsystemVersion = minOSVersion.minorVersion;
+
+  _peHeader.Subsystem = context.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 = llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_SEH;
+  if (context.isTerminalServerAware())
+    dllCharacteristics |=
+        llvm::COFF::IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
+  if (context.isNxCompat())
+    dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
+  if (context.getDynamicBaseEnabled())
+    dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
+  if (!context.getAllowBind())
+    dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_BIND;
+  if (!context.getAllowIsolation())
+    dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;
+  _peHeader.DLLCharacteristics = dllCharacteristics;
+
+  _peHeader.SizeOfStackReserve = context.getStackReserve();
+  _peHeader.SizeOfStackCommit = context.getStackCommit();
+  _peHeader.SizeOfHeapReserve = context.getHeapReserve();
+  _peHeader.SizeOfHeapCommit = context.getHeapCommit();
 
-class ExecutableWriter : public Writer {
-public:
-  explicit ExecutableWriter(const PECOFFLinkingContext &context)
-      : _PECOFFLinkingContext(context), _numSections(0),
-        _imageSizeInMemory(PAGE_SIZE), _imageSizeOnDisk(0) {}
+  // The number of data directory entries. We always have 16 entries.
+  _peHeader.NumberOfRvaAndSize = 16;
+}
 
-  // Create all chunks that consist of the output file.
-  void build(const File &linkedFile) {
-    // Create file chunks and add them to the list.
-    auto *dosStub = new DOSStubChunk(_PECOFFLinkingContext);
-    auto *peHeader = new PEHeaderChunk(_PECOFFLinkingContext);
-    auto *dataDirectory = new DataDirectoryChunk(linkedFile);
-    auto *sectionTable = new SectionHeaderTableChunk();
-    auto *text = new TextSectionChunk(linkedFile);
-    auto *rdata = new RDataSectionChunk(linkedFile);
-    auto *data = new DataSectionChunk(linkedFile);
-    auto *bss = new BssSectionChunk(linkedFile);
-    BaseRelocChunk *baseReloc = nullptr;
-    if (_PECOFFLinkingContext.getBaseRelocationEnabled())
-      baseReloc = new BaseRelocChunk(linkedFile);
-
-    addChunk(dosStub);
-    addChunk(peHeader);
-    addChunk(dataDirectory);
-    addChunk(sectionTable);
-
-    // Do not add the empty section. Windows loader does not like a section of
-    // size zero and rejects such executable.
-    if (text->size())
-      addSectionChunk(text, sectionTable);
-    if (rdata->size())
-      addSectionChunk(rdata, sectionTable);
-    if (data->size())
-      addSectionChunk(data, sectionTable);
-    if (bss->size())
-      addSectionChunk(bss, sectionTable);
-
-    // Now that 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 (baseReloc) {
-      baseReloc->setContents(_chunks);
-      if (baseReloc->size()) {
-        addSectionChunk(baseReloc, sectionTable);
-        dataDirectory->setBaseRelocField(baseReloc->getSectionRva(),
-                                         baseReloc->rawSize());
+void PEHeaderChunk::write(uint8_t *fileBuffer) {
+  fileBuffer += fileOffset();
+  std::memcpy(fileBuffer, llvm::COFF::PEMagic, sizeof(llvm::COFF::PEMagic));
+  fileBuffer += sizeof(llvm::COFF::PEMagic);
+  std::memcpy(fileBuffer, &_coffHeader, sizeof(_coffHeader));
+  fileBuffer += sizeof(_coffHeader);
+  std::memcpy(fileBuffer, &_peHeader, sizeof(_peHeader));
+}
+
+void AtomChunk::write(uint8_t *fileBuffer) {
+  for (const auto *layout : _atomLayouts) {
+    const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
+    ArrayRef<uint8_t> rawContent = atom->rawContent();
+    std::memcpy(fileBuffer + layout->_fileOffset, rawContent.data(),
+                rawContent.size());
+  }
+}
+
+void AtomChunk::applyRelocations(uint8_t *fileBuffer,
+                                 std::map<const Atom *, uint64_t> &atomRva,
+                                 uint64_t imageBaseAddress) {
+  for (const auto *layout : _atomLayouts) {
+    const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
+    for (const Reference *ref : *atom) {
+      auto relocSite = reinterpret_cast<ulittle32_t *>(
+          fileBuffer + layout->_fileOffset + ref->offsetInAtom());
+      uint64_t targetAddr = atomRva[ref->target()];
+      // Also account for whatever offset is already stored at the relocation
+      // site.
+      targetAddr += *relocSite;
+
+      // Skip if this reference is not for relocation.
+      if (ref->kind() < lld::Reference::kindTargetLow)
+        continue;
+
+      switch (ref->kind()) {
+      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.
+        *relocSite = targetAddr + imageBaseAddress;
+        break;
+      case llvm::COFF::IMAGE_REL_I386_DIR32NB:
+        // Set target's 32-bit RVA.
+        *relocSite = 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;
+        *relocSite = targetAddr - disp;
+        break;
+      }
+      default:
+        llvm_unreachable("Unsupported relocation kind");
       }
     }
+  }
+}
 
-    setImageSizeOnDisk();
+/// Print atom VAs. Used only for debugging.
+void AtomChunk::printAtomAddresses(uint64_t baseAddr) {
+  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";
+  }
+}
 
-    // Now that we know the size and file offset of sections. Set the file
-    // header accordingly.
-    peHeader->setSizeOfCode(calcSizeOfCode());
-    if (text->size()) {
-      peHeader->setBaseOfCode(text->getVirtualAddress());
-    }
-    if (rdata->size()) {
-      peHeader->setBaseOfData(rdata->getVirtualAddress());
-    } else if (data->size()) {
-      peHeader->setBaseOfData(data->getVirtualAddress());
-    }
-    peHeader->setSizeOfInitializedData(calcSizeOfInitializedData());
-    peHeader->setSizeOfUninitializedData(calcSizeOfUninitializedData());
-    peHeader->setNumberOfSections(_numSections);
-    peHeader->setSizeOfImage(_imageSizeInMemory);
-
-    // The combined size of the DOS, PE and section headers including garbage
-    // between the end of the header and the beginning of the first section.
-    peHeader->setSizeOfHeaders(dosStub->size() + peHeader->size() +
-        sectionTable->size() + dataDirectory->size());
-
-    setAddressOfEntryPoint(text, peHeader);
-  }
-
-  virtual error_code writeFile(const File &linkedFile, StringRef path) {
-    this->build(linkedFile);
-
-    uint64_t totalSize = _chunks.back()->fileOffset() + _chunks.back()->size();
-    OwningPtr<llvm::FileOutputBuffer> buffer;
-    error_code ec = llvm::FileOutputBuffer::create(
-        path, totalSize, buffer, llvm::FileOutputBuffer::F_executable);
-    if (ec)
-      return ec;
-
-    for (const auto &chunk : _chunks)
-      chunk->write(buffer->getBufferStart());
-    applyAllRelocations(buffer->getBufferStart());
-    DEBUG(printAllAtomAddresses());
-    return buffer->commit();
+/// 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<uint64_t> &relocSites) {
+  // TODO: llvm-objdump doesn't support parsing the base relocation table, so
+  // we can't really test this at the moment. As a temporary solution, we
+  // should output debug messages with atom names and addresses so that we
+  // can inspect relocations, and fix the tests (base-reloc.test, maybe
+  // others) to use those messages.
+  for (const auto *layout : _atomLayouts) {
+    const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
+    for (const Reference *ref : *atom)
+      if (ref->kind() == llvm::COFF::IMAGE_REL_I386_DIR32)
+        relocSites.push_back(layout->_virtualAddr + ref->offsetInAtom());
   }
+}
 
-private:
-  /// 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 applyAllRelocations(uint8_t *bufferStart) {
-    for (auto &cp : _chunks)
-      if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp))
-        chunk->applyRelocations(bufferStart, atomRva,
-                                _PECOFFLinkingContext.getBaseAddress());
-  }
-
-  /// Print atom VAs. Used only for debugging.
-  void printAllAtomAddresses() {
-    for (auto &cp : _chunks)
-      if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp))
-        chunk->printAtomAddresses(_PECOFFLinkingContext.getBaseAddress());
-  }
-
-  void addChunk(Chunk *chunk) {
-    _chunks.push_back(std::unique_ptr<Chunk>(chunk));
-  }
-
-  void addSectionChunk(SectionChunk *chunk,
-                       SectionHeaderTableChunk *table) {
-    _chunks.push_back(std::unique_ptr<Chunk>(chunk));
-    table->addSection(chunk);
-    _numSections++;
-
-    // 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.
-    chunk->setVirtualAddress(_imageSizeInMemory);
-    chunk->buildAtomToVirtualAddr(atomRva);
-    _imageSizeInMemory = llvm::RoundUpToAlignment(
-        _imageSizeInMemory + chunk->size(), PAGE_SIZE);
-  }
-
-  void 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->size();
-    }
+void DataDirectoryChunk::write(uint8_t *fileBuffer) {
+  if (!_atomLayouts.empty()) {
+    assert(_atomLayouts.size() == 1);
+    const AtomLayout *layout = _atomLayouts[0];
+    ArrayRef<uint8_t> content =
+        static_cast<const DefinedAtom *>(layout->_atom)->rawContent();
+    std::memcpy(fileBuffer + _fileOffset, content.data(), content.size());
+  }
+
+  // Write base relocation table entry.
+  int baseRelocOffset = llvm::COFF::DataDirectoryIndex::BASE_RELOCATION_TABLE *
+                        sizeof(llvm::object::data_directory);
+  auto *baseReloc = reinterpret_cast<llvm::object::data_directory *>(
+      fileBuffer + _fileOffset + baseRelocOffset);
+  baseReloc->RelativeVirtualAddress = _baseRelocAddr;
+  baseReloc->Size = _baseRelocSize;
+}
+
+llvm::object::coff_section &SectionChunk::getSectionHeader() {
+  // Fix up section size before returning it. VirtualSize should be the size
+  // of the actual content, and SizeOfRawData should be aligned to the section
+  // alignment.
+  _sectionHeader.VirtualSize = _size;
+  _sectionHeader.SizeOfRawData = size();
+  return _sectionHeader;
+}
+
+ulittle32_t SectionChunk::getSectionCharacteristics() {
+  return _sectionHeader.Characteristics;
+}
+
+void SectionChunk::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, 1 << atom->alignment().powerOf2);
+
+  // Create an AtomLayout and move the current pointer.
+  auto *layout = new (_alloc) AtomLayout(atom, _size, _size);
+  _atomLayouts.push_back(layout);
+  _size += atom->size();
+}
+
+SectionChunk::SectionChunk(StringRef sectionName, uint32_t characteristics)
+    : AtomChunk(kindSection),
+      _sectionHeader(createSectionHeader(sectionName, characteristics)) {
+  // The section should be aligned to disk sector.
+  _align = SECTOR_SIZE;
+}
+
+void SectionChunk::buildContents(const File &linkedFile,
+                                 bool (*isEligible)(const DefinedAtom *)) {
+  // Extract atoms from the linked file and append them to this section.
+  for (const DefinedAtom *atom : linkedFile.defined()) {
+    assert(atom->sectionChoice() == DefinedAtom::sectionBasedOnContent);
+    if (isEligible(atom))
+      appendAtom(atom);
   }
 
-  void setAddressOfEntryPoint(TextSectionChunk *text, PEHeaderChunk *peHeader) {
-    // 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 (_PECOFFLinkingContext.entrySymbolName().empty() &&
-        _PECOFFLinkingContext.getImageType()
-                              == PECOFFLinkingContext::IMAGE_DLL) {
-      peHeader->setAddressOfEntryPoint(0);
-    } else {
-      uint64_t entryPointAddress = text->getAtomVirtualAddress(
-        _PECOFFLinkingContext.entrySymbolName());
-      if (entryPointAddress != 0)
-        peHeader->setAddressOfEntryPoint(entryPointAddress);
-    }
+  // Now that we have a list of atoms that to be written in this section,
+  // and we know the size of the section. Let's write them to the section
+  // header. VirtualSize should be the size of the actual content, and
+  // SizeOfRawData should be aligned to the section alignment.
+  _sectionHeader.VirtualSize = _size;
+  _sectionHeader.SizeOfRawData = size();
+}
+
+llvm::object::coff_section
+SectionChunk::createSectionHeader(StringRef sectionName,
+                                  uint32_t characteristics) const {
+  llvm::object::coff_section header;
+
+  // Section name equal to or shorter than 8 byte fits in the section
+  // header. Longer names should be stored to string table, which is not
+  // implemented yet.
+  if (sizeof(header.Name) < sectionName.size())
+    llvm_unreachable("Cannot handle section name longer than 8 byte");
+
+  // Name field must be NUL-padded. If the name is exactly 8 byte long,
+  // there's no terminating NUL.
+  std::memset(header.Name, 0, sizeof(header.Name));
+  std::strncpy(header.Name, sectionName.data(), sizeof(header.Name));
+
+  header.VirtualSize = 0;
+  header.VirtualAddress = 0;
+  header.SizeOfRawData = 0;
+  header.PointerToRawData = 0;
+  header.PointerToRelocations = 0;
+  header.PointerToLinenumbers = 0;
+  header.NumberOfRelocations = 0;
+  header.NumberOfLinenumbers = 0;
+  header.Characteristics = characteristics;
+  return header;
+}
+
+void TextSectionChunk::write(uint8_t *fileBuffer) {
+  if (_atomLayouts.empty())
+    return;
+  // 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 = fileBuffer + _atomLayouts.front()->_fileOffset;
+  uint8_t *end = fileBuffer + _atomLayouts.back()->_fileOffset;
+  memset(start, 0xCC, end - start);
+  SectionChunk::write(fileBuffer);
+}
+
+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 *fileBuffer) {
+  uint64_t offset = 0;
+  fileBuffer += fileOffset();
+  for (const auto &chunk : _sections) {
+    const llvm::object::coff_section &header = chunk->getSectionHeader();
+    std::memcpy(fileBuffer + offset, &header, sizeof(header));
+    offset += sizeof(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.
+void BaseRelocChunk::setContents(ChunkVectorT &chunks) {
+  std::vector<uint64_t> relocSites = listRelocSites(chunks);
+  PageOffsetT blocks = groupByPage(relocSites);
+  for (auto &i : blocks) {
+    uint64_t pageAddr = i.first;
+    const std::vector<uint16_t> &offsetsInPage = i.second;
+    appendAtom(createBaseRelocBlock(_file, pageAddr, offsetsInPage));
   }
+}
+
+// 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<uint64_t> BaseRelocChunk::listRelocSites(ChunkVectorT &chunks) {
+  std::vector<uint64_t> ret;
+  for (auto &cp : chunks)
+    if (SectionChunk *chunk = dyn_cast<SectionChunk>(&*cp))
+      chunk->addBaseRelocations(ret);
+  return ret;
+}
+
+// Divide the given RVAs into blocks.
+BaseRelocChunk::PageOffsetT
+BaseRelocChunk::groupByPage(std::vector<uint64_t> relocSites) {
+  PageOffsetT blocks;
+  uint64_t mask = static_cast<uint64_t>(PAGE_SIZE) - 1;
+  for (uint64_t addr : relocSites)
+    blocks[addr & ~mask].push_back(addr & mask);
+  return blocks;
+}
 
-  uint64_t calcSectionSize(llvm::COFF::SectionCharacteristics sectionType) {
-    uint64_t ret = 0;
-    for (auto &cp : _chunks)
-      if (SectionChunk *chunk = dyn_cast<SectionChunk>(&*cp))
-        if (chunk->getSectionCharacteristics() & sectionType)
-          ret += chunk->size();
-    return ret;
+// Create the content of a relocation block.
+DefinedAtom *
+BaseRelocChunk::createBaseRelocBlock(const File &file, uint64_t pageAddr,
+                                     const std::vector<uint16_t> &offsets) {
+  // 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) * offsets.size(),
+      sizeof(ulittle32_t));
+  std::vector<uint8_t> contents(size);
+  uint8_t *ptr = &contents[0];
+
+  // The first four bytes is the page RVA.
+  *reinterpret_cast<ulittle32_t *>(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.
+  *reinterpret_cast<ulittle32_t *>(ptr) = size;
+  ptr += sizeof(ulittle32_t);
+
+  // The rest of the block consists of offsets in the page.
+  for (uint16_t offset : offsets) {
+    assert(offset < PAGE_SIZE);
+    uint16_t val = (llvm::COFF::IMAGE_REL_BASED_HIGHLOW << 12) | offset;
+    *reinterpret_cast<ulittle16_t *>(ptr) = val;
+    ptr += sizeof(ulittle16_t);
   }
+  return new (_alloc) BaseRelocAtom(file, std::move(contents));
+}
+
+} // end anonymous namespace
+
+class ExecutableWriter : public Writer {
+public:
+  explicit ExecutableWriter(const PECOFFLinkingContext &context)
+      : _PECOFFLinkingContext(context), _numSections(0),
+        _imageSizeInMemory(PAGE_SIZE), _imageSizeOnDisk(0) {}
+
+  void build(const File &linkedFile);
+  virtual error_code writeFile(const File &linkedFile, StringRef path);
+
+private:
+  void applyAllRelocations(uint8_t *bufferStart);
+  void printAllAtomAddresses();
+  void addChunk(Chunk *chunk);
+  void addSectionChunk(SectionChunk *chunk, SectionHeaderTableChunk *table);
+  void setImageSizeOnDisk();
+  void setAddressOfEntryPoint(TextSectionChunk *text, PEHeaderChunk *peHeader);
+  uint64_t calcSectionSize(llvm::COFF::SectionCharacteristics sectionType);
 
   uint64_t calcSizeOfInitializedData() {
     return calcSectionSize(llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA);
@@ -991,6 +891,165 @@ private:
   std::map<const Atom *, uint64_t> atomRva;
 };
 
+// Create all chunks that consist of the output file.
+void ExecutableWriter::build(const File &linkedFile) {
+  // Create file chunks and add them to the list.
+  auto *dosStub = new DOSStubChunk(_PECOFFLinkingContext);
+  auto *peHeader = new PEHeaderChunk(_PECOFFLinkingContext);
+  auto *dataDirectory = new DataDirectoryChunk(linkedFile);
+  auto *sectionTable = new SectionHeaderTableChunk();
+  auto *text = new TextSectionChunk(linkedFile);
+  auto *rdata = new RDataSectionChunk(linkedFile);
+  auto *data = new DataSectionChunk(linkedFile);
+  auto *bss = new BssSectionChunk(linkedFile);
+  BaseRelocChunk *baseReloc = nullptr;
+  if (_PECOFFLinkingContext.getBaseRelocationEnabled())
+    baseReloc = new BaseRelocChunk(linkedFile);
+
+  addChunk(dosStub);
+  addChunk(peHeader);
+  addChunk(dataDirectory);
+  addChunk(sectionTable);
+
+  // Do not add the empty section. Windows loader does not like a section of
+  // size zero and rejects such executable.
+  if (text->size())
+    addSectionChunk(text, sectionTable);
+  if (rdata->size())
+    addSectionChunk(rdata, sectionTable);
+  if (data->size())
+    addSectionChunk(data, sectionTable);
+  if (bss->size())
+    addSectionChunk(bss, sectionTable);
+
+  // Now that 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 (baseReloc) {
+    baseReloc->setContents(_chunks);
+    if (baseReloc->size()) {
+      addSectionChunk(baseReloc, sectionTable);
+      dataDirectory->setBaseRelocField(baseReloc->getSectionRva(),
+                                       baseReloc->rawSize());
+    }
+  }
+
+  setImageSizeOnDisk();
+
+  // Now that we know the size and file offset of sections. Set the file
+  // header accordingly.
+  peHeader->setSizeOfCode(calcSizeOfCode());
+  if (text->size()) {
+    peHeader->setBaseOfCode(text->getVirtualAddress());
+  }
+  if (rdata->size()) {
+    peHeader->setBaseOfData(rdata->getVirtualAddress());
+  } else if (data->size()) {
+    peHeader->setBaseOfData(data->getVirtualAddress());
+  }
+  peHeader->setSizeOfInitializedData(calcSizeOfInitializedData());
+  peHeader->setSizeOfUninitializedData(calcSizeOfUninitializedData());
+  peHeader->setNumberOfSections(_numSections);
+  peHeader->setSizeOfImage(_imageSizeInMemory);
+
+  // The combined size of the DOS, PE and section headers including garbage
+  // between the end of the header and the beginning of the first section.
+  peHeader->setSizeOfHeaders(dosStub->size() + peHeader->size() +
+                             sectionTable->size() + dataDirectory->size());
+
+  setAddressOfEntryPoint(text, peHeader);
+}
+
+error_code ExecutableWriter::writeFile(const File &linkedFile, StringRef path) {
+  this->build(linkedFile);
+
+  uint64_t totalSize = _chunks.back()->fileOffset() + _chunks.back()->size();
+  OwningPtr<llvm::FileOutputBuffer> buffer;
+  error_code ec = llvm::FileOutputBuffer::create(
+      path, totalSize, buffer, llvm::FileOutputBuffer::F_executable);
+  if (ec)
+    return ec;
+
+  for (const auto &chunk : _chunks)
+    chunk->write(buffer->getBufferStart());
+  applyAllRelocations(buffer->getBufferStart());
+  DEBUG(printAllAtomAddresses());
+  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 ExecutableWriter::applyAllRelocations(uint8_t *bufferStart) {
+  for (auto &cp : _chunks)
+    if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp))
+      chunk->applyRelocations(bufferStart, atomRva,
+                              _PECOFFLinkingContext.getBaseAddress());
+}
+
+/// Print atom VAs. Used only for debugging.
+void ExecutableWriter::printAllAtomAddresses() {
+  for (auto &cp : _chunks)
+    if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp))
+      chunk->printAtomAddresses(_PECOFFLinkingContext.getBaseAddress());
+}
+
+void ExecutableWriter::addChunk(Chunk *chunk) {
+  _chunks.push_back(std::unique_ptr<Chunk>(chunk));
+}
+
+void ExecutableWriter::addSectionChunk(SectionChunk *chunk,
+                                       SectionHeaderTableChunk *table) {
+  _chunks.push_back(std::unique_ptr<Chunk>(chunk));
+  table->addSection(chunk);
+  _numSections++;
+
+  // 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.
+  chunk->setVirtualAddress(_imageSizeInMemory);
+  chunk->buildAtomToVirtualAddr(atomRva);
+  _imageSizeInMemory =
+      llvm::RoundUpToAlignment(_imageSizeInMemory + chunk->size(), PAGE_SIZE);
+}
+
+void ExecutableWriter::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->size();
+  }
+}
+
+void ExecutableWriter::setAddressOfEntryPoint(TextSectionChunk *text,
+                                              PEHeaderChunk *peHeader) {
+  // 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 (_PECOFFLinkingContext.entrySymbolName().empty() &&
+      _PECOFFLinkingContext.getImageType() == PECOFFLinkingContext::IMAGE_DLL) {
+    peHeader->setAddressOfEntryPoint(0);
+  } else {
+    uint64_t entryPointAddress =
+        text->getAtomVirtualAddress(_PECOFFLinkingContext.entrySymbolName());
+    if (entryPointAddress != 0)
+      peHeader->setAddressOfEntryPoint(entryPointAddress);
+  }
+}
+
+uint64_t ExecutableWriter::calcSectionSize(
+    llvm::COFF::SectionCharacteristics sectionType) {
+  uint64_t ret = 0;
+  for (auto &cp : _chunks)
+    if (SectionChunk *chunk = dyn_cast<SectionChunk>(&*cp))
+      if (chunk->getSectionCharacteristics() & sectionType)
+        ret += chunk->size();
+  return ret;
+}
+
 } // end namespace pecoff
 
 std::unique_ptr<Writer> createWriterPECOFF(const PECOFFLinkingContext &info) {





More information about the llvm-commits mailing list