diff options
| author | Rui Ueyama <ruiu@google.com> | 2015-08-06 16:19:35 +0000 |
|---|---|---|
| committer | Rui Ueyama <ruiu@google.com> | 2015-08-06 16:19:35 +0000 |
| commit | 251b0e268bef44159ad6916b972ba9c74ce51384 (patch) | |
| tree | 841faf6ee22c2de7a7faed3f9dd81cd33f691d62 /lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp | |
| parent | f77e909f0a19ffd3223e1afef14c4afa5959197a (diff) | |
| download | bcm5719-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.cpp | 1417 |
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> §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) & (~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> §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(); - 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 |

