diff options
Diffstat (limited to 'lib/ReaderWriter/PECOFF/WriterPECOFF.cpp')
-rw-r--r-- | lib/ReaderWriter/PECOFF/WriterPECOFF.cpp | 1417 |
1 files changed, 1417 insertions, 0 deletions
diff --git a/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp b/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp new file mode 100644 index 000000000000..d34e2d3d63fd --- /dev/null +++ b/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp @@ -0,0 +1,1417 @@ +//===- 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(), _context(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(_context.getDosStub().size(), (size_t)64); + _size = llvm::RoundUpToAlignment(size, 8); + } + + void write(uint8_t *buffer) override { + ArrayRef<uint8_t> array = _context.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 &_context; +}; + +/// 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> §ionRva, + uint64_t imageBaseAddress); + void applyRelocationsX86(uint8_t *buffer, + std::map<const Atom *, uint64_t> &atomRva, + std::vector<uint64_t> §ionRva, + uint64_t imageBaseAddress); + void applyRelocationsX64(uint8_t *buffer, + std::map<const Atom *, uint64_t> &atomRva, + std::vector<uint64_t> §ionRva, + 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> §ionRva) { + 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> §ionRva) { + // 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) & (-1 << 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> §ionRva, + 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> §ionRva, + 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(); + int align = _ctx.getPageSize(); + for (auto atomLayout : _atomLayouts) { + auto *atom = cast<const DefinedAtom>(atomLayout->_atom); + align = std::max(align, 1 << atom->alignment().powerOf2); + } + 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, 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(); +} + +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 |