summaryrefslogtreecommitdiffstats
path: root/lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp
diff options
context:
space:
mode:
authorRui Ueyama <ruiu@google.com>2015-08-06 16:19:35 +0000
committerRui Ueyama <ruiu@google.com>2015-08-06 16:19:35 +0000
commit251b0e268bef44159ad6916b972ba9c74ce51384 (patch)
tree841faf6ee22c2de7a7faed3f9dd81cd33f691d62 /lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp
parentf77e909f0a19ffd3223e1afef14c4afa5959197a (diff)
downloadbcm5719-llvm-251b0e268bef44159ad6916b972ba9c74ce51384.tar.gz
bcm5719-llvm-251b0e268bef44159ad6916b972ba9c74ce51384.zip
COFF: Remove the old COFF linker and make link an alias to link2.
It's time to remove old COFF linker because the new one is now complete. llvm-svn: 244226
Diffstat (limited to 'lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp')
-rw-r--r--lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp1417
1 files changed, 0 insertions, 1417 deletions
diff --git a/lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp b/lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp
deleted file mode 100644
index 4c6309addef..00000000000
--- a/lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp
+++ /dev/null
@@ -1,1417 +0,0 @@
-//===- lib/ReaderWriter/PECOFF/WriterPECOFF.cpp ---------------------------===//
-//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-///
-/// PE/COFF file consists of DOS Header, PE Header, COFF Header and Section
-/// Tables followed by raw section data.
-///
-/// This writer is responsible for writing Core Linker results to an Windows
-/// executable file.
-///
-/// This writer currently supports 32 bit PE/COFF for x86 processor only.
-///
-//===----------------------------------------------------------------------===//
-
-#include "Atoms.h"
-#include "WriterImportLibrary.h"
-#include "lld/Core/DefinedAtom.h"
-#include "lld/Core/File.h"
-#include "lld/Core/Writer.h"
-#include "lld/ReaderWriter/AtomLayout.h"
-#include "lld/ReaderWriter/PECOFFLinkingContext.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/Object/COFF.h"
-#include "llvm/Support/COFF.h"
-#include "llvm/Support/Debug.h"
-#include "llvm/Support/Endian.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/ErrorOr.h"
-#include "llvm/Support/FileOutputBuffer.h"
-#include "llvm/Support/Format.h"
-#include <algorithm>
-#include <cstdlib>
-#include <map>
-#include <time.h>
-#include <vector>
-
-#define DEBUG_TYPE "WriterPECOFF"
-
-using namespace llvm::support::endian;
-
-using llvm::COFF::DataDirectoryIndex;
-using llvm::object::coff_runtime_function_x64;
-using llvm::support::ulittle16_t;
-using llvm::support::ulittle32_t;
-using llvm::support::ulittle64_t;
-
-namespace lld {
-namespace pecoff {
-
-// Disk sector size. Some data needs to be aligned at disk sector boundary in
-// file.
-static const int SECTOR_SIZE = 512;
-
-namespace {
-class SectionChunk;
-
-/// A Chunk is an abstract contiguous range in an output file.
-class Chunk {
-public:
- enum Kind {
- kindHeader,
- kindSection,
- kindStringTable,
- kindAtomChunk
- };
-
- explicit Chunk(Kind kind) : _kind(kind), _size(0) {}
- virtual ~Chunk() {}
- virtual void write(uint8_t *buffer) = 0;
- virtual uint64_t size() const { return _size; }
- virtual uint64_t onDiskSize() const { return size(); }
- virtual uint64_t align() const { return 1; }
-
- uint64_t fileOffset() const { return _fileOffset; }
- void setFileOffset(uint64_t fileOffset) { _fileOffset = fileOffset; }
- Kind getKind() const { return _kind; }
-
-protected:
- Kind _kind;
- uint64_t _size;
- uint64_t _fileOffset;
-};
-
-/// A HeaderChunk is an abstract class to represent a file header for
-/// PE/COFF. The data in the header chunk is metadata about program and will
-/// be consumed by the windows loader. HeaderChunks are not mapped to memory
-/// when executed.
-class HeaderChunk : public Chunk {
-public:
- HeaderChunk() : Chunk(kindHeader) {}
-
- static bool classof(const Chunk *c) { return c->getKind() == kindHeader; }
-};
-
-/// A DOSStubChunk represents the DOS compatible header at the beginning
-/// of PE/COFF files.
-class DOSStubChunk : public HeaderChunk {
-public:
- explicit DOSStubChunk(const PECOFFLinkingContext &ctx)
- : HeaderChunk(), _ctx(ctx) {
- // Minimum size of DOS stub is 64 bytes. The next block (PE header) needs to
- // be aligned on 8 byte boundary.
- size_t size = std::max(_ctx.getDosStub().size(), (size_t)64);
- _size = llvm::RoundUpToAlignment(size, 8);
- }
-
- void write(uint8_t *buffer) override {
- ArrayRef<uint8_t> array = _ctx.getDosStub();
- std::memcpy(buffer, array.data(), array.size());
- auto *header = reinterpret_cast<llvm::object::dos_header *>(buffer);
- header->AddressOfRelocationTable = sizeof(llvm::object::dos_header);
- header->AddressOfNewExeHeader = _size;
- }
-
-private:
- const PECOFFLinkingContext &_ctx;
-};
-
-/// A PEHeaderChunk represents PE header including COFF header.
-template <class PEHeader>
-class PEHeaderChunk : public HeaderChunk {
-public:
- explicit PEHeaderChunk(const PECOFFLinkingContext &ctx);
-
- void write(uint8_t *buffer) override;
-
- void setSizeOfHeaders(uint64_t size) {
- // Must be multiple of FileAlignment.
- _peHeader.SizeOfHeaders = llvm::RoundUpToAlignment(size, SECTOR_SIZE);
- }
-
- void setSizeOfCode(uint64_t size) { _peHeader.SizeOfCode = size; }
- void setBaseOfCode(uint32_t rva) { _peHeader.BaseOfCode = rva; }
- void setBaseOfData(uint32_t rva);
- void setSizeOfImage(uint32_t size) { _peHeader.SizeOfImage = size; }
-
- void setSizeOfInitializedData(uint64_t size) {
- _peHeader.SizeOfInitializedData = size;
- }
-
- void setSizeOfUninitializedData(uint64_t size) {
- _peHeader.SizeOfUninitializedData = size;
- }
-
- void setNumberOfSections(uint32_t num) { _coffHeader.NumberOfSections = num; }
- void setNumberOfSymbols(uint32_t num) { _coffHeader.NumberOfSymbols = num; }
-
- void setAddressOfEntryPoint(uint32_t address) {
- _peHeader.AddressOfEntryPoint = address;
- }
-
- void setPointerToSymbolTable(uint32_t rva) {
- _coffHeader.PointerToSymbolTable = rva;
- }
-
-private:
- llvm::object::coff_file_header _coffHeader;
- PEHeader _peHeader;
-};
-
-/// A SectionHeaderTableChunk represents Section Table Header of PE/COFF
-/// format, which is a list of section headers.
-class SectionHeaderTableChunk : public HeaderChunk {
-public:
- SectionHeaderTableChunk() : HeaderChunk() {}
- void addSection(SectionChunk *chunk);
- uint64_t size() const override;
- void write(uint8_t *buffer) override;
-
-private:
- static llvm::object::coff_section createSectionHeader(SectionChunk *chunk);
-
- std::vector<SectionChunk *> _sections;
-};
-
-class StringTableChunk : public Chunk {
-public:
- StringTableChunk() : Chunk(kindStringTable) {}
-
- static bool classof(const Chunk *c) {
- return c->getKind() == kindStringTable;
- }
-
- uint32_t addSectionName(StringRef sectionName) {
- if (_stringTable.empty()) {
- // The string table immediately follows the symbol table.
- // We don't really need a symbol table, but some tools (e.g. dumpbin)
- // don't like zero-length symbol table.
- // Make room for the empty symbol slot, which occupies 18 byte.
- // We also need to reserve 4 bytes for the string table header.
- int size = sizeof(llvm::object::coff_symbol16) + 4;
- _stringTable.insert(_stringTable.begin(), size, 0);
- // Set the name of the dummy symbol to the first string table entry.
- // It's better than letting dumpbin print out a garabage as a symbol name.
- char *off = _stringTable.data() + 4;
- write32le(off, 4);
- }
- uint32_t offset = _stringTable.size();
- _stringTable.insert(_stringTable.end(), sectionName.begin(),
- sectionName.end());
- _stringTable.push_back('\0');
- return offset - sizeof(llvm::object::coff_symbol16);
- }
-
- uint64_t size() const override { return _stringTable.size(); }
-
- void write(uint8_t *buffer) override {
- if (_stringTable.empty())
- return;
- char *off = _stringTable.data() + sizeof(llvm::object::coff_symbol16);
- write32le(off, _stringTable.size());
- std::memcpy(buffer, _stringTable.data(), _stringTable.size());
- }
-
-private:
- std::vector<char> _stringTable;
-};
-
-class SectionChunk : public Chunk {
-public:
- uint64_t onDiskSize() const override {
- if (_characteristics & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)
- return 0;
- return llvm::RoundUpToAlignment(size(), SECTOR_SIZE);
- }
-
- uint64_t align() const override { return SECTOR_SIZE; }
- uint32_t getCharacteristics() const { return _characteristics; }
- StringRef getSectionName() const { return _sectionName; }
- virtual uint64_t memAlign() const { return _memAlign; }
-
- static bool classof(const Chunk *c) {
- Kind kind = c->getKind();
- return kind == kindSection || kind == kindAtomChunk;
- }
-
- uint64_t getVirtualAddress() { return _virtualAddress; }
- virtual void setVirtualAddress(uint32_t rva) { _virtualAddress = rva; }
-
- uint32_t getStringTableOffset() const { return _stringTableOffset; }
- void setStringTableOffset(uint32_t offset) { _stringTableOffset = offset; }
-
-protected:
- SectionChunk(Kind kind, StringRef sectionName, uint32_t characteristics,
- const PECOFFLinkingContext &ctx)
- : Chunk(kind), _sectionName(sectionName),
- _characteristics(characteristics), _virtualAddress(0),
- _stringTableOffset(0), _memAlign(ctx.getPageSize()) {}
-
-private:
- StringRef _sectionName;
- const uint32_t _characteristics;
- uint64_t _virtualAddress;
- uint32_t _stringTableOffset;
- uint64_t _memAlign;
-};
-
-struct BaseReloc {
- BaseReloc(uint64_t a, llvm::COFF::BaseRelocationType t) : addr(a), type(t) {}
- uint64_t addr;
- llvm::COFF::BaseRelocationType type;
-};
-
-/// An AtomChunk represents a section containing atoms.
-class AtomChunk : public SectionChunk {
-public:
- AtomChunk(const PECOFFLinkingContext &ctx, StringRef name,
- const std::vector<const DefinedAtom *> &atoms);
-
- void write(uint8_t *buffer) override;
-
- uint64_t memAlign() const override;
- void appendAtom(const DefinedAtom *atom);
- void buildAtomRvaMap(std::map<const Atom *, uint64_t> &atomRva) const;
-
- void applyRelocationsARM(uint8_t *buffer,
- std::map<const Atom *, uint64_t> &atomRva,
- std::vector<uint64_t> &sectionRva,
- uint64_t imageBaseAddress);
- void applyRelocationsX86(uint8_t *buffer,
- std::map<const Atom *, uint64_t> &atomRva,
- std::vector<uint64_t> &sectionRva,
- uint64_t imageBaseAddress);
- void applyRelocationsX64(uint8_t *buffer,
- std::map<const Atom *, uint64_t> &atomRva,
- std::vector<uint64_t> &sectionRva,
- uint64_t imageBaseAddress);
-
- void printAtomAddresses(uint64_t baseAddr) const;
- void addBaseRelocations(std::vector<BaseReloc> &relocSites) const;
-
- void setVirtualAddress(uint32_t rva) override;
- uint64_t getAtomVirtualAddress(StringRef name) const;
-
- static bool classof(const Chunk *c) { return c->getKind() == kindAtomChunk; }
-
-protected:
- std::vector<AtomLayout *> _atomLayouts;
- uint64_t _virtualAddress;
-
-private:
- uint32_t
- computeCharacteristics(const PECOFFLinkingContext &ctx, StringRef name,
- const std::vector<const DefinedAtom *> &atoms) const {
- return ctx.getSectionAttributes(name,
- getDefaultCharacteristics(name, atoms));
- }
-
- uint32_t getDefaultCharacteristics(
- StringRef name, const std::vector<const DefinedAtom *> &atoms) const;
-
- mutable llvm::BumpPtrAllocator _alloc;
- llvm::COFF::MachineTypes _machineType;
- const PECOFFLinkingContext &_ctx;
-};
-
-/// A DataDirectoryChunk represents data directory entries that follows the PE
-/// header in the output file. An entry consists of an 8 byte field that
-/// indicates a relative virtual address (the starting address of the entry data
-/// in memory) and 8 byte entry data size.
-class DataDirectoryChunk : public HeaderChunk {
-public:
- DataDirectoryChunk()
- : HeaderChunk(), _data(std::vector<llvm::object::data_directory>(16)) {}
-
- uint64_t size() const override {
- return sizeof(llvm::object::data_directory) * _data.size();
- }
-
- void setField(DataDirectoryIndex index, uint32_t addr, uint32_t size);
- void write(uint8_t *buffer) override;
-
-private:
- std::vector<llvm::object::data_directory> _data;
-};
-
-/// A BaseRelocChunk represents ".reloc" section.
-///
-/// .reloc section contains a list of addresses. If the PE/COFF loader decides
-/// to load the binary at a memory address different from its preferred base
-/// address, which is specified by ImageBase field in the COFF header, the
-/// loader needs to relocate the binary, so that all the addresses in the binary
-/// point to new locations. The loader will do that by fixing up the addresses
-/// specified by .reloc section.
-///
-/// The executable is almost always loaded at the preferred base address because
-/// it's loaded into an empty address space. The DLL is however an subject of
-/// load-time relocation because it may conflict with other DLLs or the
-/// executable.
-class BaseRelocChunk : public SectionChunk {
- typedef std::vector<std::unique_ptr<Chunk> > ChunkVectorT;
-
-public:
- BaseRelocChunk(ChunkVectorT &chunks, const PECOFFLinkingContext &ctx)
- : SectionChunk(kindSection, ".reloc", characteristics, ctx),
- _ctx(ctx), _contents(createContents(chunks)) {}
-
- void write(uint8_t *buffer) override {
- std::memcpy(buffer, &_contents[0], _contents.size());
- }
-
- uint64_t size() const override { return _contents.size(); }
-
-private:
- // When loaded into memory, reloc section should be readable and writable.
- static const uint32_t characteristics =
- llvm::COFF::IMAGE_SCN_MEM_READ |
- llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
- llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE;
-
- std::vector<uint8_t> createContents(ChunkVectorT &chunks) const;
-
- // Returns a list of RVAs that needs to be relocated if the binary is loaded
- // at an address different from its preferred one.
- std::vector<BaseReloc> listRelocSites(ChunkVectorT &chunks) const;
-
- // Create the content of a relocation block.
- std::vector<uint8_t>
- createBaseRelocBlock(uint64_t pageAddr, const BaseReloc *begin,
- const BaseReloc *end) const;
-
- const PECOFFLinkingContext &_ctx;
- std::vector<uint8_t> _contents;
-};
-
-template <class PEHeader>
-PEHeaderChunk<PEHeader>::PEHeaderChunk(const PECOFFLinkingContext &ctx)
- : HeaderChunk() {
- // Set the size of the chunk and initialize the header with null bytes.
- _size = sizeof(llvm::COFF::PEMagic) + sizeof(_coffHeader) + sizeof(_peHeader);
- std::memset(&_coffHeader, 0, sizeof(_coffHeader));
- std::memset(&_peHeader, 0, sizeof(_peHeader));
-
- _coffHeader.Machine = ctx.getMachineType();
- _coffHeader.TimeDateStamp = time(nullptr);
-
- // Attributes of the executable.
- uint16_t characteristics = llvm::COFF::IMAGE_FILE_EXECUTABLE_IMAGE;
- if (!ctx.is64Bit())
- characteristics |= llvm::COFF::IMAGE_FILE_32BIT_MACHINE;
- if (ctx.isDll())
- characteristics |= llvm::COFF::IMAGE_FILE_DLL;
- if (ctx.getLargeAddressAware() || ctx.is64Bit())
- characteristics |= llvm::COFF::IMAGE_FILE_LARGE_ADDRESS_AWARE;
- if (ctx.getSwapRunFromCD())
- characteristics |= llvm::COFF::IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP;
- if (ctx.getSwapRunFromNet())
- characteristics |= llvm::COFF::IMAGE_FILE_NET_RUN_FROM_SWAP;
- if (!ctx.getBaseRelocationEnabled())
- characteristics |= llvm::COFF::IMAGE_FILE_RELOCS_STRIPPED;
-
- _coffHeader.Characteristics = characteristics;
-
- _peHeader.Magic = ctx.is64Bit() ? llvm::COFF::PE32Header::PE32_PLUS
- : llvm::COFF::PE32Header::PE32;
-
- // The address of the executable when loaded into memory. The default for
- // DLLs is 0x10000000. The default for executables is 0x400000.
- _peHeader.ImageBase = ctx.getBaseAddress();
-
- // Sections should be page-aligned when loaded into memory, which is 4KB on
- // x86.
- _peHeader.SectionAlignment = ctx.getSectionDefaultAlignment();
-
- // Sections in an executable file on disk should be sector-aligned (512 byte).
- _peHeader.FileAlignment = SECTOR_SIZE;
-
- // The version number of the resultant executable/DLL. The number is purely
- // informative, and neither the linker nor the loader won't use it. User can
- // set the value using /version command line option. Default is 0.0.
- PECOFFLinkingContext::Version imageVersion = ctx.getImageVersion();
- _peHeader.MajorImageVersion = imageVersion.majorVersion;
- _peHeader.MinorImageVersion = imageVersion.minorVersion;
-
- // The required Windows version number. This is the internal version and
- // shouldn't be confused with product name. Windows 7 is version 6.1 and
- // Windows 8 is 6.2, for example.
- PECOFFLinkingContext::Version minOSVersion = ctx.getMinOSVersion();
- _peHeader.MajorOperatingSystemVersion = minOSVersion.majorVersion;
- _peHeader.MinorOperatingSystemVersion = minOSVersion.minorVersion;
- _peHeader.MajorSubsystemVersion = minOSVersion.majorVersion;
- _peHeader.MinorSubsystemVersion = minOSVersion.minorVersion;
-
- _peHeader.Subsystem = ctx.getSubsystem();
-
- // Despite its name, DLL characteristics field has meaning both for
- // executables and DLLs. We are not very sure if the following bits must
- // be set, but regular binaries seem to have these bits, so we follow
- // them.
- uint16_t dllCharacteristics = 0;
- if (ctx.noSEH())
- dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_SEH;
- if (ctx.isTerminalServerAware())
- dllCharacteristics |=
- llvm::COFF::IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
- if (ctx.isNxCompat())
- dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
- if (ctx.getDynamicBaseEnabled())
- dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
- if (!ctx.getAllowBind())
- dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_BIND;
- if (!ctx.getAllowIsolation())
- dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;
- if (ctx.getHighEntropyVA() && ctx.is64Bit())
- dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA;
- _peHeader.DLLCharacteristics = dllCharacteristics;
-
- _peHeader.SizeOfStackReserve = ctx.getStackReserve();
- _peHeader.SizeOfStackCommit = ctx.getStackCommit();
- _peHeader.SizeOfHeapReserve = ctx.getHeapReserve();
- _peHeader.SizeOfHeapCommit = ctx.getHeapCommit();
-
- // The number of data directory entries. We always have 16 entries.
- _peHeader.NumberOfRvaAndSize = 16;
-
- // The size of PE header including optional data directory.
- _coffHeader.SizeOfOptionalHeader = sizeof(PEHeader) +
- _peHeader.NumberOfRvaAndSize * sizeof(llvm::object::data_directory);
-}
-
-template <>
-void PEHeaderChunk<llvm::object::pe32_header>::setBaseOfData(uint32_t rva) {
- _peHeader.BaseOfData = rva;
-}
-
-template <>
-void PEHeaderChunk<llvm::object::pe32plus_header>::setBaseOfData(uint32_t rva) {
- // BaseOfData field does not exist in PE32+ header.
-}
-
-template <class PEHeader>
-void PEHeaderChunk<PEHeader>::write(uint8_t *buffer) {
- std::memcpy(buffer, llvm::COFF::PEMagic, sizeof(llvm::COFF::PEMagic));
- buffer += sizeof(llvm::COFF::PEMagic);
- std::memcpy(buffer, &_coffHeader, sizeof(_coffHeader));
- buffer += sizeof(_coffHeader);
- std::memcpy(buffer, &_peHeader, sizeof(_peHeader));
-}
-
-AtomChunk::AtomChunk(const PECOFFLinkingContext &ctx, StringRef sectionName,
- const std::vector<const DefinedAtom *> &atoms)
- : SectionChunk(kindAtomChunk, sectionName,
- computeCharacteristics(ctx, sectionName, atoms), ctx),
- _virtualAddress(0), _machineType(ctx.getMachineType()), _ctx(ctx) {
- for (auto *a : atoms)
- appendAtom(a);
-}
-
-void AtomChunk::write(uint8_t *buffer) {
- if (_atomLayouts.empty())
- return;
- if (getCharacteristics() & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)
- return;
- if (getCharacteristics() & llvm::COFF::IMAGE_SCN_CNT_CODE) {
- // Fill the section with INT 3 (0xCC) rather than NUL, so that the
- // disassembler will not interpret a garbage between atoms as the beginning
- // of multi-byte machine code. This does not change the behavior of
- // resulting binary but help debugging.
- uint8_t *start = buffer + _atomLayouts.front()->_fileOffset;
- uint8_t *end = buffer + _atomLayouts.back()->_fileOffset;
- memset(start, 0xCC, end - start);
- }
-
- for (const auto *layout : _atomLayouts) {
- const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
- ArrayRef<uint8_t> rawContent = atom->rawContent();
- std::memcpy(buffer + layout->_fileOffset, rawContent.data(),
- rawContent.size());
- }
-}
-
-// Add all atoms to the given map. This data will be used to do relocation.
-void
-AtomChunk::buildAtomRvaMap(std::map<const Atom *, uint64_t> &atomRva) const {
- for (const auto *layout : _atomLayouts)
- atomRva[layout->_atom] = layout->_virtualAddr;
-}
-
-static int getSectionIndex(uint64_t targetAddr,
- const std::vector<uint64_t> &sectionRva) {
- int i = 1;
- for (uint64_t rva : sectionRva) {
- if (targetAddr < rva)
- return i;
- ++i;
- }
- return i;
-}
-
-static uint32_t getSectionStartAddr(uint64_t targetAddr,
- const std::vector<uint64_t> &sectionRva) {
- // Scan the list of section start addresses to find the section start address
- // for the given RVA.
- for (int i = 0, e = sectionRva.size(); i < e; ++i)
- if (i == e - 1 || (sectionRva[i] <= targetAddr && targetAddr < sectionRva[i + 1]))
- return sectionRva[i];
- llvm_unreachable("Section missing");
-}
-
-static void applyThumbMoveImmediate(ulittle16_t *mov, uint16_t imm) {
- // MOVW(T3): |11110|i|10|0|1|0|0|imm4|0|imm3|Rd|imm8|
- // imm32 = zext imm4:i:imm3:imm8
- // MOVT(T1): |11110|i|10|1|1|0|0|imm4|0|imm3|Rd|imm8|
- // imm16 = imm4:i:imm3:imm8
- mov[0] =
- mov[0] | (((imm & 0x0800) >> 11) << 10) | (((imm & 0xf000) >> 12) << 0);
- mov[1] =
- mov[1] | (((imm & 0x0700) >> 8) << 12) | (((imm & 0x00ff) >> 0) << 0);
-}
-
-static void applyThumbBranchImmediate(ulittle16_t *bl, int32_t imm) {
- // BL(T1): |11110|S|imm10|11|J1|1|J2|imm11|
- // imm32 = sext S:I1:I2:imm10:imm11:'0'
- // B.W(T4): |11110|S|imm10|10|J1|1|J2|imm11|
- // imm32 = sext S:I1:I2:imm10:imm11:'0'
- //
- // I1 = ~(J1 ^ S), I2 = ~(J2 ^ S)
-
- assert((~abs(imm) & (~0ULL << 24)) && "bl/b.w out of range");
-
- uint32_t S = (imm < 0 ? 1 : 0);
- uint32_t J1 = ((~imm & 0x00800000) >> 23) ^ S;
- uint32_t J2 = ((~imm & 0x00400000) >> 22) ^ S;
-
- bl[0] = bl[0] | (((imm & 0x003ff000) >> 12) << 0) | (S << 10);
- bl[1] = bl[1] | (((imm & 0x00000ffe) >> 1) << 0) | (J2 << 11) | (J1 << 13);
-}
-
-void AtomChunk::applyRelocationsARM(uint8_t *Buffer,
- std::map<const Atom *, uint64_t> &AtomRVA,
- std::vector<uint64_t> &SectionRVA,
- uint64_t ImageBase) {
- Buffer = Buffer + _fileOffset;
- parallel_for_each(_atomLayouts.begin(), _atomLayouts.end(),
- [&](const AtomLayout *layout) {
- const DefinedAtom *Atom = cast<DefinedAtom>(layout->_atom);
- for (const Reference *R : *Atom) {
- if (R->kindNamespace() != Reference::KindNamespace::COFF)
- continue;
-
- bool AssumeTHUMBCode = false;
- if (auto Target = dyn_cast<DefinedAtom>(R->target()))
- AssumeTHUMBCode = Target->permissions() == DefinedAtom::permR_X ||
- Target->permissions() == DefinedAtom::permRWX;
-
- const auto AtomOffset = R->offsetInAtom();
- const auto FileOffset = layout->_fileOffset;
- const auto TargetAddr = AtomRVA[R->target()] | (AssumeTHUMBCode ? 1 : 0);
- auto RelocSite16 =
- reinterpret_cast<ulittle16_t *>(Buffer + FileOffset + AtomOffset);
- auto RelocSite32 =
- reinterpret_cast<ulittle32_t *>(Buffer + FileOffset + AtomOffset);
-
- switch (R->kindValue()) {
- default: llvm_unreachable("unsupported relocation type");
- case llvm::COFF::IMAGE_REL_ARM_ADDR32:
- *RelocSite32 = *RelocSite32 + TargetAddr + ImageBase;
- break;
- case llvm::COFF::IMAGE_REL_ARM_ADDR32NB:
- *RelocSite32 = *RelocSite32 + TargetAddr;
- break;
- case llvm::COFF::IMAGE_REL_ARM_MOV32T:
- applyThumbMoveImmediate(&RelocSite16[0], (TargetAddr + ImageBase) >> 0);
- applyThumbMoveImmediate(&RelocSite16[2], (TargetAddr + ImageBase) >> 16);
- break;
- case llvm::COFF::IMAGE_REL_ARM_BRANCH24T:
- // NOTE: the thumb bit will implicitly be truncated properly
- applyThumbBranchImmediate(RelocSite16,
- TargetAddr - AtomRVA[Atom] - AtomOffset - 4);
- break;
- case llvm::COFF::IMAGE_REL_ARM_BLX23T:
- // NOTE: the thumb bit will implicitly be truncated properly
- applyThumbBranchImmediate(RelocSite16,
- TargetAddr - AtomRVA[Atom] - AtomOffset - 4);
- break;
- }
- }
- });
-}
-
-void AtomChunk::applyRelocationsX86(uint8_t *buffer,
- std::map<const Atom *, uint64_t> &atomRva,
- std::vector<uint64_t> &sectionRva,
- uint64_t imageBaseAddress) {
- buffer += _fileOffset;
- parallel_for_each(_atomLayouts.begin(), _atomLayouts.end(),
- [&](const AtomLayout *layout) {
- const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
- for (const Reference *ref : *atom) {
- // Skip if this reference is not for COFF relocation.
- if (ref->kindNamespace() != Reference::KindNamespace::COFF)
- continue;
- auto relocSite32 = reinterpret_cast<ulittle32_t *>(
- buffer + layout->_fileOffset + ref->offsetInAtom());
- auto relocSite16 = reinterpret_cast<ulittle16_t *>(relocSite32);
- const Atom *target = ref->target();
- uint64_t targetAddr = atomRva[target];
- // Also account for whatever offset is already stored at the relocation
- // site.
- switch (ref->kindValue()) {
- case llvm::COFF::IMAGE_REL_I386_ABSOLUTE:
- // This relocation is no-op.
- break;
- case llvm::COFF::IMAGE_REL_I386_DIR32:
- // Set target's 32-bit VA.
- if (auto *abs = dyn_cast<AbsoluteAtom>(target))
- *relocSite32 += abs->value();
- else
- *relocSite32 += targetAddr + imageBaseAddress;
- break;
- case llvm::COFF::IMAGE_REL_I386_DIR32NB:
- // Set target's 32-bit RVA.
- *relocSite32 += targetAddr;
- break;
- case llvm::COFF::IMAGE_REL_I386_REL32: {
- // Set 32-bit relative address of the target. This relocation is
- // usually used for relative branch or call instruction.
- uint32_t disp = atomRva[atom] + ref->offsetInAtom() + 4;
- *relocSite32 += targetAddr - disp;
- break;
- }
- case llvm::COFF::IMAGE_REL_I386_SECTION:
- // The 16-bit section index that contains the target symbol.
- *relocSite16 += getSectionIndex(targetAddr, sectionRva);
- break;
- case llvm::COFF::IMAGE_REL_I386_SECREL:
- // The 32-bit relative address from the beginning of the section that
- // contains the target symbol.
- *relocSite32 +=
- targetAddr - getSectionStartAddr(targetAddr, sectionRva);
- break;
- default:
- llvm::report_fatal_error("Unsupported relocation kind");
- }
- }
- });
-}
-
-void AtomChunk::applyRelocationsX64(uint8_t *buffer,
- std::map<const Atom *, uint64_t> &atomRva,
- std::vector<uint64_t> &sectionRva,
- uint64_t imageBase) {
- buffer += _fileOffset;
- parallel_for_each(_atomLayouts.begin(), _atomLayouts.end(),
- [&](const AtomLayout *layout) {
- const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
- for (const Reference *ref : *atom) {
- if (ref->kindNamespace() != Reference::KindNamespace::COFF)
- continue;
-
- uint8_t *loc = buffer + layout->_fileOffset + ref->offsetInAtom();
- auto relocSite16 = reinterpret_cast<ulittle16_t *>(loc);
- auto relocSite32 = reinterpret_cast<ulittle32_t *>(loc);
- auto relocSite64 = reinterpret_cast<ulittle64_t *>(loc);
- uint64_t targetAddr = atomRva[ref->target()];
-
- switch (ref->kindValue()) {
- case llvm::COFF::IMAGE_REL_AMD64_ADDR64:
- *relocSite64 += targetAddr + imageBase;
- break;
- case llvm::COFF::IMAGE_REL_AMD64_ADDR32:
- *relocSite32 += targetAddr + imageBase;
- break;
- case llvm::COFF::IMAGE_REL_AMD64_ADDR32NB:
- *relocSite32 += targetAddr;
- break;
- case llvm::COFF::IMAGE_REL_AMD64_REL32:
- *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 4;
- break;
- case llvm::COFF::IMAGE_REL_AMD64_REL32_1:
- *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 5;
- break;
- case llvm::COFF::IMAGE_REL_AMD64_REL32_2:
- *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 6;
- break;
- case llvm::COFF::IMAGE_REL_AMD64_REL32_3:
- *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 7;
- break;
- case llvm::COFF::IMAGE_REL_AMD64_REL32_4:
- *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 8;
- break;
- case llvm::COFF::IMAGE_REL_AMD64_REL32_5:
- *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 9;
- break;
- case llvm::COFF::IMAGE_REL_AMD64_SECTION:
- *relocSite16 += getSectionIndex(targetAddr, sectionRva) - 1;
- break;
- case llvm::COFF::IMAGE_REL_AMD64_SECREL:
- *relocSite32 +=
- targetAddr - getSectionStartAddr(targetAddr, sectionRva);
- break;
- default:
- llvm::errs() << "Kind: " << (int)ref->kindValue() << "\n";
- llvm::report_fatal_error("Unsupported relocation kind");
- }
- }
- });
-}
-
-/// Print atom VAs. Used only for debugging.
-void AtomChunk::printAtomAddresses(uint64_t baseAddr) const {
- for (const auto *layout : _atomLayouts) {
- const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
- uint64_t addr = layout->_virtualAddr;
- llvm::dbgs() << llvm::format("0x%08llx: ", addr + baseAddr)
- << (atom->name().empty() ? "(anonymous)" : atom->name())
- << "\n";
- }
-}
-
-/// List all virtual addresses (and not relative virtual addresses) that need
-/// to be fixed up if image base is relocated. The only relocation type that
-/// needs to be fixed is DIR32 on i386. REL32 is not (and should not be)
-/// fixed up because it's PC-relative.
-void AtomChunk::addBaseRelocations(std::vector<BaseReloc> &relocSites) const {
- for (const auto *layout : _atomLayouts) {
- const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom);
- for (const Reference *ref : *atom) {
- if (ref->kindNamespace() != Reference::KindNamespace::COFF)
- continue;
-
- // An absolute symbol points to a fixed location in memory. Their
- // address should not be fixed at load time. One exception is ImageBase
- // because that's relative to run-time image base address.
- if (auto *abs = dyn_cast<AbsoluteAtom>(ref->target()))
- if (!abs->name().equals("__ImageBase") &&
- !abs->name().equals("___ImageBase"))
- continue;
-
- uint64_t address = layout->_virtualAddr + ref->offsetInAtom();
- switch (_machineType) {
- default: llvm_unreachable("unsupported machine type");
- case llvm::COFF::IMAGE_FILE_MACHINE_I386:
- if (ref->kindValue() == llvm::COFF::IMAGE_REL_I386_DIR32)
- relocSites.push_back(
- BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_HIGHLOW));
- break;
- case llvm::COFF::IMAGE_FILE_MACHINE_AMD64:
- if (ref->kindValue() == llvm::COFF::IMAGE_REL_AMD64_ADDR64)
- relocSites.push_back(
- BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_DIR64));
- break;
- case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT:
- if (ref->kindValue() == llvm::COFF::IMAGE_REL_ARM_ADDR32)
- relocSites.push_back(
- BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_HIGHLOW));
- else if (ref->kindValue() == llvm::COFF::IMAGE_REL_ARM_MOV32T)
- relocSites.push_back(
- BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_ARM_MOV32T));
- break;
- }
- }
- }
-}
-
-void AtomChunk::setVirtualAddress(uint32_t rva) {
- SectionChunk::setVirtualAddress(rva);
- for (AtomLayout *layout : _atomLayouts)
- layout->_virtualAddr += rva;
-}
-
-uint64_t AtomChunk::getAtomVirtualAddress(StringRef name) const {
- for (auto atomLayout : _atomLayouts)
- if (atomLayout->_atom->name() == name)
- return atomLayout->_virtualAddr;
- return 0;
-}
-
-void DataDirectoryChunk::setField(DataDirectoryIndex index, uint32_t addr,
- uint32_t size) {
- llvm::object::data_directory &dir = _data[index];
- dir.RelativeVirtualAddress = addr;
- dir.Size = size;
-}
-
-void DataDirectoryChunk::write(uint8_t *buffer) {
- std::memcpy(buffer, &_data[0], size());
-}
-
-uint64_t AtomChunk::memAlign() const {
- // ReaderCOFF propagated the section alignment to the first atom in
- // the section. We restore that here.
- if (_atomLayouts.empty())
- return _ctx.getPageSize();
- unsigned align = _ctx.getPageSize();
- for (auto atomLayout : _atomLayouts) {
- auto *atom = cast<const DefinedAtom>(atomLayout->_atom);
- align = std::max(align, (unsigned)atom->alignment().value);
- }
- return align;
-}
-
-void AtomChunk::appendAtom(const DefinedAtom *atom) {
- // Atom may have to be at a proper alignment boundary. If so, move the
- // pointer to make a room after the last atom before adding new one.
- _size = llvm::RoundUpToAlignment(_size, atom->alignment().value);
-
- // Create an AtomLayout and move the current pointer.
- auto *layout = new (_alloc) AtomLayout(atom, _size, _size);
- _atomLayouts.push_back(layout);
- _size += atom->size();
-}
-
-uint32_t AtomChunk::getDefaultCharacteristics(
- StringRef name, const std::vector<const DefinedAtom *> &atoms) const {
- const uint32_t code = llvm::COFF::IMAGE_SCN_CNT_CODE;
- const uint32_t execute = llvm::COFF::IMAGE_SCN_MEM_EXECUTE;
- const uint32_t read = llvm::COFF::IMAGE_SCN_MEM_READ;
- const uint32_t write = llvm::COFF::IMAGE_SCN_MEM_WRITE;
- const uint32_t data = llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
- const uint32_t bss = llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA;
- if (name == ".text")
- return code | execute | read;
- if (name == ".data")
- return data | read | write;
- if (name == ".rdata")
- return data | read;
- if (name == ".bss")
- return bss | read | write;
- assert(atoms.size() > 0);
- switch (atoms[0]->permissions()) {
- case DefinedAtom::permR__:
- return data | read;
- case DefinedAtom::permRW_:
- return data | read | write;
- case DefinedAtom::permR_X:
- return code | execute | read;
- case DefinedAtom::permRWX:
- return code | execute | read | write;
- default:
- llvm_unreachable("Unsupported permission");
- }
-}
-
-void SectionHeaderTableChunk::addSection(SectionChunk *chunk) {
- _sections.push_back(chunk);
-}
-
-uint64_t SectionHeaderTableChunk::size() const {
- return _sections.size() * sizeof(llvm::object::coff_section);
-}
-
-void SectionHeaderTableChunk::write(uint8_t *buffer) {
- uint64_t offset = 0;
- for (SectionChunk *chunk : _sections) {
- llvm::object::coff_section header = createSectionHeader(chunk);
- std::memcpy(buffer + offset, &header, sizeof(header));
- offset += sizeof(header);
- }
-}
-
-llvm::object::coff_section
-SectionHeaderTableChunk::createSectionHeader(SectionChunk *chunk) {
- llvm::object::coff_section header;
-
- // We have extended the COFF specification by allowing section names to be
- // greater than eight characters. We achieve this by adding the section names
- // to the string table. Binutils' linker, ld, performs the same trick.
- StringRef sectionName = chunk->getSectionName();
- std::memset(header.Name, 0, llvm::COFF::NameSize);
- if (uint32_t stringTableOffset = chunk->getStringTableOffset())
- sprintf(header.Name, "/%u", stringTableOffset);
- else
- std::strncpy(header.Name, sectionName.data(), sectionName.size());
-
- uint32_t characteristics = chunk->getCharacteristics();
- header.VirtualSize = chunk->size();
- header.VirtualAddress = chunk->getVirtualAddress();
- header.SizeOfRawData = chunk->onDiskSize();
- header.PointerToRelocations = 0;
- header.PointerToLinenumbers = 0;
- header.NumberOfRelocations = 0;
- header.NumberOfLinenumbers = 0;
- header.Characteristics = characteristics;
-
- if (characteristics & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
- header.PointerToRawData = 0;
- } else {
- header.PointerToRawData = chunk->fileOffset();
- }
- return header;
-}
-
-/// Creates .reloc section content from the other sections. The content of
-/// .reloc is basically a list of relocation sites. The relocation sites are
-/// divided into blocks. Each block represents the base relocation for a 4K
-/// page.
-///
-/// By dividing 32 bit RVAs into blocks, COFF saves disk and memory space for
-/// the base relocation. A block consists of a 32 bit page RVA and 16 bit
-/// relocation entries which represent offsets in the page. That is a more
-/// compact representation than a simple vector of 32 bit RVAs.
-std::vector<uint8_t>
-BaseRelocChunk::createContents(ChunkVectorT &chunks) const {
- std::vector<uint8_t> contents;
- std::vector<BaseReloc> relocSites = listRelocSites(chunks);
-
- uint64_t mask = _ctx.getPageSize() - 1;
- parallel_sort(relocSites.begin(), relocSites.end(),
- [=](const BaseReloc &a, const BaseReloc &b) {
- return (a.addr & ~mask) < (b.addr & ~mask);
- });
-
- // Base relocations for the same memory page are grouped together
- // and passed to createBaseRelocBlock.
- for (auto it = relocSites.begin(), e = relocSites.end(); it != e;) {
- auto beginIt = it;
- uint64_t pageAddr = (beginIt->addr & ~mask);
- for (++it; it != e; ++it)
- if ((it->addr & ~mask) != pageAddr)
- break;
- const BaseReloc *begin = &*beginIt;
- const BaseReloc *end = begin + (it - beginIt);
- std::vector<uint8_t> block = createBaseRelocBlock(pageAddr, begin, end);
- contents.insert(contents.end(), block.begin(), block.end());
- }
- return contents;
-}
-
-// Returns a list of RVAs that needs to be relocated if the binary is loaded
-// at an address different from its preferred one.
-std::vector<BaseReloc>
-BaseRelocChunk::listRelocSites(ChunkVectorT &chunks) const {
- std::vector<BaseReloc> ret;
- for (auto &cp : chunks)
- if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp))
- chunk->addBaseRelocations(ret);
- return ret;
-}
-
-// Create the content of a relocation block.
-std::vector<uint8_t>
-BaseRelocChunk::createBaseRelocBlock(uint64_t pageAddr,
- const BaseReloc *begin,
- const BaseReloc *end) const {
- // Relocation blocks should be padded with IMAGE_REL_I386_ABSOLUTE to be
- // aligned to a DWORD size boundary.
- uint32_t size = llvm::RoundUpToAlignment(
- sizeof(ulittle32_t) * 2 + sizeof(ulittle16_t) * (end - begin),
- sizeof(ulittle32_t));
- std::vector<uint8_t> contents(size);
- uint8_t *ptr = &contents[0];
-
- // The first four bytes is the page RVA.
- write32le(ptr, pageAddr);
- ptr += sizeof(ulittle32_t);
-
- // The second four bytes is the size of the block, including the the page
- // RVA and this size field.
- write32le(ptr, size);
- ptr += sizeof(ulittle32_t);
-
- uint64_t mask = _ctx.getPageSize() - 1;
- for (const BaseReloc *i = begin; i < end; ++i) {
- write16le(ptr, (i->type << 12) | (i->addr & mask));
- ptr += sizeof(ulittle16_t);
- }
- return contents;
-}
-
-} // end anonymous namespace
-
-class PECOFFWriter : public Writer {
-public:
- explicit PECOFFWriter(const PECOFFLinkingContext &context)
- : _ctx(context), _numSections(0), _imageSizeInMemory(_ctx.getPageSize()),
- _imageSizeOnDisk(0) {}
-
- template <class PEHeader> void build(const File &linkedFile);
- std::error_code writeFile(const File &linkedFile, StringRef path) override;
-
-private:
- void applyAllRelocations(uint8_t *bufferStart);
- void printAllAtomAddresses() const;
- void reorderSEHTableEntries(uint8_t *bufferStart);
- void reorderSEHTableEntriesX86(uint8_t *bufferStart);
- void reorderSEHTableEntriesX64(uint8_t *bufferStart);
-
- void addChunk(Chunk *chunk);
- void addSectionChunk(std::unique_ptr<SectionChunk> chunk,
- SectionHeaderTableChunk *table,
- StringTableChunk *stringTable);
- void setImageSizeOnDisk();
- uint64_t
- calcSectionSize(llvm::COFF::SectionCharacteristics sectionType) const;
-
- uint64_t calcSizeOfInitializedData() const {
- return calcSectionSize(llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA);
- }
-
- uint64_t calcSizeOfUninitializedData() const {
- return calcSectionSize(llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA);
- }
-
- uint64_t calcSizeOfCode() const {
- return calcSectionSize(llvm::COFF::IMAGE_SCN_CNT_CODE);
- }
-
- std::vector<std::unique_ptr<Chunk> > _chunks;
- const PECOFFLinkingContext &_ctx;
- uint32_t _numSections;
-
- // The size of the image in memory. This is initialized with
- // _ctx.getPageSize(), as the first page starting at ImageBase is usually left
- // unmapped. IIUC there's no technical reason to do so, but we'll follow that
- // convention so that we don't produce odd-looking binary.
- uint32_t _imageSizeInMemory;
-
- // The size of the image on disk. This is basically the sum of all chunks in
- // the output file with paddings between them.
- uint32_t _imageSizeOnDisk;
-
- // The map from atom to its relative virtual address.
- std::map<const Atom *, uint64_t> _atomRva;
-};
-
-StringRef customSectionName(const DefinedAtom *atom) {
- assert(atom->sectionChoice() == DefinedAtom::sectionCustomRequired);
- StringRef s = atom->customSectionName();
- size_t pos = s.find('$');
- return (pos == StringRef::npos) ? s : s.substr(0, pos);
-}
-
-StringRef chooseSectionByContent(const DefinedAtom *atom) {
- switch (atom->contentType()) {
- case DefinedAtom::typeCode:
- return ".text";
- case DefinedAtom::typeZeroFill:
- return ".bss";
- case DefinedAtom::typeData:
- if (atom->permissions() == DefinedAtom::permR__)
- return ".rdata";
- if (atom->permissions() == DefinedAtom::permRW_)
- return ".data";
- break;
- default:
- break;
- }
- llvm::errs() << "Atom: contentType=" << atom->contentType()
- << " permission=" << atom->permissions() << "\n";
- llvm::report_fatal_error("Failed to choose section based on content");
-}
-
-typedef std::map<StringRef, std::vector<const DefinedAtom *> > AtomVectorMap;
-
-void groupAtoms(const PECOFFLinkingContext &ctx, const File &file,
- AtomVectorMap &result) {
- for (const DefinedAtom *atom : file.defined()) {
- if (atom->sectionChoice() == DefinedAtom::sectionCustomRequired) {
- StringRef section = customSectionName(atom);
- result[ctx.getOutputSectionName(section)].push_back(atom);
- continue;
- }
- if (atom->sectionChoice() == DefinedAtom::sectionBasedOnContent) {
- StringRef section = chooseSectionByContent(atom);
- result[ctx.getOutputSectionName(section)].push_back(atom);
- continue;
- }
- llvm_unreachable("Unknown section choice");
- }
-}
-
-static const DefinedAtom *findTLSUsedSymbol(const PECOFFLinkingContext &ctx,
- const File &file) {
- StringRef sym = ctx.decorateSymbol("_tls_used");
- for (const DefinedAtom *atom : file.defined())
- if (atom->name() == sym)
- return atom;
- return nullptr;
-}
-
-// Create all chunks that consist of the output file.
-template <class PEHeader>
-void PECOFFWriter::build(const File &linkedFile) {
- AtomVectorMap atoms;
- groupAtoms(_ctx, linkedFile, atoms);
-
- // Create file chunks and add them to the list.
- auto *dosStub = new DOSStubChunk(_ctx);
- auto *peHeader = new PEHeaderChunk<PEHeader>(_ctx);
- auto *dataDirectory = new DataDirectoryChunk();
- auto *sectionTable = new SectionHeaderTableChunk();
- auto *stringTable = new StringTableChunk();
- addChunk(dosStub);
- addChunk(peHeader);
- addChunk(dataDirectory);
- addChunk(sectionTable);
- addChunk(stringTable);
-
- // Create sections and add the atoms to them.
- for (auto i : atoms) {
- StringRef sectionName = i.first;
- std::vector<const DefinedAtom *> &contents = i.second;
- std::unique_ptr<SectionChunk> section(
- new AtomChunk(_ctx, sectionName, contents));
- if (section->size() > 0)
- addSectionChunk(std::move(section), sectionTable, stringTable);
- }
-
- // Build atom to its RVA map.
- for (std::unique_ptr<Chunk> &cp : _chunks)
- if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp))
- chunk->buildAtomRvaMap(_atomRva);
-
- // We know the addresses of all defined atoms that needs to be
- // relocated. So we can create the ".reloc" section which contains
- // all the relocation sites.
- if (_ctx.getBaseRelocationEnabled()) {
- std::unique_ptr<SectionChunk> baseReloc(new BaseRelocChunk(_chunks, _ctx));
- if (baseReloc->size()) {
- SectionChunk &ref = *baseReloc;
- addSectionChunk(std::move(baseReloc), sectionTable, stringTable);
- dataDirectory->setField(DataDirectoryIndex::BASE_RELOCATION_TABLE,
- ref.getVirtualAddress(), ref.size());
- }
- }
-
- setImageSizeOnDisk();
-
- if (stringTable->size()) {
- peHeader->setPointerToSymbolTable(stringTable->fileOffset());
- peHeader->setNumberOfSymbols(1);
- }
-
- for (std::unique_ptr<Chunk> &chunk : _chunks) {
- SectionChunk *section = dyn_cast<SectionChunk>(chunk.get());
- if (!section)
- continue;
- if (section->getSectionName() == ".text") {
- peHeader->setBaseOfCode(section->getVirtualAddress());
-
- // Find the virtual address of the entry point symbol if any. PECOFF spec
- // says that entry point for dll images is optional, in which case it must
- // be set to 0.
- if (_ctx.hasEntry()) {
- AtomChunk *atom = cast<AtomChunk>(section);
- uint64_t entryPointAddress =
- atom->getAtomVirtualAddress(_ctx.getEntrySymbolName());
-
- if (entryPointAddress) {
- // NOTE: ARM NT assumes a pure THUMB execution, so adjust the entry
- // point accordingly
- if (_ctx.getMachineType() == llvm::COFF::IMAGE_FILE_MACHINE_ARMNT)
- entryPointAddress |= 1;
- peHeader->setAddressOfEntryPoint(entryPointAddress);
- }
- } else {
- peHeader->setAddressOfEntryPoint(0);
- }
- }
- StringRef name = section->getSectionName();
- if (name == ".data") {
- peHeader->setBaseOfData(section->getVirtualAddress());
- continue;
- }
- DataDirectoryIndex ignore = DataDirectoryIndex(-1);
- DataDirectoryIndex idx = llvm::StringSwitch<DataDirectoryIndex>(name)
- .Case(".pdata", DataDirectoryIndex::EXCEPTION_TABLE)
- .Case(".rsrc", DataDirectoryIndex::RESOURCE_TABLE)
- .Case(".idata.a", DataDirectoryIndex::IAT)
- .Case(".idata.d", DataDirectoryIndex::IMPORT_TABLE)
- .Case(".edata", DataDirectoryIndex::EXPORT_TABLE)
- .Case(".loadcfg", DataDirectoryIndex::LOAD_CONFIG_TABLE)
- .Case(".didat.d", DataDirectoryIndex::DELAY_IMPORT_DESCRIPTOR)
- .Default(ignore);
- if (idx == ignore)
- continue;
- dataDirectory->setField(idx, section->getVirtualAddress(), section->size());
- }
-
- if (const DefinedAtom *atom = findTLSUsedSymbol(_ctx, linkedFile)) {
- dataDirectory->setField(DataDirectoryIndex::TLS_TABLE, _atomRva[atom],
- 0x18);
- }
-
- // Now that we know the size and file offset of sections. Set the file
- // header accordingly.
- peHeader->setSizeOfCode(calcSizeOfCode());
- peHeader->setSizeOfInitializedData(calcSizeOfInitializedData());
- peHeader->setSizeOfUninitializedData(calcSizeOfUninitializedData());
- peHeader->setNumberOfSections(_numSections);
- peHeader->setSizeOfImage(_imageSizeInMemory);
- peHeader->setSizeOfHeaders(sectionTable->fileOffset() + sectionTable->size());
-}
-
-std::error_code PECOFFWriter::writeFile(const File &linkedFile,
- StringRef path) {
- if (_ctx.is64Bit()) {
- this->build<llvm::object::pe32plus_header>(linkedFile);
- } else {
- this->build<llvm::object::pe32_header>(linkedFile);
- }
-
- uint64_t totalSize =
- _chunks.back()->fileOffset() + _chunks.back()->onDiskSize();
- std::unique_ptr<llvm::FileOutputBuffer> buffer;
- std::error_code ec = llvm::FileOutputBuffer::create(
- path, totalSize, buffer, llvm::FileOutputBuffer::F_executable);
- if (ec)
- return ec;
-
- for (std::unique_ptr<Chunk> &chunk : _chunks)
- chunk->write(buffer->getBufferStart() + chunk->fileOffset());
- applyAllRelocations(buffer->getBufferStart());
- reorderSEHTableEntries(buffer->getBufferStart());
- DEBUG(printAllAtomAddresses());
-
- if (_ctx.isDll())
- writeImportLibrary(_ctx);
-
- return buffer->commit();
-}
-
-/// Apply relocations to the output file buffer. This two pass. In the first
-/// pass, we visit all atoms to create a map from atom to its virtual
-/// address. In the second pass, we visit all relocation references to fix
-/// up addresses in the buffer.
-void PECOFFWriter::applyAllRelocations(uint8_t *bufferStart) {
- // Create the list of section start addresses. It's needed for
- // relocations of SECREL type.
- std::vector<uint64_t> sectionRva;
- for (auto &cp : _chunks)
- if (SectionChunk *section = dyn_cast<SectionChunk>(&*cp))
- sectionRva.push_back(section->getVirtualAddress());
-
- uint64_t base = _ctx.getBaseAddress();
- for (auto &cp : _chunks) {
- if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp)) {
- switch (_ctx.getMachineType()) {
- default: llvm_unreachable("unsupported machine type");
- case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT:
- chunk->applyRelocationsARM(bufferStart, _atomRva, sectionRva, base);
- break;
- case llvm::COFF::IMAGE_FILE_MACHINE_I386:
- chunk->applyRelocationsX86(bufferStart, _atomRva, sectionRva, base);
- break;
- case llvm::COFF::IMAGE_FILE_MACHINE_AMD64:
- chunk->applyRelocationsX64(bufferStart, _atomRva, sectionRva, base);
- break;
- }
- }
- }
-}
-
-/// Print atom VAs. Used only for debugging.
-void PECOFFWriter::printAllAtomAddresses() const {
- for (auto &cp : _chunks)
- if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp))
- chunk->printAtomAddresses(_ctx.getBaseAddress());
-}
-
-void PECOFFWriter::reorderSEHTableEntries(uint8_t *bufferStart) {
- auto machineType = _ctx.getMachineType();
- if (machineType == llvm::COFF::IMAGE_FILE_MACHINE_I386)
- reorderSEHTableEntriesX86(bufferStart);
- if (machineType == llvm::COFF::IMAGE_FILE_MACHINE_AMD64)
- reorderSEHTableEntriesX64(bufferStart);
-}
-
-/// It seems that the entries in .sxdata must be sorted. This function is called
-/// after a COFF file image is created in memory and before it is written to
-/// disk. It is safe to reorder entries at this stage because the contents of
-/// the entries are RVAs and there's no reference to a .sxdata entry other than
-/// to the beginning of the section.
-void PECOFFWriter::reorderSEHTableEntriesX86(uint8_t *bufferStart) {
- for (std::unique_ptr<Chunk> &chunk : _chunks) {
- if (SectionChunk *section = dyn_cast<SectionChunk>(chunk.get())) {
- if (section->getSectionName() == ".sxdata") {
- int numEntries = section->size() / sizeof(ulittle32_t);
- ulittle32_t *begin = reinterpret_cast<ulittle32_t *>(bufferStart + section->fileOffset());
- ulittle32_t *end = begin + numEntries;
- std::sort(begin, end);
- }
- }
- }
-}
-
-/// The entries in .pdata must be sorted according to its BeginAddress field
-/// value. It's safe to do it because of the same reason as .sxdata.
-void PECOFFWriter::reorderSEHTableEntriesX64(uint8_t *bufferStart) {
- for (std::unique_ptr<Chunk> &chunk : _chunks) {
- if (SectionChunk *section = dyn_cast<SectionChunk>(chunk.get())) {
- if (section->getSectionName() != ".pdata")
- continue;
- int numEntries = section->size() / sizeof(coff_runtime_function_x64);
- coff_runtime_function_x64 *begin =
- (coff_runtime_function_x64 *)(bufferStart + section->fileOffset());
- coff_runtime_function_x64 *end = begin + numEntries;
- std::sort(begin, end, [](const coff_runtime_function_x64 &lhs,
- const coff_runtime_function_x64 &rhs) {
- return lhs.BeginAddress < rhs.BeginAddress;
- });
- }
- }
-}
-
-void PECOFFWriter::addChunk(Chunk *chunk) {
- _chunks.push_back(std::unique_ptr<Chunk>(chunk));
-}
-
-void PECOFFWriter::addSectionChunk(std::unique_ptr<SectionChunk> chunk,
- SectionHeaderTableChunk *table,
- StringTableChunk *stringTable) {
- table->addSection(chunk.get());
- _numSections++;
-
- StringRef sectionName = chunk->getSectionName();
- if (sectionName.size() > llvm::COFF::NameSize) {
- uint32_t stringTableOffset = stringTable->addSectionName(sectionName);
- chunk->setStringTableOffset(stringTableOffset);
- }
-
- // Compute and set the starting address of sections when loaded in
- // memory. They are different from positions on disk because sections need
- // to be sector-aligned on disk but page-aligned in memory.
- _imageSizeInMemory = llvm::RoundUpToAlignment(
- _imageSizeInMemory, chunk->memAlign());
- chunk->setVirtualAddress(_imageSizeInMemory);
- _imageSizeInMemory = llvm::RoundUpToAlignment(
- _imageSizeInMemory + chunk->size(), _ctx.getPageSize());
- _chunks.push_back(std::move(chunk));
-}
-
-void PECOFFWriter::setImageSizeOnDisk() {
- for (auto &chunk : _chunks) {
- // Compute and set the offset of the chunk in the output file.
- _imageSizeOnDisk =
- llvm::RoundUpToAlignment(_imageSizeOnDisk, chunk->align());
- chunk->setFileOffset(_imageSizeOnDisk);
- _imageSizeOnDisk += chunk->onDiskSize();
- }
-}
-
-uint64_t PECOFFWriter::calcSectionSize(
- llvm::COFF::SectionCharacteristics sectionType) const {
- uint64_t ret = 0;
- for (auto &cp : _chunks)
- if (SectionChunk *chunk = dyn_cast<SectionChunk>(&*cp))
- if (chunk->getCharacteristics() & sectionType)
- ret += chunk->onDiskSize();
- return ret;
-}
-
-} // end namespace pecoff
-
-std::unique_ptr<Writer> createWriterPECOFF(const PECOFFLinkingContext &info) {
- return std::unique_ptr<Writer>(new pecoff::PECOFFWriter(info));
-}
-
-} // end namespace lld
OpenPOWER on IntegriCloud