diff options
Diffstat (limited to 'COFF/Writer.cpp')
-rw-r--r-- | COFF/Writer.cpp | 765 |
1 files changed, 765 insertions, 0 deletions
diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp new file mode 100644 index 0000000000000..a74b316b87a41 --- /dev/null +++ b/COFF/Writer.cpp @@ -0,0 +1,765 @@ +//===- Writer.cpp ---------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Config.h" +#include "DLL.h" +#include "Error.h" +#include "InputFiles.h" +#include "SymbolTable.h" +#include "Symbols.h" +#include "Writer.h" +#include "lld/Core/Parallel.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cstdio> +#include <map> +#include <memory> +#include <utility> + +using namespace llvm; +using namespace llvm::COFF; +using namespace llvm::object; +using namespace llvm::support; +using namespace llvm::support::endian; +using namespace lld; +using namespace lld::coff; + +static const int PageSize = 4096; +static const int SectorSize = 512; +static const int DOSStubSize = 64; +static const int NumberfOfDataDirectory = 16; + +namespace { +// The writer writes a SymbolTable result to a file. +class Writer { +public: + Writer(SymbolTable *T) : Symtab(T) {} + void run(); + +private: + void createSections(); + void createMiscChunks(); + void createImportTables(); + void createExportTable(); + void assignAddresses(); + void removeEmptySections(); + void createSymbolAndStringTable(); + void openFile(StringRef OutputPath); + template <typename PEHeaderTy> void writeHeader(); + void fixSafeSEHSymbols(); + void writeSections(); + void sortExceptionTable(); + void applyRelocations(); + + llvm::Optional<coff_symbol16> createSymbol(Defined *D); + size_t addEntryToStringTable(StringRef Str); + + OutputSection *findSection(StringRef Name); + OutputSection *createSection(StringRef Name); + void addBaserels(OutputSection *Dest); + void addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V); + + uint32_t getSizeOfInitializedData(); + std::map<StringRef, std::vector<DefinedImportData *>> binImports(); + + SymbolTable *Symtab; + std::unique_ptr<llvm::FileOutputBuffer> Buffer; + llvm::SpecificBumpPtrAllocator<OutputSection> CAlloc; + llvm::SpecificBumpPtrAllocator<BaserelChunk> BAlloc; + std::vector<OutputSection *> OutputSections; + std::vector<char> Strtab; + std::vector<llvm::object::coff_symbol16> OutputSymtab; + IdataContents Idata; + DelayLoadContents DelayIdata; + EdataContents Edata; + std::unique_ptr<SEHTableChunk> SEHTable; + + uint64_t FileSize; + uint32_t PointerToSymbolTable = 0; + uint64_t SizeOfImage; + uint64_t SizeOfHeaders; + + std::vector<std::unique_ptr<Chunk>> Chunks; +}; +} // anonymous namespace + +namespace lld { +namespace coff { + +void writeResult(SymbolTable *T) { Writer(T).run(); } + +// OutputSection represents a section in an output file. It's a +// container of chunks. OutputSection and Chunk are 1:N relationship. +// Chunks cannot belong to more than one OutputSections. The writer +// creates multiple OutputSections and assign them unique, +// non-overlapping file offsets and RVAs. +class OutputSection { +public: + OutputSection(StringRef N) : Name(N), Header({}) {} + void setRVA(uint64_t); + void setFileOffset(uint64_t); + void addChunk(Chunk *C); + StringRef getName() { return Name; } + std::vector<Chunk *> &getChunks() { return Chunks; } + void addPermissions(uint32_t C); + uint32_t getPermissions() { return Header.Characteristics & PermMask; } + uint32_t getCharacteristics() { return Header.Characteristics; } + uint64_t getRVA() { return Header.VirtualAddress; } + uint64_t getFileOff() { return Header.PointerToRawData; } + void writeHeaderTo(uint8_t *Buf); + + // Returns the size of this section in an executable memory image. + // This may be smaller than the raw size (the raw size is multiple + // of disk sector size, so there may be padding at end), or may be + // larger (if that's the case, the loader reserves spaces after end + // of raw data). + uint64_t getVirtualSize() { return Header.VirtualSize; } + + // Returns the size of the section in the output file. + uint64_t getRawSize() { return Header.SizeOfRawData; } + + // Set offset into the string table storing this section name. + // Used only when the name is longer than 8 bytes. + void setStringTableOff(uint32_t V) { StringTableOff = V; } + + // N.B. The section index is one based. + uint32_t SectionIndex = 0; + +private: + StringRef Name; + coff_section Header; + uint32_t StringTableOff = 0; + std::vector<Chunk *> Chunks; +}; + +void OutputSection::setRVA(uint64_t RVA) { + Header.VirtualAddress = RVA; + for (Chunk *C : Chunks) + C->setRVA(C->getRVA() + RVA); +} + +void OutputSection::setFileOffset(uint64_t Off) { + // If a section has no actual data (i.e. BSS section), we want to + // set 0 to its PointerToRawData. Otherwise the output is rejected + // by the loader. + if (Header.SizeOfRawData == 0) + return; + Header.PointerToRawData = Off; +} + +void OutputSection::addChunk(Chunk *C) { + Chunks.push_back(C); + C->setOutputSection(this); + uint64_t Off = Header.VirtualSize; + Off = RoundUpToAlignment(Off, C->getAlign()); + C->setRVA(Off); + C->setOutputSectionOff(Off); + Off += C->getSize(); + Header.VirtualSize = Off; + if (C->hasData()) + Header.SizeOfRawData = RoundUpToAlignment(Off, SectorSize); +} + +void OutputSection::addPermissions(uint32_t C) { + Header.Characteristics |= C & PermMask; +} + +// Write the section header to a given buffer. +void OutputSection::writeHeaderTo(uint8_t *Buf) { + auto *Hdr = reinterpret_cast<coff_section *>(Buf); + *Hdr = Header; + if (StringTableOff) { + // If name is too long, write offset into the string table as a name. + sprintf(Hdr->Name, "/%d", StringTableOff); + } else { + assert(!Config->Debug || Name.size() <= COFF::NameSize); + strncpy(Hdr->Name, Name.data(), + std::min(Name.size(), (size_t)COFF::NameSize)); + } +} + +uint64_t Defined::getSecrel() { + if (auto *D = dyn_cast<DefinedRegular>(this)) + return getRVA() - D->getChunk()->getOutputSection()->getRVA(); + error("SECREL relocation points to a non-regular symbol"); +} + +uint64_t Defined::getSectionIndex() { + if (auto *D = dyn_cast<DefinedRegular>(this)) + return D->getChunk()->getOutputSection()->SectionIndex; + error("SECTION relocation points to a non-regular symbol"); +} + +bool Defined::isExecutable() { + const auto X = IMAGE_SCN_MEM_EXECUTE; + if (auto *D = dyn_cast<DefinedRegular>(this)) + return D->getChunk()->getOutputSection()->getPermissions() & X; + return isa<DefinedImportThunk>(this); +} + +} // namespace coff +} // namespace lld + +// The main function of the writer. +void Writer::run() { + createSections(); + createMiscChunks(); + createImportTables(); + createExportTable(); + if (Config->Relocatable) + createSection(".reloc"); + assignAddresses(); + removeEmptySections(); + createSymbolAndStringTable(); + openFile(Config->OutputFile); + if (Config->is64()) { + writeHeader<pe32plus_header>(); + } else { + writeHeader<pe32_header>(); + } + fixSafeSEHSymbols(); + writeSections(); + sortExceptionTable(); + error(Buffer->commit(), "Failed to write the output file"); +} + +static StringRef getOutputSection(StringRef Name) { + StringRef S = Name.split('$').first; + auto It = Config->Merge.find(S); + if (It == Config->Merge.end()) + return S; + return It->second; +} + +// Create output section objects and add them to OutputSections. +void Writer::createSections() { + // First, bin chunks by name. + std::map<StringRef, std::vector<Chunk *>> Map; + for (Chunk *C : Symtab->getChunks()) { + auto *SC = dyn_cast<SectionChunk>(C); + if (SC && !SC->isLive()) { + if (Config->Verbose) + SC->printDiscardedMessage(); + continue; + } + Map[C->getSectionName()].push_back(C); + } + + // Then create an OutputSection for each section. + // '$' and all following characters in input section names are + // discarded when determining output section. So, .text$foo + // contributes to .text, for example. See PE/COFF spec 3.2. + SmallDenseMap<StringRef, OutputSection *> Sections; + for (auto Pair : Map) { + StringRef Name = getOutputSection(Pair.first); + OutputSection *&Sec = Sections[Name]; + if (!Sec) { + Sec = new (CAlloc.Allocate()) OutputSection(Name); + OutputSections.push_back(Sec); + } + std::vector<Chunk *> &Chunks = Pair.second; + for (Chunk *C : Chunks) { + Sec->addChunk(C); + Sec->addPermissions(C->getPermissions()); + } + } +} + +void Writer::createMiscChunks() { + // Create thunks for locally-dllimported symbols. + if (!Symtab->LocalImportChunks.empty()) { + OutputSection *Sec = createSection(".rdata"); + for (Chunk *C : Symtab->LocalImportChunks) + Sec->addChunk(C); + } + + // Create SEH table. x86-only. + if (Config->Machine != I386) + return; + std::set<Defined *> Handlers; + for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) { + if (!File->SEHCompat) + return; + for (SymbolBody *B : File->SEHandlers) + Handlers.insert(cast<Defined>(B->repl())); + } + SEHTable.reset(new SEHTableChunk(Handlers)); + createSection(".rdata")->addChunk(SEHTable.get()); +} + +// Create .idata section for the DLL-imported symbol table. +// The format of this section is inherently Windows-specific. +// IdataContents class abstracted away the details for us, +// so we just let it create chunks and add them to the section. +void Writer::createImportTables() { + if (Symtab->ImportFiles.empty()) + return; + + // Initialize DLLOrder so that import entries are ordered in + // the same order as in the command line. (That affects DLL + // initialization order, and this ordering is MSVC-compatible.) + for (ImportFile *File : Symtab->ImportFiles) { + std::string DLL = StringRef(File->DLLName).lower(); + if (Config->DLLOrder.count(DLL) == 0) + Config->DLLOrder[DLL] = Config->DLLOrder.size(); + } + + OutputSection *Text = createSection(".text"); + for (ImportFile *File : Symtab->ImportFiles) { + if (DefinedImportThunk *Thunk = File->ThunkSym) + Text->addChunk(Thunk->getChunk()); + if (Config->DelayLoads.count(StringRef(File->DLLName).lower())) { + DelayIdata.add(File->ImpSym); + } else { + Idata.add(File->ImpSym); + } + } + if (!Idata.empty()) { + OutputSection *Sec = createSection(".idata"); + for (Chunk *C : Idata.getChunks()) + Sec->addChunk(C); + } + if (!DelayIdata.empty()) { + Defined *Helper = cast<Defined>(Config->DelayLoadHelper->repl()); + DelayIdata.create(Helper); + OutputSection *Sec = createSection(".didat"); + for (Chunk *C : DelayIdata.getChunks()) + Sec->addChunk(C); + Sec = createSection(".data"); + for (Chunk *C : DelayIdata.getDataChunks()) + Sec->addChunk(C); + Sec = createSection(".text"); + for (std::unique_ptr<Chunk> &C : DelayIdata.getCodeChunks()) + Sec->addChunk(C.get()); + } +} + +void Writer::createExportTable() { + if (Config->Exports.empty()) + return; + OutputSection *Sec = createSection(".edata"); + for (std::unique_ptr<Chunk> &C : Edata.Chunks) + Sec->addChunk(C.get()); +} + +// The Windows loader doesn't seem to like empty sections, +// so we remove them if any. +void Writer::removeEmptySections() { + auto IsEmpty = [](OutputSection *S) { return S->getVirtualSize() == 0; }; + OutputSections.erase( + std::remove_if(OutputSections.begin(), OutputSections.end(), IsEmpty), + OutputSections.end()); + uint32_t Idx = 1; + for (OutputSection *Sec : OutputSections) + Sec->SectionIndex = Idx++; +} + +size_t Writer::addEntryToStringTable(StringRef Str) { + assert(Str.size() > COFF::NameSize); + size_t OffsetOfEntry = Strtab.size() + 4; // +4 for the size field + Strtab.insert(Strtab.end(), Str.begin(), Str.end()); + Strtab.push_back('\0'); + return OffsetOfEntry; +} + +Optional<coff_symbol16> Writer::createSymbol(Defined *Def) { + if (auto *D = dyn_cast<DefinedRegular>(Def)) + if (!D->getChunk()->isLive()) + return None; + + coff_symbol16 Sym; + StringRef Name = Def->getName(); + if (Name.size() > COFF::NameSize) { + Sym.Name.Offset.Zeroes = 0; + Sym.Name.Offset.Offset = addEntryToStringTable(Name); + } else { + memset(Sym.Name.ShortName, 0, COFF::NameSize); + memcpy(Sym.Name.ShortName, Name.data(), Name.size()); + } + + if (auto *D = dyn_cast<DefinedCOFF>(Def)) { + COFFSymbolRef Ref = D->getCOFFSymbol(); + Sym.Type = Ref.getType(); + Sym.StorageClass = Ref.getStorageClass(); + } else { + Sym.Type = IMAGE_SYM_TYPE_NULL; + Sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; + } + Sym.NumberOfAuxSymbols = 0; + + switch (Def->kind()) { + case SymbolBody::DefinedAbsoluteKind: + case SymbolBody::DefinedRelativeKind: + Sym.Value = Def->getRVA(); + Sym.SectionNumber = IMAGE_SYM_ABSOLUTE; + break; + default: { + uint64_t RVA = Def->getRVA(); + OutputSection *Sec = nullptr; + for (OutputSection *S : OutputSections) { + if (S->getRVA() > RVA) + break; + Sec = S; + } + Sym.Value = RVA - Sec->getRVA(); + Sym.SectionNumber = Sec->SectionIndex; + break; + } + } + return Sym; +} + +void Writer::createSymbolAndStringTable() { + if (!Config->Debug || !Config->WriteSymtab) + return; + + // Name field in the section table is 8 byte long. Longer names need + // to be written to the string table. First, construct string table. + for (OutputSection *Sec : OutputSections) { + StringRef Name = Sec->getName(); + if (Name.size() <= COFF::NameSize) + continue; + Sec->setStringTableOff(addEntryToStringTable(Name)); + } + + for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) + for (SymbolBody *B : File->getSymbols()) + if (auto *D = dyn_cast<Defined>(B)) + if (Optional<coff_symbol16> Sym = createSymbol(D)) + OutputSymtab.push_back(*Sym); + + for (ImportFile *File : Symtab->ImportFiles) + for (SymbolBody *B : File->getSymbols()) + if (Optional<coff_symbol16> Sym = createSymbol(cast<Defined>(B))) + OutputSymtab.push_back(*Sym); + + OutputSection *LastSection = OutputSections.back(); + // We position the symbol table to be adjacent to the end of the last section. + uint64_t FileOff = + LastSection->getFileOff() + + RoundUpToAlignment(LastSection->getRawSize(), SectorSize); + if (!OutputSymtab.empty()) { + PointerToSymbolTable = FileOff; + FileOff += OutputSymtab.size() * sizeof(coff_symbol16); + } + if (!Strtab.empty()) + FileOff += Strtab.size() + 4; + FileSize = RoundUpToAlignment(FileOff, SectorSize); +} + +// Visits all sections to assign incremental, non-overlapping RVAs and +// file offsets. +void Writer::assignAddresses() { + SizeOfHeaders = DOSStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + + sizeof(data_directory) * NumberfOfDataDirectory + + sizeof(coff_section) * OutputSections.size(); + SizeOfHeaders += + Config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); + SizeOfHeaders = RoundUpToAlignment(SizeOfHeaders, SectorSize); + uint64_t RVA = 0x1000; // The first page is kept unmapped. + FileSize = SizeOfHeaders; + // Move DISCARDABLE (or non-memory-mapped) sections to the end of file because + // the loader cannot handle holes. + std::stable_partition( + OutputSections.begin(), OutputSections.end(), [](OutputSection *S) { + return (S->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE) == 0; + }); + for (OutputSection *Sec : OutputSections) { + if (Sec->getName() == ".reloc") + addBaserels(Sec); + Sec->setRVA(RVA); + Sec->setFileOffset(FileSize); + RVA += RoundUpToAlignment(Sec->getVirtualSize(), PageSize); + FileSize += RoundUpToAlignment(Sec->getRawSize(), SectorSize); + } + SizeOfImage = SizeOfHeaders + RoundUpToAlignment(RVA - 0x1000, PageSize); +} + +template <typename PEHeaderTy> void Writer::writeHeader() { + // Write DOS stub + uint8_t *Buf = Buffer->getBufferStart(); + auto *DOS = reinterpret_cast<dos_header *>(Buf); + Buf += DOSStubSize; + DOS->Magic[0] = 'M'; + DOS->Magic[1] = 'Z'; + DOS->AddressOfRelocationTable = sizeof(dos_header); + DOS->AddressOfNewExeHeader = DOSStubSize; + + // Write PE magic + memcpy(Buf, PEMagic, sizeof(PEMagic)); + Buf += sizeof(PEMagic); + + // Write COFF header + auto *COFF = reinterpret_cast<coff_file_header *>(Buf); + Buf += sizeof(*COFF); + COFF->Machine = Config->Machine; + COFF->NumberOfSections = OutputSections.size(); + COFF->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE; + if (Config->LargeAddressAware) + COFF->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE; + if (!Config->is64()) + COFF->Characteristics |= IMAGE_FILE_32BIT_MACHINE; + if (Config->DLL) + COFF->Characteristics |= IMAGE_FILE_DLL; + if (!Config->Relocatable) + COFF->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED; + COFF->SizeOfOptionalHeader = + sizeof(PEHeaderTy) + sizeof(data_directory) * NumberfOfDataDirectory; + + // Write PE header + auto *PE = reinterpret_cast<PEHeaderTy *>(Buf); + Buf += sizeof(*PE); + PE->Magic = Config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32; + PE->ImageBase = Config->ImageBase; + PE->SectionAlignment = PageSize; + PE->FileAlignment = SectorSize; + PE->MajorImageVersion = Config->MajorImageVersion; + PE->MinorImageVersion = Config->MinorImageVersion; + PE->MajorOperatingSystemVersion = Config->MajorOSVersion; + PE->MinorOperatingSystemVersion = Config->MinorOSVersion; + PE->MajorSubsystemVersion = Config->MajorOSVersion; + PE->MinorSubsystemVersion = Config->MinorOSVersion; + PE->Subsystem = Config->Subsystem; + PE->SizeOfImage = SizeOfImage; + PE->SizeOfHeaders = SizeOfHeaders; + if (!Config->NoEntry) { + Defined *Entry = cast<Defined>(Config->Entry->repl()); + PE->AddressOfEntryPoint = Entry->getRVA(); + // Pointer to thumb code must have the LSB set, so adjust it. + if (Config->Machine == ARMNT) + PE->AddressOfEntryPoint |= 1; + } + PE->SizeOfStackReserve = Config->StackReserve; + PE->SizeOfStackCommit = Config->StackCommit; + PE->SizeOfHeapReserve = Config->HeapReserve; + PE->SizeOfHeapCommit = Config->HeapCommit; + if (Config->DynamicBase) + PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE; + if (Config->HighEntropyVA) + PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA; + if (!Config->AllowBind) + PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_BIND; + if (Config->NxCompat) + PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT; + if (!Config->AllowIsolation) + PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION; + if (Config->TerminalServerAware) + PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; + PE->NumberOfRvaAndSize = NumberfOfDataDirectory; + if (OutputSection *Text = findSection(".text")) { + PE->BaseOfCode = Text->getRVA(); + PE->SizeOfCode = Text->getRawSize(); + } + PE->SizeOfInitializedData = getSizeOfInitializedData(); + + // Write data directory + auto *Dir = reinterpret_cast<data_directory *>(Buf); + Buf += sizeof(*Dir) * NumberfOfDataDirectory; + if (OutputSection *Sec = findSection(".edata")) { + Dir[EXPORT_TABLE].RelativeVirtualAddress = Sec->getRVA(); + Dir[EXPORT_TABLE].Size = Sec->getVirtualSize(); + } + if (!Idata.empty()) { + Dir[IMPORT_TABLE].RelativeVirtualAddress = Idata.getDirRVA(); + Dir[IMPORT_TABLE].Size = Idata.getDirSize(); + Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA(); + Dir[IAT].Size = Idata.getIATSize(); + } + if (!DelayIdata.empty()) { + Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress = + DelayIdata.getDirRVA(); + Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize(); + } + if (OutputSection *Sec = findSection(".rsrc")) { + Dir[RESOURCE_TABLE].RelativeVirtualAddress = Sec->getRVA(); + Dir[RESOURCE_TABLE].Size = Sec->getVirtualSize(); + } + if (OutputSection *Sec = findSection(".reloc")) { + Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA(); + Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize(); + } + if (OutputSection *Sec = findSection(".pdata")) { + Dir[EXCEPTION_TABLE].RelativeVirtualAddress = Sec->getRVA(); + Dir[EXCEPTION_TABLE].Size = Sec->getVirtualSize(); + } + if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) { + if (Defined *B = dyn_cast<Defined>(Sym->Body)) { + Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA(); + Dir[TLS_TABLE].Size = 40; + } + } + if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) { + if (Defined *B = dyn_cast<Defined>(Sym->Body)) { + Dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = B->getRVA(); + Dir[LOAD_CONFIG_TABLE].Size = Config->is64() ? 112 : 64; + } + } + + // Write section table + for (OutputSection *Sec : OutputSections) { + Sec->writeHeaderTo(Buf); + Buf += sizeof(coff_section); + } + + if (OutputSymtab.empty()) + return; + + COFF->PointerToSymbolTable = PointerToSymbolTable; + uint32_t NumberOfSymbols = OutputSymtab.size(); + COFF->NumberOfSymbols = NumberOfSymbols; + auto *SymbolTable = reinterpret_cast<coff_symbol16 *>( + Buffer->getBufferStart() + COFF->PointerToSymbolTable); + for (size_t I = 0; I != NumberOfSymbols; ++I) + SymbolTable[I] = OutputSymtab[I]; + // Create the string table, it follows immediately after the symbol table. + // The first 4 bytes is length including itself. + Buf = reinterpret_cast<uint8_t *>(&SymbolTable[NumberOfSymbols]); + write32le(Buf, Strtab.size() + 4); + memcpy(Buf + 4, Strtab.data(), Strtab.size()); +} + +void Writer::openFile(StringRef Path) { + ErrorOr<std::unique_ptr<FileOutputBuffer>> BufferOrErr = + FileOutputBuffer::create(Path, FileSize, FileOutputBuffer::F_executable); + error(BufferOrErr, Twine("failed to open ") + Path); + Buffer = std::move(*BufferOrErr); +} + +void Writer::fixSafeSEHSymbols() { + if (!SEHTable) + return; + Config->SEHTable->setRVA(SEHTable->getRVA()); + Config->SEHCount->setVA(SEHTable->getSize() / 4); +} + +// Write section contents to a mmap'ed file. +void Writer::writeSections() { + uint8_t *Buf = Buffer->getBufferStart(); + for (OutputSection *Sec : OutputSections) { + uint8_t *SecBuf = Buf + Sec->getFileOff(); + // Fill gaps between functions in .text with INT3 instructions + // instead of leaving as NUL bytes (which can be interpreted as + // ADD instructions). + if (Sec->getPermissions() & IMAGE_SCN_CNT_CODE) + memset(SecBuf, 0xCC, Sec->getRawSize()); + parallel_for_each(Sec->getChunks().begin(), Sec->getChunks().end(), + [&](Chunk *C) { C->writeTo(SecBuf); }); + } +} + +// Sort .pdata section contents according to PE/COFF spec 5.5. +void Writer::sortExceptionTable() { + OutputSection *Sec = findSection(".pdata"); + if (!Sec) + return; + // We assume .pdata contains function table entries only. + uint8_t *Begin = Buffer->getBufferStart() + Sec->getFileOff(); + uint8_t *End = Begin + Sec->getVirtualSize(); + if (Config->Machine == AMD64) { + struct Entry { ulittle32_t Begin, End, Unwind; }; + parallel_sort( + (Entry *)Begin, (Entry *)End, + [](const Entry &A, const Entry &B) { return A.Begin < B.Begin; }); + return; + } + if (Config->Machine == ARMNT) { + struct Entry { ulittle32_t Begin, Unwind; }; + parallel_sort( + (Entry *)Begin, (Entry *)End, + [](const Entry &A, const Entry &B) { return A.Begin < B.Begin; }); + return; + } + errs() << "warning: don't know how to handle .pdata.\n"; +} + +OutputSection *Writer::findSection(StringRef Name) { + for (OutputSection *Sec : OutputSections) + if (Sec->getName() == Name) + return Sec; + return nullptr; +} + +uint32_t Writer::getSizeOfInitializedData() { + uint32_t Res = 0; + for (OutputSection *S : OutputSections) + if (S->getPermissions() & IMAGE_SCN_CNT_INITIALIZED_DATA) + Res += S->getRawSize(); + return Res; +} + +// Returns an existing section or create a new one if not found. +OutputSection *Writer::createSection(StringRef Name) { + if (auto *Sec = findSection(Name)) + return Sec; + const auto DATA = IMAGE_SCN_CNT_INITIALIZED_DATA; + const auto BSS = IMAGE_SCN_CNT_UNINITIALIZED_DATA; + const auto CODE = IMAGE_SCN_CNT_CODE; + const auto DISCARDABLE = IMAGE_SCN_MEM_DISCARDABLE; + const auto R = IMAGE_SCN_MEM_READ; + const auto W = IMAGE_SCN_MEM_WRITE; + const auto X = IMAGE_SCN_MEM_EXECUTE; + uint32_t Perms = StringSwitch<uint32_t>(Name) + .Case(".bss", BSS | R | W) + .Case(".data", DATA | R | W) + .Case(".didat", DATA | R) + .Case(".edata", DATA | R) + .Case(".idata", DATA | R) + .Case(".rdata", DATA | R) + .Case(".reloc", DATA | DISCARDABLE | R) + .Case(".text", CODE | R | X) + .Default(0); + if (!Perms) + llvm_unreachable("unknown section name"); + auto Sec = new (CAlloc.Allocate()) OutputSection(Name); + Sec->addPermissions(Perms); + OutputSections.push_back(Sec); + return Sec; +} + +// Dest is .reloc section. Add contents to that section. +void Writer::addBaserels(OutputSection *Dest) { + std::vector<Baserel> V; + for (OutputSection *Sec : OutputSections) { + if (Sec == Dest) + continue; + // Collect all locations for base relocations. + for (Chunk *C : Sec->getChunks()) + C->getBaserels(&V); + // Add the addresses to .reloc section. + if (!V.empty()) + addBaserelBlocks(Dest, V); + V.clear(); + } +} + +// Add addresses to .reloc section. Note that addresses are grouped by page. +void Writer::addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V) { + const uint32_t Mask = ~uint32_t(PageSize - 1); + uint32_t Page = V[0].RVA & Mask; + size_t I = 0, J = 1; + for (size_t E = V.size(); J < E; ++J) { + uint32_t P = V[J].RVA & Mask; + if (P == Page) + continue; + BaserelChunk *Buf = BAlloc.Allocate(); + Dest->addChunk(new (Buf) BaserelChunk(Page, &V[I], &V[0] + J)); + I = J; + Page = P; + } + if (I == J) + return; + BaserelChunk *Buf = BAlloc.Allocate(); + Dest->addChunk(new (Buf) BaserelChunk(Page, &V[I], &V[0] + J)); +} |