|  | //===- Writer.cpp ---------------------------------------------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "Writer.h" | 
|  | #include "COFFLinkerContext.h" | 
|  | #include "CallGraphSort.h" | 
|  | #include "Config.h" | 
|  | #include "DLL.h" | 
|  | #include "InputFiles.h" | 
|  | #include "LLDMapFile.h" | 
|  | #include "MapFile.h" | 
|  | #include "PDB.h" | 
|  | #include "SymbolTable.h" | 
|  | #include "Symbols.h" | 
|  | #include "lld/Common/ErrorHandler.h" | 
|  | #include "lld/Common/Memory.h" | 
|  | #include "lld/Common/Timer.h" | 
|  | #include "llvm/ADT/DenseMap.h" | 
|  | #include "llvm/ADT/STLExtras.h" | 
|  | #include "llvm/ADT/StringSet.h" | 
|  | #include "llvm/BinaryFormat/COFF.h" | 
|  | #include "llvm/Support/BinaryStreamReader.h" | 
|  | #include "llvm/Support/Debug.h" | 
|  | #include "llvm/Support/Endian.h" | 
|  | #include "llvm/Support/FileOutputBuffer.h" | 
|  | #include "llvm/Support/Parallel.h" | 
|  | #include "llvm/Support/Path.h" | 
|  | #include "llvm/Support/RandomNumberGenerator.h" | 
|  | #include "llvm/Support/TimeProfiler.h" | 
|  | #include "llvm/Support/xxhash.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; | 
|  |  | 
|  | /* To re-generate DOSProgram: | 
|  | $ cat > /tmp/DOSProgram.asm | 
|  | org 0 | 
|  | ; Copy cs to ds. | 
|  | push cs | 
|  | pop ds | 
|  | ; Point ds:dx at the $-terminated string. | 
|  | mov dx, str | 
|  | ; Int 21/AH=09h: Write string to standard output. | 
|  | mov ah, 0x9 | 
|  | int 0x21 | 
|  | ; Int 21/AH=4Ch: Exit with return code (in AL). | 
|  | mov ax, 0x4C01 | 
|  | int 0x21 | 
|  | str: | 
|  | db 'This program cannot be run in DOS mode.$' | 
|  | align 8, db 0 | 
|  | $ nasm -fbin /tmp/DOSProgram.asm -o /tmp/DOSProgram.bin | 
|  | $ xxd -i /tmp/DOSProgram.bin | 
|  | */ | 
|  | static unsigned char dosProgram[] = { | 
|  | 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, | 
|  | 0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, | 
|  | 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, | 
|  | 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, | 
|  | 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x24, 0x00, 0x00 | 
|  | }; | 
|  | static_assert(sizeof(dosProgram) % 8 == 0, | 
|  | "DOSProgram size must be multiple of 8"); | 
|  |  | 
|  | static const int dosStubSize = sizeof(dos_header) + sizeof(dosProgram); | 
|  | static_assert(dosStubSize % 8 == 0, "DOSStub size must be multiple of 8"); | 
|  |  | 
|  | static const int numberOfDataDirectory = 16; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class DebugDirectoryChunk : public NonSectionChunk { | 
|  | public: | 
|  | DebugDirectoryChunk(const COFFLinkerContext &c, | 
|  | const std::vector<std::pair<COFF::DebugType, Chunk *>> &r, | 
|  | bool writeRepro) | 
|  | : records(r), writeRepro(writeRepro), ctx(c) {} | 
|  |  | 
|  | size_t getSize() const override { | 
|  | return (records.size() + int(writeRepro)) * sizeof(debug_directory); | 
|  | } | 
|  |  | 
|  | void writeTo(uint8_t *b) const override { | 
|  | auto *d = reinterpret_cast<debug_directory *>(b); | 
|  |  | 
|  | for (const std::pair<COFF::DebugType, Chunk *>& record : records) { | 
|  | Chunk *c = record.second; | 
|  | const OutputSection *os = ctx.getOutputSection(c); | 
|  | uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA()); | 
|  | fillEntry(d, record.first, c->getSize(), c->getRVA(), offs); | 
|  | ++d; | 
|  | } | 
|  |  | 
|  | if (writeRepro) { | 
|  | // FIXME: The COFF spec allows either a 0-sized entry to just say | 
|  | // "the timestamp field is really a hash", or a 4-byte size field | 
|  | // followed by that many bytes containing a longer hash (with the | 
|  | // lowest 4 bytes usually being the timestamp in little-endian order). | 
|  | // Consider storing the full 8 bytes computed by xxh3_64bits here. | 
|  | fillEntry(d, COFF::IMAGE_DEBUG_TYPE_REPRO, 0, 0, 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | void setTimeDateStamp(uint32_t timeDateStamp) { | 
|  | for (support::ulittle32_t *tds : timeDateStamps) | 
|  | *tds = timeDateStamp; | 
|  | } | 
|  |  | 
|  | private: | 
|  | void fillEntry(debug_directory *d, COFF::DebugType debugType, size_t size, | 
|  | uint64_t rva, uint64_t offs) const { | 
|  | d->Characteristics = 0; | 
|  | d->TimeDateStamp = 0; | 
|  | d->MajorVersion = 0; | 
|  | d->MinorVersion = 0; | 
|  | d->Type = debugType; | 
|  | d->SizeOfData = size; | 
|  | d->AddressOfRawData = rva; | 
|  | d->PointerToRawData = offs; | 
|  |  | 
|  | timeDateStamps.push_back(&d->TimeDateStamp); | 
|  | } | 
|  |  | 
|  | mutable std::vector<support::ulittle32_t *> timeDateStamps; | 
|  | const std::vector<std::pair<COFF::DebugType, Chunk *>> &records; | 
|  | bool writeRepro; | 
|  | const COFFLinkerContext &ctx; | 
|  | }; | 
|  |  | 
|  | class CVDebugRecordChunk : public NonSectionChunk { | 
|  | public: | 
|  | CVDebugRecordChunk(const COFFLinkerContext &c) : ctx(c) {} | 
|  |  | 
|  | size_t getSize() const override { | 
|  | return sizeof(codeview::DebugInfo) + ctx.config.pdbAltPath.size() + 1; | 
|  | } | 
|  |  | 
|  | void writeTo(uint8_t *b) const override { | 
|  | // Save off the DebugInfo entry to backfill the file signature (build id) | 
|  | // in Writer::writeBuildId | 
|  | buildId = reinterpret_cast<codeview::DebugInfo *>(b); | 
|  |  | 
|  | // variable sized field (PDB Path) | 
|  | char *p = reinterpret_cast<char *>(b + sizeof(*buildId)); | 
|  | if (!ctx.config.pdbAltPath.empty()) | 
|  | memcpy(p, ctx.config.pdbAltPath.data(), ctx.config.pdbAltPath.size()); | 
|  | p[ctx.config.pdbAltPath.size()] = '\0'; | 
|  | } | 
|  |  | 
|  | mutable codeview::DebugInfo *buildId = nullptr; | 
|  |  | 
|  | private: | 
|  | const COFFLinkerContext &ctx; | 
|  | }; | 
|  |  | 
|  | class ExtendedDllCharacteristicsChunk : public NonSectionChunk { | 
|  | public: | 
|  | ExtendedDllCharacteristicsChunk(uint32_t c) : characteristics(c) {} | 
|  |  | 
|  | size_t getSize() const override { return 4; } | 
|  |  | 
|  | void writeTo(uint8_t *buf) const override { write32le(buf, characteristics); } | 
|  |  | 
|  | uint32_t characteristics = 0; | 
|  | }; | 
|  |  | 
|  | // PartialSection represents a group of chunks that contribute to an | 
|  | // OutputSection. Collating a collection of PartialSections of same name and | 
|  | // characteristics constitutes the OutputSection. | 
|  | class PartialSectionKey { | 
|  | public: | 
|  | StringRef name; | 
|  | unsigned characteristics; | 
|  |  | 
|  | bool operator<(const PartialSectionKey &other) const { | 
|  | int c = name.compare(other.name); | 
|  | if (c > 0) | 
|  | return false; | 
|  | if (c == 0) | 
|  | return characteristics < other.characteristics; | 
|  | return true; | 
|  | } | 
|  | }; | 
|  |  | 
|  | struct ChunkRange { | 
|  | Chunk *first = nullptr, *last; | 
|  | }; | 
|  |  | 
|  | // The writer writes a SymbolTable result to a file. | 
|  | class Writer { | 
|  | public: | 
|  | Writer(COFFLinkerContext &c) | 
|  | : buffer(errorHandler().outputBuffer), delayIdata(c), edata(c), ctx(c) {} | 
|  | void run(); | 
|  |  | 
|  | private: | 
|  | void createSections(); | 
|  | void createMiscChunks(); | 
|  | void createImportTables(); | 
|  | void appendImportThunks(); | 
|  | void locateImportTables(); | 
|  | void createExportTable(); | 
|  | void mergeSections(); | 
|  | void sortECChunks(); | 
|  | void removeUnusedSections(); | 
|  | void assignAddresses(); | 
|  | bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin); | 
|  | std::pair<Defined *, bool> getThunk(DenseMap<uint64_t, Defined *> &lastThunks, | 
|  | Defined *target, uint64_t p, | 
|  | uint16_t type, int margin); | 
|  | bool createThunks(OutputSection *os, int margin); | 
|  | bool verifyRanges(const std::vector<Chunk *> chunks); | 
|  | void createECCodeMap(); | 
|  | void finalizeAddresses(); | 
|  | void removeEmptySections(); | 
|  | void assignOutputSectionIndices(); | 
|  | void createSymbolAndStringTable(); | 
|  | void openFile(StringRef outputPath); | 
|  | template <typename PEHeaderTy> void writeHeader(); | 
|  | void createSEHTable(); | 
|  | void createRuntimePseudoRelocs(); | 
|  | void createECChunks(); | 
|  | void insertCtorDtorSymbols(); | 
|  | void markSymbolsWithRelocations(ObjFile *file, SymbolRVASet &usedSymbols); | 
|  | void createGuardCFTables(); | 
|  | void markSymbolsForRVATable(ObjFile *file, | 
|  | ArrayRef<SectionChunk *> symIdxChunks, | 
|  | SymbolRVASet &tableSymbols); | 
|  | void getSymbolsFromSections(ObjFile *file, | 
|  | ArrayRef<SectionChunk *> symIdxChunks, | 
|  | std::vector<Symbol *> &symbols); | 
|  | void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, | 
|  | StringRef countSym, bool hasFlag=false); | 
|  | void setSectionPermissions(); | 
|  | void setECSymbols(); | 
|  | void writeSections(); | 
|  | void writeBuildId(); | 
|  | void writePEChecksum(); | 
|  | void sortSections(); | 
|  | template <typename T> void sortExceptionTable(ChunkRange &exceptionTable); | 
|  | void sortExceptionTables(); | 
|  | void sortCRTSectionChunks(std::vector<Chunk *> &chunks); | 
|  | void addSyntheticIdata(); | 
|  | void sortBySectionOrder(std::vector<Chunk *> &chunks); | 
|  | void fixPartialSectionChars(StringRef name, uint32_t chars); | 
|  | bool fixGnuImportChunks(); | 
|  | void fixTlsAlignment(); | 
|  | PartialSection *createPartialSection(StringRef name, uint32_t outChars); | 
|  | PartialSection *findPartialSection(StringRef name, uint32_t outChars); | 
|  |  | 
|  | std::optional<coff_symbol16> createSymbol(Defined *d); | 
|  | size_t addEntryToStringTable(StringRef str); | 
|  |  | 
|  | OutputSection *findSection(StringRef name); | 
|  | void addBaserels(); | 
|  | void addBaserelBlocks(std::vector<Baserel> &v); | 
|  |  | 
|  | uint32_t getSizeOfInitializedData(); | 
|  |  | 
|  | void prepareLoadConfig(); | 
|  | template <typename T> void prepareLoadConfig(T *loadConfig); | 
|  | template <typename T> void checkLoadConfigGuardData(const T *loadConfig); | 
|  |  | 
|  | std::unique_ptr<FileOutputBuffer> &buffer; | 
|  | std::map<PartialSectionKey, PartialSection *> partialSections; | 
|  | std::vector<char> strtab; | 
|  | std::vector<llvm::object::coff_symbol16> outputSymtab; | 
|  | std::vector<ECCodeMapEntry> codeMap; | 
|  | IdataContents idata; | 
|  | Chunk *importTableStart = nullptr; | 
|  | uint64_t importTableSize = 0; | 
|  | Chunk *edataStart = nullptr; | 
|  | Chunk *edataEnd = nullptr; | 
|  | Chunk *iatStart = nullptr; | 
|  | uint64_t iatSize = 0; | 
|  | DelayLoadContents delayIdata; | 
|  | EdataContents edata; | 
|  | bool setNoSEHCharacteristic = false; | 
|  | uint32_t tlsAlignment = 0; | 
|  |  | 
|  | DebugDirectoryChunk *debugDirectory = nullptr; | 
|  | std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords; | 
|  | CVDebugRecordChunk *buildId = nullptr; | 
|  | ArrayRef<uint8_t> sectionTable; | 
|  |  | 
|  | uint64_t fileSize; | 
|  | uint32_t pointerToSymbolTable = 0; | 
|  | uint64_t sizeOfImage; | 
|  | uint64_t sizeOfHeaders; | 
|  |  | 
|  | OutputSection *textSec; | 
|  | OutputSection *rdataSec; | 
|  | OutputSection *buildidSec; | 
|  | OutputSection *dataSec; | 
|  | OutputSection *pdataSec; | 
|  | OutputSection *idataSec; | 
|  | OutputSection *edataSec; | 
|  | OutputSection *didatSec; | 
|  | OutputSection *rsrcSec; | 
|  | OutputSection *relocSec; | 
|  | OutputSection *ctorsSec; | 
|  | OutputSection *dtorsSec; | 
|  | // Either .rdata section or .buildid section. | 
|  | OutputSection *debugInfoSec; | 
|  |  | 
|  | // The range of .pdata sections in the output file. | 
|  | // | 
|  | // We need to keep track of the location of .pdata in whichever section it | 
|  | // gets merged into so that we can sort its contents and emit a correct data | 
|  | // directory entry for the exception table. This is also the case for some | 
|  | // other sections (such as .edata) but because the contents of those sections | 
|  | // are entirely linker-generated we can keep track of their locations using | 
|  | // the chunks that the linker creates. All .pdata chunks come from input | 
|  | // files, so we need to keep track of them separately. | 
|  | ChunkRange pdata; | 
|  |  | 
|  | // x86_64 .pdata sections on ARM64EC/ARM64X targets. | 
|  | ChunkRange hybridPdata; | 
|  |  | 
|  | COFFLinkerContext &ctx; | 
|  | }; | 
|  | } // anonymous namespace | 
|  |  | 
|  | void lld::coff::writeResult(COFFLinkerContext &ctx) { | 
|  | llvm::TimeTraceScope timeScope("Write output(s)"); | 
|  | Writer(ctx).run(); | 
|  | } | 
|  |  | 
|  | void OutputSection::addChunk(Chunk *c) { | 
|  | chunks.push_back(c); | 
|  | } | 
|  |  | 
|  | void OutputSection::insertChunkAtStart(Chunk *c) { | 
|  | chunks.insert(chunks.begin(), c); | 
|  | } | 
|  |  | 
|  | void OutputSection::setPermissions(uint32_t c) { | 
|  | header.Characteristics &= ~permMask; | 
|  | header.Characteristics |= c; | 
|  | } | 
|  |  | 
|  | void OutputSection::merge(OutputSection *other) { | 
|  | chunks.insert(chunks.end(), other->chunks.begin(), other->chunks.end()); | 
|  | other->chunks.clear(); | 
|  | contribSections.insert(contribSections.end(), other->contribSections.begin(), | 
|  | other->contribSections.end()); | 
|  | other->contribSections.clear(); | 
|  |  | 
|  | // MS link.exe compatibility: when merging a code section into a data section, | 
|  | // mark the target section as a code section. | 
|  | if (other->header.Characteristics & IMAGE_SCN_CNT_CODE) { | 
|  | header.Characteristics |= IMAGE_SCN_CNT_CODE; | 
|  | header.Characteristics &= | 
|  | ~(IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_CNT_UNINITIALIZED_DATA); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Write the section header to a given buffer. | 
|  | void OutputSection::writeHeaderTo(uint8_t *buf, bool isDebug) { | 
|  | 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. | 
|  | encodeSectionName(hdr->Name, stringTableOff); | 
|  | } else { | 
|  | assert(!isDebug || name.size() <= COFF::NameSize || | 
|  | (hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0); | 
|  | strncpy(hdr->Name, name.data(), | 
|  | std::min(name.size(), (size_t)COFF::NameSize)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void OutputSection::addContributingPartialSection(PartialSection *sec) { | 
|  | contribSections.push_back(sec); | 
|  | } | 
|  |  | 
|  | // Check whether the target address S is in range from a relocation | 
|  | // of type relType at address P. | 
|  | bool Writer::isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) { | 
|  | if (ctx.config.machine == ARMNT) { | 
|  | int64_t diff = AbsoluteDifference(s, p + 4) + margin; | 
|  | switch (relType) { | 
|  | case IMAGE_REL_ARM_BRANCH20T: | 
|  | return isInt<21>(diff); | 
|  | case IMAGE_REL_ARM_BRANCH24T: | 
|  | case IMAGE_REL_ARM_BLX23T: | 
|  | return isInt<25>(diff); | 
|  | default: | 
|  | return true; | 
|  | } | 
|  | } else if (ctx.config.machine == ARM64) { | 
|  | int64_t diff = AbsoluteDifference(s, p) + margin; | 
|  | switch (relType) { | 
|  | case IMAGE_REL_ARM64_BRANCH26: | 
|  | return isInt<28>(diff); | 
|  | case IMAGE_REL_ARM64_BRANCH19: | 
|  | return isInt<21>(diff); | 
|  | case IMAGE_REL_ARM64_BRANCH14: | 
|  | return isInt<16>(diff); | 
|  | default: | 
|  | return true; | 
|  | } | 
|  | } else { | 
|  | llvm_unreachable("Unexpected architecture"); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Return the last thunk for the given target if it is in range, | 
|  | // or create a new one. | 
|  | std::pair<Defined *, bool> | 
|  | Writer::getThunk(DenseMap<uint64_t, Defined *> &lastThunks, Defined *target, | 
|  | uint64_t p, uint16_t type, int margin) { | 
|  | Defined *&lastThunk = lastThunks[target->getRVA()]; | 
|  | if (lastThunk && isInRange(type, lastThunk->getRVA(), p, margin)) | 
|  | return {lastThunk, false}; | 
|  | Chunk *c; | 
|  | switch (ctx.config.machine) { | 
|  | case ARMNT: | 
|  | c = make<RangeExtensionThunkARM>(ctx, target); | 
|  | break; | 
|  | case ARM64: | 
|  | c = make<RangeExtensionThunkARM64>(ctx, target); | 
|  | break; | 
|  | default: | 
|  | llvm_unreachable("Unexpected architecture"); | 
|  | } | 
|  | Defined *d = make<DefinedSynthetic>("range_extension_thunk", c); | 
|  | lastThunk = d; | 
|  | return {d, true}; | 
|  | } | 
|  |  | 
|  | // This checks all relocations, and for any relocation which isn't in range | 
|  | // it adds a thunk after the section chunk that contains the relocation. | 
|  | // If the latest thunk for the specific target is in range, that is used | 
|  | // instead of creating a new thunk. All range checks are done with the | 
|  | // specified margin, to make sure that relocations that originally are in | 
|  | // range, but only barely, also get thunks - in case other added thunks makes | 
|  | // the target go out of range. | 
|  | // | 
|  | // After adding thunks, we verify that all relocations are in range (with | 
|  | // no extra margin requirements). If this failed, we restart (throwing away | 
|  | // the previously created thunks) and retry with a wider margin. | 
|  | bool Writer::createThunks(OutputSection *os, int margin) { | 
|  | bool addressesChanged = false; | 
|  | DenseMap<uint64_t, Defined *> lastThunks; | 
|  | DenseMap<std::pair<ObjFile *, Defined *>, uint32_t> thunkSymtabIndices; | 
|  | size_t thunksSize = 0; | 
|  | // Recheck Chunks.size() each iteration, since we can insert more | 
|  | // elements into it. | 
|  | for (size_t i = 0; i != os->chunks.size(); ++i) { | 
|  | SectionChunk *sc = dyn_cast_or_null<SectionChunk>(os->chunks[i]); | 
|  | if (!sc) | 
|  | continue; | 
|  | size_t thunkInsertionSpot = i + 1; | 
|  |  | 
|  | // Try to get a good enough estimate of where new thunks will be placed. | 
|  | // Offset this by the size of the new thunks added so far, to make the | 
|  | // estimate slightly better. | 
|  | size_t thunkInsertionRVA = sc->getRVA() + sc->getSize() + thunksSize; | 
|  | ObjFile *file = sc->file; | 
|  | std::vector<std::pair<uint32_t, uint32_t>> relocReplacements; | 
|  | ArrayRef<coff_relocation> originalRelocs = | 
|  | file->getCOFFObj()->getRelocations(sc->header); | 
|  | for (size_t j = 0, e = originalRelocs.size(); j < e; ++j) { | 
|  | const coff_relocation &rel = originalRelocs[j]; | 
|  | Symbol *relocTarget = file->getSymbol(rel.SymbolTableIndex); | 
|  |  | 
|  | // The estimate of the source address P should be pretty accurate, | 
|  | // but we don't know whether the target Symbol address should be | 
|  | // offset by thunksSize or not (or by some of thunksSize but not all of | 
|  | // it), giving us some uncertainty once we have added one thunk. | 
|  | uint64_t p = sc->getRVA() + rel.VirtualAddress + thunksSize; | 
|  |  | 
|  | Defined *sym = dyn_cast_or_null<Defined>(relocTarget); | 
|  | if (!sym) | 
|  | continue; | 
|  |  | 
|  | uint64_t s = sym->getRVA(); | 
|  |  | 
|  | if (isInRange(rel.Type, s, p, margin)) | 
|  | continue; | 
|  |  | 
|  | // If the target isn't in range, hook it up to an existing or new thunk. | 
|  | auto [thunk, wasNew] = getThunk(lastThunks, sym, p, rel.Type, margin); | 
|  | if (wasNew) { | 
|  | Chunk *thunkChunk = thunk->getChunk(); | 
|  | thunkChunk->setRVA( | 
|  | thunkInsertionRVA); // Estimate of where it will be located. | 
|  | os->chunks.insert(os->chunks.begin() + thunkInsertionSpot, thunkChunk); | 
|  | thunkInsertionSpot++; | 
|  | thunksSize += thunkChunk->getSize(); | 
|  | thunkInsertionRVA += thunkChunk->getSize(); | 
|  | addressesChanged = true; | 
|  | } | 
|  |  | 
|  | // To redirect the relocation, add a symbol to the parent object file's | 
|  | // symbol table, and replace the relocation symbol table index with the | 
|  | // new index. | 
|  | auto insertion = thunkSymtabIndices.insert({{file, thunk}, ~0U}); | 
|  | uint32_t &thunkSymbolIndex = insertion.first->second; | 
|  | if (insertion.second) | 
|  | thunkSymbolIndex = file->addRangeThunkSymbol(thunk); | 
|  | relocReplacements.emplace_back(j, thunkSymbolIndex); | 
|  | } | 
|  |  | 
|  | // Get a writable copy of this section's relocations so they can be | 
|  | // modified. If the relocations point into the object file, allocate new | 
|  | // memory. Otherwise, this must be previously allocated memory that can be | 
|  | // modified in place. | 
|  | ArrayRef<coff_relocation> curRelocs = sc->getRelocs(); | 
|  | MutableArrayRef<coff_relocation> newRelocs; | 
|  | if (originalRelocs.data() == curRelocs.data()) { | 
|  | newRelocs = MutableArrayRef( | 
|  | bAlloc().Allocate<coff_relocation>(originalRelocs.size()), | 
|  | originalRelocs.size()); | 
|  | } else { | 
|  | newRelocs = MutableArrayRef( | 
|  | const_cast<coff_relocation *>(curRelocs.data()), curRelocs.size()); | 
|  | } | 
|  |  | 
|  | // Copy each relocation, but replace the symbol table indices which need | 
|  | // thunks. | 
|  | auto nextReplacement = relocReplacements.begin(); | 
|  | auto endReplacement = relocReplacements.end(); | 
|  | for (size_t i = 0, e = originalRelocs.size(); i != e; ++i) { | 
|  | newRelocs[i] = originalRelocs[i]; | 
|  | if (nextReplacement != endReplacement && nextReplacement->first == i) { | 
|  | newRelocs[i].SymbolTableIndex = nextReplacement->second; | 
|  | ++nextReplacement; | 
|  | } | 
|  | } | 
|  |  | 
|  | sc->setRelocs(newRelocs); | 
|  | } | 
|  | return addressesChanged; | 
|  | } | 
|  |  | 
|  | // Create a code map for CHPE metadata. | 
|  | void Writer::createECCodeMap() { | 
|  | if (!isArm64EC(ctx.config.machine)) | 
|  | return; | 
|  |  | 
|  | // Clear the map in case we were're recomputing the map after adding | 
|  | // a range extension thunk. | 
|  | codeMap.clear(); | 
|  |  | 
|  | std::optional<chpe_range_type> lastType; | 
|  | Chunk *first, *last; | 
|  |  | 
|  | auto closeRange = [&]() { | 
|  | if (lastType) { | 
|  | codeMap.push_back({first, last, *lastType}); | 
|  | lastType.reset(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | for (OutputSection *sec : ctx.outputSections) { | 
|  | for (Chunk *c : sec->chunks) { | 
|  | // Skip empty section chunks. MS link.exe does not seem to do that and | 
|  | // generates empty code ranges in some cases. | 
|  | if (isa<SectionChunk>(c) && !c->getSize()) | 
|  | continue; | 
|  |  | 
|  | std::optional<chpe_range_type> chunkType = c->getArm64ECRangeType(); | 
|  | if (chunkType != lastType) { | 
|  | closeRange(); | 
|  | first = c; | 
|  | lastType = chunkType; | 
|  | } | 
|  | last = c; | 
|  | } | 
|  | } | 
|  |  | 
|  | closeRange(); | 
|  |  | 
|  | Symbol *tableCountSym = ctx.symtab.findUnderscore("__hybrid_code_map_count"); | 
|  | cast<DefinedAbsolute>(tableCountSym)->setVA(codeMap.size()); | 
|  | } | 
|  |  | 
|  | // Verify that all relocations are in range, with no extra margin requirements. | 
|  | bool Writer::verifyRanges(const std::vector<Chunk *> chunks) { | 
|  | for (Chunk *c : chunks) { | 
|  | SectionChunk *sc = dyn_cast_or_null<SectionChunk>(c); | 
|  | if (!sc) | 
|  | continue; | 
|  |  | 
|  | ArrayRef<coff_relocation> relocs = sc->getRelocs(); | 
|  | for (const coff_relocation &rel : relocs) { | 
|  | Symbol *relocTarget = sc->file->getSymbol(rel.SymbolTableIndex); | 
|  |  | 
|  | Defined *sym = dyn_cast_or_null<Defined>(relocTarget); | 
|  | if (!sym) | 
|  | continue; | 
|  |  | 
|  | uint64_t p = sc->getRVA() + rel.VirtualAddress; | 
|  | uint64_t s = sym->getRVA(); | 
|  |  | 
|  | if (!isInRange(rel.Type, s, p, 0)) | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Assign addresses and add thunks if necessary. | 
|  | void Writer::finalizeAddresses() { | 
|  | assignAddresses(); | 
|  | if (ctx.config.machine != ARMNT && ctx.config.machine != ARM64) | 
|  | return; | 
|  |  | 
|  | size_t origNumChunks = 0; | 
|  | for (OutputSection *sec : ctx.outputSections) { | 
|  | sec->origChunks = sec->chunks; | 
|  | origNumChunks += sec->chunks.size(); | 
|  | } | 
|  |  | 
|  | int pass = 0; | 
|  | int margin = 1024 * 100; | 
|  | while (true) { | 
|  | llvm::TimeTraceScope timeScope2("Add thunks pass"); | 
|  |  | 
|  | // First check whether we need thunks at all, or if the previous pass of | 
|  | // adding them turned out ok. | 
|  | bool rangesOk = true; | 
|  | size_t numChunks = 0; | 
|  | { | 
|  | llvm::TimeTraceScope timeScope3("Verify ranges"); | 
|  | for (OutputSection *sec : ctx.outputSections) { | 
|  | if (!verifyRanges(sec->chunks)) { | 
|  | rangesOk = false; | 
|  | break; | 
|  | } | 
|  | numChunks += sec->chunks.size(); | 
|  | } | 
|  | } | 
|  | if (rangesOk) { | 
|  | if (pass > 0) | 
|  | log("Added " + Twine(numChunks - origNumChunks) + " thunks with " + | 
|  | "margin " + Twine(margin) + " in " + Twine(pass) + " passes"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (pass >= 10) | 
|  | fatal("adding thunks hasn't converged after " + Twine(pass) + " passes"); | 
|  |  | 
|  | if (pass > 0) { | 
|  | // If the previous pass didn't work out, reset everything back to the | 
|  | // original conditions before retrying with a wider margin. This should | 
|  | // ideally never happen under real circumstances. | 
|  | for (OutputSection *sec : ctx.outputSections) | 
|  | sec->chunks = sec->origChunks; | 
|  | margin *= 2; | 
|  | } | 
|  |  | 
|  | // Try adding thunks everywhere where it is needed, with a margin | 
|  | // to avoid things going out of range due to the added thunks. | 
|  | bool addressesChanged = false; | 
|  | { | 
|  | llvm::TimeTraceScope timeScope3("Create thunks"); | 
|  | for (OutputSection *sec : ctx.outputSections) | 
|  | addressesChanged |= createThunks(sec, margin); | 
|  | } | 
|  | // If the verification above thought we needed thunks, we should have | 
|  | // added some. | 
|  | assert(addressesChanged); | 
|  | (void)addressesChanged; | 
|  |  | 
|  | // Recalculate the layout for the whole image (and verify the ranges at | 
|  | // the start of the next round). | 
|  | assignAddresses(); | 
|  |  | 
|  | pass++; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Writer::writePEChecksum() { | 
|  | if (!ctx.config.writeCheckSum) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | llvm::TimeTraceScope timeScope("PE checksum"); | 
|  |  | 
|  | // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#checksum | 
|  | uint32_t *buf = (uint32_t *)buffer->getBufferStart(); | 
|  | uint32_t size = (uint32_t)(buffer->getBufferSize()); | 
|  |  | 
|  | coff_file_header *coffHeader = | 
|  | (coff_file_header *)((uint8_t *)buf + dosStubSize + sizeof(PEMagic)); | 
|  | pe32_header *peHeader = | 
|  | (pe32_header *)((uint8_t *)coffHeader + sizeof(coff_file_header)); | 
|  |  | 
|  | uint64_t sum = 0; | 
|  | uint32_t count = size; | 
|  | ulittle16_t *addr = (ulittle16_t *)buf; | 
|  |  | 
|  | // The PE checksum algorithm, implemented as suggested in RFC1071 | 
|  | while (count > 1) { | 
|  | sum += *addr++; | 
|  | count -= 2; | 
|  | } | 
|  |  | 
|  | // Add left-over byte, if any | 
|  | if (count > 0) | 
|  | sum += *(unsigned char *)addr; | 
|  |  | 
|  | // Fold 32-bit sum to 16 bits | 
|  | while (sum >> 16) { | 
|  | sum = (sum & 0xffff) + (sum >> 16); | 
|  | } | 
|  |  | 
|  | sum += size; | 
|  | peHeader->CheckSum = sum; | 
|  | } | 
|  |  | 
|  | // The main function of the writer. | 
|  | void Writer::run() { | 
|  | { | 
|  | llvm::TimeTraceScope timeScope("Write PE"); | 
|  | ScopedTimer t1(ctx.codeLayoutTimer); | 
|  |  | 
|  | createImportTables(); | 
|  | createSections(); | 
|  | appendImportThunks(); | 
|  | // Import thunks must be added before the Control Flow Guard tables are | 
|  | // added. | 
|  | createMiscChunks(); | 
|  | createExportTable(); | 
|  | mergeSections(); | 
|  | sortECChunks(); | 
|  | removeUnusedSections(); | 
|  | finalizeAddresses(); | 
|  | removeEmptySections(); | 
|  | assignOutputSectionIndices(); | 
|  | setSectionPermissions(); | 
|  | setECSymbols(); | 
|  | createSymbolAndStringTable(); | 
|  |  | 
|  | if (fileSize > UINT32_MAX) | 
|  | fatal("image size (" + Twine(fileSize) + ") " + | 
|  | "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")"); | 
|  |  | 
|  | openFile(ctx.config.outputFile); | 
|  | if (ctx.config.is64()) { | 
|  | writeHeader<pe32plus_header>(); | 
|  | } else { | 
|  | writeHeader<pe32_header>(); | 
|  | } | 
|  | writeSections(); | 
|  | prepareLoadConfig(); | 
|  | sortExceptionTables(); | 
|  |  | 
|  | // Fix up the alignment in the TLS Directory's characteristic field, | 
|  | // if a specific alignment value is needed | 
|  | if (tlsAlignment) | 
|  | fixTlsAlignment(); | 
|  | } | 
|  |  | 
|  | if (!ctx.config.pdbPath.empty() && ctx.config.debug) { | 
|  | assert(buildId); | 
|  | createPDB(ctx, sectionTable, buildId->buildId); | 
|  | } | 
|  | writeBuildId(); | 
|  |  | 
|  | writeLLDMapFile(ctx); | 
|  | writeMapFile(ctx); | 
|  |  | 
|  | writePEChecksum(); | 
|  |  | 
|  | if (errorCount()) | 
|  | return; | 
|  |  | 
|  | llvm::TimeTraceScope timeScope("Commit PE to disk"); | 
|  | ScopedTimer t2(ctx.outputCommitTimer); | 
|  | if (auto e = buffer->commit()) | 
|  | fatal("failed to write output '" + buffer->getPath() + | 
|  | "': " + toString(std::move(e))); | 
|  | } | 
|  |  | 
|  | static StringRef getOutputSectionName(StringRef name) { | 
|  | StringRef s = name.split('$').first; | 
|  |  | 
|  | // Treat a later period as a separator for MinGW, for sections like | 
|  | // ".ctors.01234". | 
|  | return s.substr(0, s.find('.', 1)); | 
|  | } | 
|  |  | 
|  | // For /order. | 
|  | void Writer::sortBySectionOrder(std::vector<Chunk *> &chunks) { | 
|  | auto getPriority = [&ctx = ctx](const Chunk *c) { | 
|  | if (auto *sec = dyn_cast<SectionChunk>(c)) | 
|  | if (sec->sym) | 
|  | return ctx.config.order.lookup(sec->sym->getName()); | 
|  | return 0; | 
|  | }; | 
|  |  | 
|  | llvm::stable_sort(chunks, [=](const Chunk *a, const Chunk *b) { | 
|  | return getPriority(a) < getPriority(b); | 
|  | }); | 
|  | } | 
|  |  | 
|  | // Change the characteristics of existing PartialSections that belong to the | 
|  | // section Name to Chars. | 
|  | void Writer::fixPartialSectionChars(StringRef name, uint32_t chars) { | 
|  | for (auto it : partialSections) { | 
|  | PartialSection *pSec = it.second; | 
|  | StringRef curName = pSec->name; | 
|  | if (!curName.consume_front(name) || | 
|  | (!curName.empty() && !curName.starts_with("$"))) | 
|  | continue; | 
|  | if (pSec->characteristics == chars) | 
|  | continue; | 
|  | PartialSection *destSec = createPartialSection(pSec->name, chars); | 
|  | destSec->chunks.insert(destSec->chunks.end(), pSec->chunks.begin(), | 
|  | pSec->chunks.end()); | 
|  | pSec->chunks.clear(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Sort concrete section chunks from GNU import libraries. | 
|  | // | 
|  | // GNU binutils doesn't use short import files, but instead produces import | 
|  | // libraries that consist of object files, with section chunks for the .idata$* | 
|  | // sections. These are linked just as regular static libraries. Each import | 
|  | // library consists of one header object, one object file for every imported | 
|  | // symbol, and one trailer object. In order for the .idata tables/lists to | 
|  | // be formed correctly, the section chunks within each .idata$* section need | 
|  | // to be grouped by library, and sorted alphabetically within each library | 
|  | // (which makes sure the header comes first and the trailer last). | 
|  | bool Writer::fixGnuImportChunks() { | 
|  | uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; | 
|  |  | 
|  | // Make sure all .idata$* section chunks are mapped as RDATA in order to | 
|  | // be sorted into the same sections as our own synthesized .idata chunks. | 
|  | fixPartialSectionChars(".idata", rdata); | 
|  |  | 
|  | bool hasIdata = false; | 
|  | // Sort all .idata$* chunks, grouping chunks from the same library, | 
|  | // with alphabetical ordering of the object files within a library. | 
|  | for (auto it : partialSections) { | 
|  | PartialSection *pSec = it.second; | 
|  | if (!pSec->name.starts_with(".idata")) | 
|  | continue; | 
|  |  | 
|  | if (!pSec->chunks.empty()) | 
|  | hasIdata = true; | 
|  | llvm::stable_sort(pSec->chunks, [&](Chunk *s, Chunk *t) { | 
|  | SectionChunk *sc1 = dyn_cast_or_null<SectionChunk>(s); | 
|  | SectionChunk *sc2 = dyn_cast_or_null<SectionChunk>(t); | 
|  | if (!sc1 || !sc2) { | 
|  | // if SC1, order them ascending. If SC2 or both null, | 
|  | // S is not less than T. | 
|  | return sc1 != nullptr; | 
|  | } | 
|  | // Make a string with "libraryname/objectfile" for sorting, achieving | 
|  | // both grouping by library and sorting of objects within a library, | 
|  | // at once. | 
|  | std::string key1 = | 
|  | (sc1->file->parentName + "/" + sc1->file->getName()).str(); | 
|  | std::string key2 = | 
|  | (sc2->file->parentName + "/" + sc2->file->getName()).str(); | 
|  | return key1 < key2; | 
|  | }); | 
|  | } | 
|  | return hasIdata; | 
|  | } | 
|  |  | 
|  | // Add generated idata chunks, for imported symbols and DLLs, and a | 
|  | // terminator in .idata$2. | 
|  | void Writer::addSyntheticIdata() { | 
|  | uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; | 
|  | idata.create(ctx); | 
|  |  | 
|  | // Add the .idata content in the right section groups, to allow | 
|  | // chunks from other linked in object files to be grouped together. | 
|  | // See Microsoft PE/COFF spec 5.4 for details. | 
|  | auto add = [&](StringRef n, std::vector<Chunk *> &v) { | 
|  | PartialSection *pSec = createPartialSection(n, rdata); | 
|  | pSec->chunks.insert(pSec->chunks.end(), v.begin(), v.end()); | 
|  | }; | 
|  |  | 
|  | // The loader assumes a specific order of data. | 
|  | // Add each type in the correct order. | 
|  | add(".idata$2", idata.dirs); | 
|  | add(".idata$4", idata.lookups); | 
|  | add(".idata$5", idata.addresses); | 
|  | if (!idata.hints.empty()) | 
|  | add(".idata$6", idata.hints); | 
|  | add(".idata$7", idata.dllNames); | 
|  | } | 
|  |  | 
|  | // Locate the first Chunk and size of the import directory list and the | 
|  | // IAT. | 
|  | void Writer::locateImportTables() { | 
|  | uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; | 
|  |  | 
|  | if (PartialSection *importDirs = findPartialSection(".idata$2", rdata)) { | 
|  | if (!importDirs->chunks.empty()) | 
|  | importTableStart = importDirs->chunks.front(); | 
|  | for (Chunk *c : importDirs->chunks) | 
|  | importTableSize += c->getSize(); | 
|  | } | 
|  |  | 
|  | if (PartialSection *importAddresses = findPartialSection(".idata$5", rdata)) { | 
|  | if (!importAddresses->chunks.empty()) | 
|  | iatStart = importAddresses->chunks.front(); | 
|  | for (Chunk *c : importAddresses->chunks) | 
|  | iatSize += c->getSize(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Return whether a SectionChunk's suffix (the dollar and any trailing | 
|  | // suffix) should be removed and sorted into the main suffixless | 
|  | // PartialSection. | 
|  | static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name, | 
|  | bool isMinGW) { | 
|  | // On MinGW, comdat groups are formed by putting the comdat group name | 
|  | // after the '$' in the section name. For .eh_frame$<symbol>, that must | 
|  | // still be sorted before the .eh_frame trailer from crtend.o, thus just | 
|  | // strip the section name trailer. For other sections, such as | 
|  | // .tls$$<symbol> (where non-comdat .tls symbols are otherwise stored in | 
|  | // ".tls$"), they must be strictly sorted after .tls. And for the | 
|  | // hypothetical case of comdat .CRT$XCU, we definitely need to keep the | 
|  | // suffix for sorting. Thus, to play it safe, only strip the suffix for | 
|  | // the standard sections. | 
|  | if (!isMinGW) | 
|  | return false; | 
|  | if (!sc || !sc->isCOMDAT()) | 
|  | return false; | 
|  | return name.starts_with(".text$") || name.starts_with(".data$") || | 
|  | name.starts_with(".rdata$") || name.starts_with(".pdata$") || | 
|  | name.starts_with(".xdata$") || name.starts_with(".eh_frame$"); | 
|  | } | 
|  |  | 
|  | void Writer::sortSections() { | 
|  | if (!ctx.config.callGraphProfile.empty()) { | 
|  | DenseMap<const SectionChunk *, int> order = | 
|  | computeCallGraphProfileOrder(ctx); | 
|  | for (auto it : order) { | 
|  | if (DefinedRegular *sym = it.first->sym) | 
|  | ctx.config.order[sym->getName()] = it.second; | 
|  | } | 
|  | } | 
|  | if (!ctx.config.order.empty()) | 
|  | for (auto it : partialSections) | 
|  | sortBySectionOrder(it.second->chunks); | 
|  | } | 
|  |  | 
|  | // Create output section objects and add them to OutputSections. | 
|  | void Writer::createSections() { | 
|  | llvm::TimeTraceScope timeScope("Output sections"); | 
|  | // First, create the builtin sections. | 
|  | const uint32_t data = IMAGE_SCN_CNT_INITIALIZED_DATA; | 
|  | const uint32_t bss = IMAGE_SCN_CNT_UNINITIALIZED_DATA; | 
|  | const uint32_t code = IMAGE_SCN_CNT_CODE; | 
|  | const uint32_t discardable = IMAGE_SCN_MEM_DISCARDABLE; | 
|  | const uint32_t r = IMAGE_SCN_MEM_READ; | 
|  | const uint32_t w = IMAGE_SCN_MEM_WRITE; | 
|  | const uint32_t x = IMAGE_SCN_MEM_EXECUTE; | 
|  |  | 
|  | SmallDenseMap<std::pair<StringRef, uint32_t>, OutputSection *> sections; | 
|  | auto createSection = [&](StringRef name, uint32_t outChars) { | 
|  | OutputSection *&sec = sections[{name, outChars}]; | 
|  | if (!sec) { | 
|  | sec = make<OutputSection>(name, outChars); | 
|  | ctx.outputSections.push_back(sec); | 
|  | } | 
|  | return sec; | 
|  | }; | 
|  |  | 
|  | // Try to match the section order used by link.exe. | 
|  | textSec = createSection(".text", code | r | x); | 
|  | createSection(".bss", bss | r | w); | 
|  | rdataSec = createSection(".rdata", data | r); | 
|  | buildidSec = createSection(".buildid", data | r); | 
|  | dataSec = createSection(".data", data | r | w); | 
|  | pdataSec = createSection(".pdata", data | r); | 
|  | idataSec = createSection(".idata", data | r); | 
|  | edataSec = createSection(".edata", data | r); | 
|  | didatSec = createSection(".didat", data | r); | 
|  | rsrcSec = createSection(".rsrc", data | r); | 
|  | relocSec = createSection(".reloc", data | discardable | r); | 
|  | ctorsSec = createSection(".ctors", data | r | w); | 
|  | dtorsSec = createSection(".dtors", data | r | w); | 
|  |  | 
|  | // Then bin chunks by name and output characteristics. | 
|  | for (Chunk *c : ctx.symtab.getChunks()) { | 
|  | auto *sc = dyn_cast<SectionChunk>(c); | 
|  | if (sc && !sc->live) { | 
|  | if (ctx.config.verbose) | 
|  | sc->printDiscardedMessage(); | 
|  | continue; | 
|  | } | 
|  | StringRef name = c->getSectionName(); | 
|  | if (shouldStripSectionSuffix(sc, name, ctx.config.mingw)) | 
|  | name = name.split('$').first; | 
|  |  | 
|  | if (name.starts_with(".tls")) | 
|  | tlsAlignment = std::max(tlsAlignment, c->getAlignment()); | 
|  |  | 
|  | PartialSection *pSec = createPartialSection(name, | 
|  | c->getOutputCharacteristics()); | 
|  | pSec->chunks.push_back(c); | 
|  | } | 
|  |  | 
|  | fixPartialSectionChars(".rsrc", data | r); | 
|  | fixPartialSectionChars(".edata", data | r); | 
|  | // Even in non MinGW cases, we might need to link against GNU import | 
|  | // libraries. | 
|  | bool hasIdata = fixGnuImportChunks(); | 
|  | if (!idata.empty()) | 
|  | hasIdata = true; | 
|  |  | 
|  | if (hasIdata) | 
|  | addSyntheticIdata(); | 
|  |  | 
|  | sortSections(); | 
|  |  | 
|  | if (hasIdata) | 
|  | locateImportTables(); | 
|  |  | 
|  | // 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. | 
|  | for (auto it : partialSections) { | 
|  | PartialSection *pSec = it.second; | 
|  | StringRef name = getOutputSectionName(pSec->name); | 
|  | uint32_t outChars = pSec->characteristics; | 
|  |  | 
|  | if (name == ".CRT") { | 
|  | // In link.exe, there is a special case for the I386 target where .CRT | 
|  | // sections are treated as if they have output characteristics DATA | R if | 
|  | // their characteristics are DATA | R | W. This implements the same | 
|  | // special case for all architectures. | 
|  | outChars = data | r; | 
|  |  | 
|  | log("Processing section " + pSec->name + " -> " + name); | 
|  |  | 
|  | sortCRTSectionChunks(pSec->chunks); | 
|  | } | 
|  |  | 
|  | OutputSection *sec = createSection(name, outChars); | 
|  | for (Chunk *c : pSec->chunks) | 
|  | sec->addChunk(c); | 
|  |  | 
|  | sec->addContributingPartialSection(pSec); | 
|  | } | 
|  |  | 
|  | // Finally, move some output sections to the end. | 
|  | auto sectionOrder = [&](const OutputSection *s) { | 
|  | // Move DISCARDABLE (or non-memory-mapped) sections to the end of file | 
|  | // because the loader cannot handle holes. Stripping can remove other | 
|  | // discardable ones than .reloc, which is first of them (created early). | 
|  | if (s->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) { | 
|  | // Move discardable sections named .debug_ to the end, after other | 
|  | // discardable sections. Stripping only removes the sections named | 
|  | // .debug_* - thus try to avoid leaving holes after stripping. | 
|  | if (s->name.starts_with(".debug_")) | 
|  | return 3; | 
|  | return 2; | 
|  | } | 
|  | // .rsrc should come at the end of the non-discardable sections because its | 
|  | // size may change by the Win32 UpdateResources() function, causing | 
|  | // subsequent sections to move (see https://crbug.com/827082). | 
|  | if (s == rsrcSec) | 
|  | return 1; | 
|  | return 0; | 
|  | }; | 
|  | llvm::stable_sort(ctx.outputSections, | 
|  | [&](const OutputSection *s, const OutputSection *t) { | 
|  | return sectionOrder(s) < sectionOrder(t); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void Writer::createMiscChunks() { | 
|  | llvm::TimeTraceScope timeScope("Misc chunks"); | 
|  | Configuration *config = &ctx.config; | 
|  |  | 
|  | for (MergeChunk *p : ctx.mergeChunkInstances) { | 
|  | if (p) { | 
|  | p->finalizeContents(); | 
|  | rdataSec->addChunk(p); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Create thunks for locally-dllimported symbols. | 
|  | if (!ctx.symtab.localImportChunks.empty()) { | 
|  | for (Chunk *c : ctx.symtab.localImportChunks) | 
|  | rdataSec->addChunk(c); | 
|  | } | 
|  |  | 
|  | // Create Debug Information Chunks | 
|  | debugInfoSec = config->mingw ? buildidSec : rdataSec; | 
|  | if (config->buildIDHash != BuildIDHash::None || config->debug || | 
|  | config->repro || config->cetCompat) { | 
|  | debugDirectory = | 
|  | make<DebugDirectoryChunk>(ctx, debugRecords, config->repro); | 
|  | debugDirectory->setAlignment(4); | 
|  | debugInfoSec->addChunk(debugDirectory); | 
|  | } | 
|  |  | 
|  | if (config->debug || config->buildIDHash != BuildIDHash::None) { | 
|  | // Make a CVDebugRecordChunk even when /DEBUG:CV is not specified.  We | 
|  | // output a PDB no matter what, and this chunk provides the only means of | 
|  | // allowing a debugger to match a PDB and an executable.  So we need it even | 
|  | // if we're ultimately not going to write CodeView data to the PDB. | 
|  | buildId = make<CVDebugRecordChunk>(ctx); | 
|  | debugRecords.emplace_back(COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId); | 
|  | if (Symbol *buildidSym = ctx.symtab.findUnderscore("__buildid")) | 
|  | replaceSymbol<DefinedSynthetic>(buildidSym, buildidSym->getName(), | 
|  | buildId, 4); | 
|  | } | 
|  |  | 
|  | if (config->cetCompat) { | 
|  | debugRecords.emplace_back(COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, | 
|  | make<ExtendedDllCharacteristicsChunk>( | 
|  | IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT)); | 
|  | } | 
|  |  | 
|  | // Align and add each chunk referenced by the debug data directory. | 
|  | for (std::pair<COFF::DebugType, Chunk *> r : debugRecords) { | 
|  | r.second->setAlignment(4); | 
|  | debugInfoSec->addChunk(r.second); | 
|  | } | 
|  |  | 
|  | // Create SEH table. x86-only. | 
|  | if (config->safeSEH) | 
|  | createSEHTable(); | 
|  |  | 
|  | // Create /guard:cf tables if requested. | 
|  | if (config->guardCF != GuardCFLevel::Off) | 
|  | createGuardCFTables(); | 
|  |  | 
|  | if (isArm64EC(config->machine)) | 
|  | createECChunks(); | 
|  |  | 
|  | if (config->autoImport) | 
|  | createRuntimePseudoRelocs(); | 
|  |  | 
|  | if (config->mingw) | 
|  | insertCtorDtorSymbols(); | 
|  | } | 
|  |  | 
|  | // 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() { | 
|  | llvm::TimeTraceScope timeScope("Import tables"); | 
|  | // 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 : ctx.importFileInstances) { | 
|  | if (!file->live) | 
|  | continue; | 
|  |  | 
|  | std::string dll = StringRef(file->dllName).lower(); | 
|  | if (ctx.config.dllOrder.count(dll) == 0) | 
|  | ctx.config.dllOrder[dll] = ctx.config.dllOrder.size(); | 
|  |  | 
|  | if (file->impSym && !isa<DefinedImportData>(file->impSym)) | 
|  | fatal(toString(ctx, *file->impSym) + " was replaced"); | 
|  | DefinedImportData *impSym = cast_or_null<DefinedImportData>(file->impSym); | 
|  | if (ctx.config.delayLoads.count(StringRef(file->dllName).lower())) { | 
|  | if (!file->thunkSym) | 
|  | fatal("cannot delay-load " + toString(file) + | 
|  | " due to import of data: " + toString(ctx, *impSym)); | 
|  | delayIdata.add(impSym); | 
|  | } else { | 
|  | idata.add(impSym); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void Writer::appendImportThunks() { | 
|  | if (ctx.importFileInstances.empty()) | 
|  | return; | 
|  |  | 
|  | llvm::TimeTraceScope timeScope("Import thunks"); | 
|  | for (ImportFile *file : ctx.importFileInstances) { | 
|  | if (!file->live) | 
|  | continue; | 
|  |  | 
|  | if (!file->thunkSym) | 
|  | continue; | 
|  |  | 
|  | if (!isa<DefinedImportThunk>(file->thunkSym)) | 
|  | fatal(toString(ctx, *file->thunkSym) + " was replaced"); | 
|  | DefinedImportThunk *thunk = cast<DefinedImportThunk>(file->thunkSym); | 
|  | if (file->thunkLive) | 
|  | textSec->addChunk(thunk->getChunk()); | 
|  | } | 
|  |  | 
|  | if (!delayIdata.empty()) { | 
|  | Defined *helper = cast<Defined>(ctx.config.delayLoadHelper); | 
|  | delayIdata.create(helper); | 
|  | for (Chunk *c : delayIdata.getChunks()) | 
|  | didatSec->addChunk(c); | 
|  | for (Chunk *c : delayIdata.getDataChunks()) | 
|  | dataSec->addChunk(c); | 
|  | for (Chunk *c : delayIdata.getCodeChunks()) | 
|  | textSec->addChunk(c); | 
|  | for (Chunk *c : delayIdata.getCodePData()) | 
|  | pdataSec->addChunk(c); | 
|  | for (Chunk *c : delayIdata.getCodeUnwindInfo()) | 
|  | rdataSec->addChunk(c); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Writer::createExportTable() { | 
|  | llvm::TimeTraceScope timeScope("Export table"); | 
|  | if (!edataSec->chunks.empty()) { | 
|  | // Allow using a custom built export table from input object files, instead | 
|  | // of having the linker synthesize the tables. | 
|  | if (ctx.config.hadExplicitExports) | 
|  | warn("literal .edata sections override exports"); | 
|  | } else if (!ctx.config.exports.empty()) { | 
|  | for (Chunk *c : edata.chunks) | 
|  | edataSec->addChunk(c); | 
|  | } | 
|  | if (!edataSec->chunks.empty()) { | 
|  | edataStart = edataSec->chunks.front(); | 
|  | edataEnd = edataSec->chunks.back(); | 
|  | } | 
|  | // Warn on exported deleting destructor. | 
|  | for (auto e : ctx.config.exports) | 
|  | if (e.sym && e.sym->getName().starts_with("??_G")) | 
|  | warn("export of deleting dtor: " + toString(ctx, *e.sym)); | 
|  | } | 
|  |  | 
|  | void Writer::removeUnusedSections() { | 
|  | llvm::TimeTraceScope timeScope("Remove unused sections"); | 
|  | // Remove sections that we can be sure won't get content, to avoid | 
|  | // allocating space for their section headers. | 
|  | auto isUnused = [this](OutputSection *s) { | 
|  | if (s == relocSec) | 
|  | return false; // This section is populated later. | 
|  | // MergeChunks have zero size at this point, as their size is finalized | 
|  | // later. Only remove sections that have no Chunks at all. | 
|  | return s->chunks.empty(); | 
|  | }; | 
|  | llvm::erase_if(ctx.outputSections, isUnused); | 
|  | } | 
|  |  | 
|  | // The Windows loader doesn't seem to like empty sections, | 
|  | // so we remove them if any. | 
|  | void Writer::removeEmptySections() { | 
|  | llvm::TimeTraceScope timeScope("Remove empty sections"); | 
|  | auto isEmpty = [](OutputSection *s) { return s->getVirtualSize() == 0; }; | 
|  | llvm::erase_if(ctx.outputSections, isEmpty); | 
|  | } | 
|  |  | 
|  | void Writer::assignOutputSectionIndices() { | 
|  | llvm::TimeTraceScope timeScope("Output sections indices"); | 
|  | // Assign final output section indices, and assign each chunk to its output | 
|  | // section. | 
|  | uint32_t idx = 1; | 
|  | for (OutputSection *os : ctx.outputSections) { | 
|  | os->sectionIndex = idx; | 
|  | for (Chunk *c : os->chunks) | 
|  | c->setOutputSectionIdx(idx); | 
|  | ++idx; | 
|  | } | 
|  |  | 
|  | // Merge chunks are containers of chunks, so assign those an output section | 
|  | // too. | 
|  | for (MergeChunk *mc : ctx.mergeChunkInstances) | 
|  | if (mc) | 
|  | for (SectionChunk *sc : mc->sections) | 
|  | if (sc && sc->live) | 
|  | sc->setOutputSectionIdx(mc->getOutputSectionIdx()); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | std::optional<coff_symbol16> Writer::createSymbol(Defined *def) { | 
|  | coff_symbol16 sym; | 
|  | switch (def->kind()) { | 
|  | case Symbol::DefinedAbsoluteKind: { | 
|  | auto *da = dyn_cast<DefinedAbsolute>(def); | 
|  | // Note: COFF symbol can only store 32-bit values, so 64-bit absolute | 
|  | // values will be truncated. | 
|  | sym.Value = da->getVA(); | 
|  | sym.SectionNumber = IMAGE_SYM_ABSOLUTE; | 
|  | break; | 
|  | } | 
|  | default: { | 
|  | // Don't write symbols that won't be written to the output to the symbol | 
|  | // table. | 
|  | // We also try to write DefinedSynthetic as a normal symbol. Some of these | 
|  | // symbols do point to an actual chunk, like __safe_se_handler_table. Others | 
|  | // like __ImageBase are outside of sections and thus cannot be represented. | 
|  | Chunk *c = def->getChunk(); | 
|  | if (!c) | 
|  | return std::nullopt; | 
|  | OutputSection *os = ctx.getOutputSection(c); | 
|  | if (!os) | 
|  | return std::nullopt; | 
|  |  | 
|  | sym.Value = def->getRVA() - os->getRVA(); | 
|  | sym.SectionNumber = os->sectionIndex; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Symbols that are runtime pseudo relocations don't point to the actual | 
|  | // symbol data itself (as they are imported), but points to the IAT entry | 
|  | // instead. Avoid emitting them to the symbol table, as they can confuse | 
|  | // debuggers. | 
|  | if (def->isRuntimePseudoReloc) | 
|  | return std::nullopt; | 
|  |  | 
|  | 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 if (def->kind() == Symbol::DefinedImportThunkKind) { | 
|  | sym.Type = (IMAGE_SYM_DTYPE_FUNCTION << SCT_COMPLEX_TYPE_SHIFT) | | 
|  | IMAGE_SYM_TYPE_NULL; | 
|  | sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; | 
|  | } else { | 
|  | sym.Type = IMAGE_SYM_TYPE_NULL; | 
|  | sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; | 
|  | } | 
|  | sym.NumberOfAuxSymbols = 0; | 
|  | return sym; | 
|  | } | 
|  |  | 
|  | void Writer::createSymbolAndStringTable() { | 
|  | llvm::TimeTraceScope timeScope("Symbol and string table"); | 
|  | // PE/COFF images are limited to 8 byte section names. Longer names can be | 
|  | // supported by writing a non-standard string table, but this string table is | 
|  | // not mapped at runtime and the long names will therefore be inaccessible. | 
|  | // link.exe always truncates section names to 8 bytes, whereas binutils always | 
|  | // preserves long section names via the string table. LLD adopts a hybrid | 
|  | // solution where discardable sections have long names preserved and | 
|  | // non-discardable sections have their names truncated, to ensure that any | 
|  | // section which is mapped at runtime also has its name mapped at runtime. | 
|  | for (OutputSection *sec : ctx.outputSections) { | 
|  | if (sec->name.size() <= COFF::NameSize) | 
|  | continue; | 
|  | if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) | 
|  | continue; | 
|  | if (ctx.config.warnLongSectionNames) { | 
|  | warn("section name " + sec->name + | 
|  | " is longer than 8 characters and will use a non-standard string " | 
|  | "table"); | 
|  | } | 
|  | sec->setStringTableOff(addEntryToStringTable(sec->name)); | 
|  | } | 
|  |  | 
|  | if (ctx.config.writeSymtab) { | 
|  | for (ObjFile *file : ctx.objFileInstances) { | 
|  | for (Symbol *b : file->getSymbols()) { | 
|  | auto *d = dyn_cast_or_null<Defined>(b); | 
|  | if (!d || d->writtenToSymtab) | 
|  | continue; | 
|  | d->writtenToSymtab = true; | 
|  | if (auto *dc = dyn_cast_or_null<DefinedCOFF>(d)) { | 
|  | COFFSymbolRef symRef = dc->getCOFFSymbol(); | 
|  | if (symRef.isSectionDefinition() || | 
|  | symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_LABEL) | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (std::optional<coff_symbol16> sym = createSymbol(d)) | 
|  | outputSymtab.push_back(*sym); | 
|  |  | 
|  | if (auto *dthunk = dyn_cast<DefinedImportThunk>(d)) { | 
|  | if (!dthunk->wrappedSym->writtenToSymtab) { | 
|  | dthunk->wrappedSym->writtenToSymtab = true; | 
|  | if (std::optional<coff_symbol16> sym = | 
|  | createSymbol(dthunk->wrappedSym)) | 
|  | outputSymtab.push_back(*sym); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (outputSymtab.empty() && strtab.empty()) | 
|  | return; | 
|  |  | 
|  | // We position the symbol table to be adjacent to the end of the last section. | 
|  | uint64_t fileOff = fileSize; | 
|  | pointerToSymbolTable = fileOff; | 
|  | fileOff += outputSymtab.size() * sizeof(coff_symbol16); | 
|  | fileOff += 4 + strtab.size(); | 
|  | fileSize = alignTo(fileOff, ctx.config.fileAlign); | 
|  | } | 
|  |  | 
|  | void Writer::mergeSections() { | 
|  | llvm::TimeTraceScope timeScope("Merge sections"); | 
|  | if (!pdataSec->chunks.empty()) { | 
|  | if (isArm64EC(ctx.config.machine)) { | 
|  | // On ARM64EC .pdata may contain both ARM64 and X64 data. Split them by | 
|  | // sorting and store their regions separately. | 
|  | llvm::stable_sort(pdataSec->chunks, [=](const Chunk *a, const Chunk *b) { | 
|  | return (a->getMachine() == AMD64) < (b->getMachine() == AMD64); | 
|  | }); | 
|  |  | 
|  | for (auto chunk : pdataSec->chunks) { | 
|  | if (chunk->getMachine() == AMD64) { | 
|  | hybridPdata.first = chunk; | 
|  | hybridPdata.last = pdataSec->chunks.back(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (!pdata.first) | 
|  | pdata.first = chunk; | 
|  | pdata.last = chunk; | 
|  | } | 
|  | } else { | 
|  | pdata.first = pdataSec->chunks.front(); | 
|  | pdata.last = pdataSec->chunks.back(); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (auto &p : ctx.config.merge) { | 
|  | StringRef toName = p.second; | 
|  | if (p.first == toName) | 
|  | continue; | 
|  | StringSet<> names; | 
|  | while (true) { | 
|  | if (!names.insert(toName).second) | 
|  | fatal("/merge: cycle found for section '" + p.first + "'"); | 
|  | auto i = ctx.config.merge.find(toName); | 
|  | if (i == ctx.config.merge.end()) | 
|  | break; | 
|  | toName = i->second; | 
|  | } | 
|  | OutputSection *from = findSection(p.first); | 
|  | OutputSection *to = findSection(toName); | 
|  | if (!from) | 
|  | continue; | 
|  | if (!to) { | 
|  | from->name = toName; | 
|  | continue; | 
|  | } | 
|  | to->merge(from); | 
|  | } | 
|  | } | 
|  |  | 
|  | // EC targets may have chunks of various architectures mixed together at this | 
|  | // point. Group code chunks of the same architecture together by sorting chunks | 
|  | // by their EC range type. | 
|  | void Writer::sortECChunks() { | 
|  | if (!isArm64EC(ctx.config.machine)) | 
|  | return; | 
|  |  | 
|  | for (OutputSection *sec : ctx.outputSections) { | 
|  | if (sec->isCodeSection()) | 
|  | llvm::stable_sort(sec->chunks, [=](const Chunk *a, const Chunk *b) { | 
|  | std::optional<chpe_range_type> aType = a->getArm64ECRangeType(), | 
|  | bType = b->getArm64ECRangeType(); | 
|  | return bType && (!aType || *aType < *bType); | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Visits all sections to assign incremental, non-overlapping RVAs and | 
|  | // file offsets. | 
|  | void Writer::assignAddresses() { | 
|  | llvm::TimeTraceScope timeScope("Assign addresses"); | 
|  | Configuration *config = &ctx.config; | 
|  |  | 
|  | // We need to create EC code map so that ECCodeMapChunk knows its size. | 
|  | // We do it here to make sure that we account for range extension chunks. | 
|  | createECCodeMap(); | 
|  |  | 
|  | sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + | 
|  | sizeof(data_directory) * numberOfDataDirectory + | 
|  | sizeof(coff_section) * ctx.outputSections.size(); | 
|  | sizeOfHeaders += | 
|  | config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); | 
|  | sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign); | 
|  | fileSize = sizeOfHeaders; | 
|  |  | 
|  | // The first page is kept unmapped. | 
|  | uint64_t rva = alignTo(sizeOfHeaders, config->align); | 
|  |  | 
|  | for (OutputSection *sec : ctx.outputSections) { | 
|  | llvm::TimeTraceScope timeScope("Section: ", sec->name); | 
|  | if (sec == relocSec) | 
|  | addBaserels(); | 
|  | uint64_t rawSize = 0, virtualSize = 0; | 
|  | sec->header.VirtualAddress = rva; | 
|  |  | 
|  | // If /FUNCTIONPADMIN is used, functions are padded in order to create a | 
|  | // hotpatchable image. | 
|  | uint32_t padding = sec->isCodeSection() ? config->functionPadMin : 0; | 
|  | std::optional<chpe_range_type> prevECRange; | 
|  |  | 
|  | for (Chunk *c : sec->chunks) { | 
|  | // Alignment EC code range baudaries. | 
|  | if (isArm64EC(ctx.config.machine) && sec->isCodeSection()) { | 
|  | std::optional<chpe_range_type> rangeType = c->getArm64ECRangeType(); | 
|  | if (rangeType != prevECRange) { | 
|  | virtualSize = alignTo(virtualSize, 4096); | 
|  | prevECRange = rangeType; | 
|  | } | 
|  | } | 
|  | if (padding && c->isHotPatchable()) | 
|  | virtualSize += padding; | 
|  | // If chunk has EC entry thunk, reserve a space for an offset to the | 
|  | // thunk. | 
|  | if (c->getEntryThunk()) | 
|  | virtualSize += sizeof(uint32_t); | 
|  | virtualSize = alignTo(virtualSize, c->getAlignment()); | 
|  | c->setRVA(rva + virtualSize); | 
|  | virtualSize += c->getSize(); | 
|  | if (c->hasData) | 
|  | rawSize = alignTo(virtualSize, config->fileAlign); | 
|  | } | 
|  | if (virtualSize > UINT32_MAX) | 
|  | error("section larger than 4 GiB: " + sec->name); | 
|  | sec->header.VirtualSize = virtualSize; | 
|  | sec->header.SizeOfRawData = rawSize; | 
|  | if (rawSize != 0) | 
|  | sec->header.PointerToRawData = fileSize; | 
|  | rva += alignTo(virtualSize, config->align); | 
|  | fileSize += alignTo(rawSize, config->fileAlign); | 
|  | } | 
|  | sizeOfImage = alignTo(rva, config->align); | 
|  |  | 
|  | // Assign addresses to sections in MergeChunks. | 
|  | for (MergeChunk *mc : ctx.mergeChunkInstances) | 
|  | if (mc) | 
|  | mc->assignSubsectionRVAs(); | 
|  | } | 
|  |  | 
|  | template <typename PEHeaderTy> void Writer::writeHeader() { | 
|  | // Write DOS header. For backwards compatibility, the first part of a PE/COFF | 
|  | // executable consists of an MS-DOS MZ executable. If the executable is run | 
|  | // under DOS, that program gets run (usually to just print an error message). | 
|  | // When run under Windows, the loader looks at AddressOfNewExeHeader and uses | 
|  | // the PE header instead. | 
|  | Configuration *config = &ctx.config; | 
|  | uint8_t *buf = buffer->getBufferStart(); | 
|  | auto *dos = reinterpret_cast<dos_header *>(buf); | 
|  | buf += sizeof(dos_header); | 
|  | dos->Magic[0] = 'M'; | 
|  | dos->Magic[1] = 'Z'; | 
|  | dos->UsedBytesInTheLastPage = dosStubSize % 512; | 
|  | dos->FileSizeInPages = divideCeil(dosStubSize, 512); | 
|  | dos->HeaderSizeInParagraphs = sizeof(dos_header) / 16; | 
|  |  | 
|  | dos->AddressOfRelocationTable = sizeof(dos_header); | 
|  | dos->AddressOfNewExeHeader = dosStubSize; | 
|  |  | 
|  | // Write DOS program. | 
|  | memcpy(buf, dosProgram, sizeof(dosProgram)); | 
|  | buf += sizeof(dosProgram); | 
|  |  | 
|  | // 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); | 
|  | switch (config->machine) { | 
|  | case ARM64EC: | 
|  | coff->Machine = AMD64; | 
|  | break; | 
|  | case ARM64X: | 
|  | coff->Machine = ARM64; | 
|  | break; | 
|  | default: | 
|  | coff->Machine = config->machine; | 
|  | } | 
|  | coff->NumberOfSections = ctx.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->driverUponly) | 
|  | coff->Characteristics |= IMAGE_FILE_UP_SYSTEM_ONLY; | 
|  | if (!config->relocatable) | 
|  | coff->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED; | 
|  | if (config->swaprunCD) | 
|  | coff->Characteristics |= IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP; | 
|  | if (config->swaprunNet) | 
|  | coff->Characteristics |= IMAGE_FILE_NET_RUN_FROM_SWAP; | 
|  | coff->SizeOfOptionalHeader = | 
|  | sizeof(PEHeaderTy) + sizeof(data_directory) * numberOfDataDirectory; | 
|  |  | 
|  | // Write PE header | 
|  | auto *pe = reinterpret_cast<PEHeaderTy *>(buf); | 
|  | buf += sizeof(*pe); | 
|  | pe->Magic = config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32; | 
|  |  | 
|  | // If {Major,Minor}LinkerVersion is left at 0.0, then for some | 
|  | // reason signing the resulting PE file with Authenticode produces a | 
|  | // signature that fails to validate on Windows 7 (but is OK on 10). | 
|  | // Set it to 14.0, which is what VS2015 outputs, and which avoids | 
|  | // that problem. | 
|  | pe->MajorLinkerVersion = 14; | 
|  | pe->MinorLinkerVersion = 0; | 
|  |  | 
|  | pe->ImageBase = config->imageBase; | 
|  | pe->SectionAlignment = config->align; | 
|  | pe->FileAlignment = config->fileAlign; | 
|  | pe->MajorImageVersion = config->majorImageVersion; | 
|  | pe->MinorImageVersion = config->minorImageVersion; | 
|  | pe->MajorOperatingSystemVersion = config->majorOSVersion; | 
|  | pe->MinorOperatingSystemVersion = config->minorOSVersion; | 
|  | pe->MajorSubsystemVersion = config->majorSubsystemVersion; | 
|  | pe->MinorSubsystemVersion = config->minorSubsystemVersion; | 
|  | pe->Subsystem = config->subsystem; | 
|  | pe->SizeOfImage = sizeOfImage; | 
|  | pe->SizeOfHeaders = sizeOfHeaders; | 
|  | if (!config->noEntry) { | 
|  | Defined *entry = cast<Defined>(config->entry); | 
|  | 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->appContainer) | 
|  | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER; | 
|  | if (config->driverWdm) | 
|  | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_WDM_DRIVER; | 
|  | 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->guardCF != GuardCFLevel::Off) | 
|  | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF; | 
|  | if (config->integrityCheck) | 
|  | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY; | 
|  | if (setNoSEHCharacteristic || config->noSEH) | 
|  | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH; | 
|  | if (config->terminalServerAware) | 
|  | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; | 
|  | pe->NumberOfRvaAndSize = numberOfDataDirectory; | 
|  | if (textSec->getVirtualSize()) { | 
|  | pe->BaseOfCode = textSec->getRVA(); | 
|  | pe->SizeOfCode = textSec->getRawSize(); | 
|  | } | 
|  | pe->SizeOfInitializedData = getSizeOfInitializedData(); | 
|  |  | 
|  | // Write data directory | 
|  | auto *dir = reinterpret_cast<data_directory *>(buf); | 
|  | buf += sizeof(*dir) * numberOfDataDirectory; | 
|  | if (edataStart) { | 
|  | dir[EXPORT_TABLE].RelativeVirtualAddress = edataStart->getRVA(); | 
|  | dir[EXPORT_TABLE].Size = | 
|  | edataEnd->getRVA() + edataEnd->getSize() - edataStart->getRVA(); | 
|  | } | 
|  | if (importTableStart) { | 
|  | dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA(); | 
|  | dir[IMPORT_TABLE].Size = importTableSize; | 
|  | } | 
|  | if (iatStart) { | 
|  | dir[IAT].RelativeVirtualAddress = iatStart->getRVA(); | 
|  | dir[IAT].Size = iatSize; | 
|  | } | 
|  | if (rsrcSec->getVirtualSize()) { | 
|  | dir[RESOURCE_TABLE].RelativeVirtualAddress = rsrcSec->getRVA(); | 
|  | dir[RESOURCE_TABLE].Size = rsrcSec->getVirtualSize(); | 
|  | } | 
|  | // ARM64EC (but not ARM64X) contains x86_64 exception table in data directory. | 
|  | ChunkRange &exceptionTable = | 
|  | ctx.config.machine == ARM64EC ? hybridPdata : pdata; | 
|  | if (exceptionTable.first) { | 
|  | dir[EXCEPTION_TABLE].RelativeVirtualAddress = | 
|  | exceptionTable.first->getRVA(); | 
|  | dir[EXCEPTION_TABLE].Size = exceptionTable.last->getRVA() + | 
|  | exceptionTable.last->getSize() - | 
|  | exceptionTable.first->getRVA(); | 
|  | } | 
|  | if (relocSec->getVirtualSize()) { | 
|  | dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA(); | 
|  | dir[BASE_RELOCATION_TABLE].Size = relocSec->getVirtualSize(); | 
|  | } | 
|  | if (Symbol *sym = ctx.symtab.findUnderscore("_tls_used")) { | 
|  | if (Defined *b = dyn_cast<Defined>(sym)) { | 
|  | dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA(); | 
|  | dir[TLS_TABLE].Size = config->is64() | 
|  | ? sizeof(object::coff_tls_directory64) | 
|  | : sizeof(object::coff_tls_directory32); | 
|  | } | 
|  | } | 
|  | if (debugDirectory) { | 
|  | dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA(); | 
|  | dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize(); | 
|  | } | 
|  | if (Symbol *sym = ctx.symtab.findUnderscore("_load_config_used")) { | 
|  | if (auto *b = dyn_cast<DefinedRegular>(sym)) { | 
|  | SectionChunk *sc = b->getChunk(); | 
|  | assert(b->getRVA() >= sc->getRVA()); | 
|  | uint64_t offsetInChunk = b->getRVA() - sc->getRVA(); | 
|  | if (!sc->hasData || offsetInChunk + 4 > sc->getSize()) | 
|  | fatal("_load_config_used is malformed"); | 
|  |  | 
|  | ArrayRef<uint8_t> secContents = sc->getContents(); | 
|  | uint32_t loadConfigSize = | 
|  | *reinterpret_cast<const ulittle32_t *>(&secContents[offsetInChunk]); | 
|  | if (offsetInChunk + loadConfigSize > sc->getSize()) | 
|  | fatal("_load_config_used is too large"); | 
|  | dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = b->getRVA(); | 
|  | dir[LOAD_CONFIG_TABLE].Size = loadConfigSize; | 
|  | } | 
|  | } | 
|  | if (!delayIdata.empty()) { | 
|  | dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress = | 
|  | delayIdata.getDirRVA(); | 
|  | dir[DELAY_IMPORT_DESCRIPTOR].Size = delayIdata.getDirSize(); | 
|  | } | 
|  |  | 
|  | // Write section table | 
|  | for (OutputSection *sec : ctx.outputSections) { | 
|  | sec->writeHeaderTo(buf, config->debug); | 
|  | buf += sizeof(coff_section); | 
|  | } | 
|  | sectionTable = ArrayRef<uint8_t>( | 
|  | buf - ctx.outputSections.size() * sizeof(coff_section), buf); | 
|  |  | 
|  | if (outputSymtab.empty() && strtab.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); | 
|  | if (!strtab.empty()) | 
|  | memcpy(buf + 4, strtab.data(), strtab.size()); | 
|  | } | 
|  |  | 
|  | void Writer::openFile(StringRef path) { | 
|  | buffer = CHECK( | 
|  | FileOutputBuffer::create(path, fileSize, FileOutputBuffer::F_executable), | 
|  | "failed to open " + path); | 
|  | } | 
|  |  | 
|  | void Writer::createSEHTable() { | 
|  | SymbolRVASet handlers; | 
|  | for (ObjFile *file : ctx.objFileInstances) { | 
|  | if (!file->hasSafeSEH()) | 
|  | error("/safeseh: " + file->getName() + " is not compatible with SEH"); | 
|  | markSymbolsForRVATable(file, file->getSXDataChunks(), handlers); | 
|  | } | 
|  |  | 
|  | // Set the "no SEH" characteristic if there really were no handlers, or if | 
|  | // there is no load config object to point to the table of handlers. | 
|  | setNoSEHCharacteristic = | 
|  | handlers.empty() || !ctx.symtab.findUnderscore("_load_config_used"); | 
|  |  | 
|  | maybeAddRVATable(std::move(handlers), "__safe_se_handler_table", | 
|  | "__safe_se_handler_count"); | 
|  | } | 
|  |  | 
|  | // Add a symbol to an RVA set. Two symbols may have the same RVA, but an RVA set | 
|  | // cannot contain duplicates. Therefore, the set is uniqued by Chunk and the | 
|  | // symbol's offset into that Chunk. | 
|  | static void addSymbolToRVASet(SymbolRVASet &rvaSet, Defined *s) { | 
|  | Chunk *c = s->getChunk(); | 
|  | if (!c) | 
|  | return; | 
|  | if (auto *sc = dyn_cast<SectionChunk>(c)) | 
|  | c = sc->repl; // Look through ICF replacement. | 
|  | uint32_t off = s->getRVA() - (c ? c->getRVA() : 0); | 
|  | rvaSet.insert({c, off}); | 
|  | } | 
|  |  | 
|  | // Given a symbol, add it to the GFIDs table if it is a live, defined, function | 
|  | // symbol in an executable section. | 
|  | static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms, | 
|  | Symbol *s) { | 
|  | if (!s) | 
|  | return; | 
|  |  | 
|  | switch (s->kind()) { | 
|  | case Symbol::DefinedLocalImportKind: | 
|  | case Symbol::DefinedImportDataKind: | 
|  | // Defines an __imp_ pointer, so it is data, so it is ignored. | 
|  | break; | 
|  | case Symbol::DefinedCommonKind: | 
|  | // Common is always data, so it is ignored. | 
|  | break; | 
|  | case Symbol::DefinedAbsoluteKind: | 
|  | case Symbol::DefinedSyntheticKind: | 
|  | // Absolute is never code, synthetic generally isn't and usually isn't | 
|  | // determinable. | 
|  | break; | 
|  | case Symbol::LazyArchiveKind: | 
|  | case Symbol::LazyObjectKind: | 
|  | case Symbol::LazyDLLSymbolKind: | 
|  | case Symbol::UndefinedKind: | 
|  | // Undefined symbols resolve to zero, so they don't have an RVA. Lazy | 
|  | // symbols shouldn't have relocations. | 
|  | break; | 
|  |  | 
|  | case Symbol::DefinedImportThunkKind: | 
|  | // Thunks are always code, include them. | 
|  | addSymbolToRVASet(addressTakenSyms, cast<Defined>(s)); | 
|  | break; | 
|  |  | 
|  | case Symbol::DefinedRegularKind: { | 
|  | // This is a regular, defined, symbol from a COFF file. Mark the symbol as | 
|  | // address taken if the symbol type is function and it's in an executable | 
|  | // section. | 
|  | auto *d = cast<DefinedRegular>(s); | 
|  | if (d->getCOFFSymbol().getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION) { | 
|  | SectionChunk *sc = dyn_cast<SectionChunk>(d->getChunk()); | 
|  | if (sc && sc->live && | 
|  | sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) | 
|  | addSymbolToRVASet(addressTakenSyms, d); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Visit all relocations from all section contributions of this object file and | 
|  | // mark the relocation target as address-taken. | 
|  | void Writer::markSymbolsWithRelocations(ObjFile *file, | 
|  | SymbolRVASet &usedSymbols) { | 
|  | for (Chunk *c : file->getChunks()) { | 
|  | // We only care about live section chunks. Common chunks and other chunks | 
|  | // don't generally contain relocations. | 
|  | SectionChunk *sc = dyn_cast<SectionChunk>(c); | 
|  | if (!sc || !sc->live) | 
|  | continue; | 
|  |  | 
|  | for (const coff_relocation &reloc : sc->getRelocs()) { | 
|  | if (ctx.config.machine == I386 && | 
|  | reloc.Type == COFF::IMAGE_REL_I386_REL32) | 
|  | // Ignore relative relocations on x86. On x86_64 they can't be ignored | 
|  | // since they're also used to compute absolute addresses. | 
|  | continue; | 
|  |  | 
|  | Symbol *ref = sc->file->getSymbol(reloc.SymbolTableIndex); | 
|  | maybeAddAddressTakenFunction(usedSymbols, ref); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Create the guard function id table. This is a table of RVAs of all | 
|  | // address-taken functions. It is sorted and uniqued, just like the safe SEH | 
|  | // table. | 
|  | void Writer::createGuardCFTables() { | 
|  | Configuration *config = &ctx.config; | 
|  |  | 
|  | SymbolRVASet addressTakenSyms; | 
|  | SymbolRVASet giatsRVASet; | 
|  | std::vector<Symbol *> giatsSymbols; | 
|  | SymbolRVASet longJmpTargets; | 
|  | SymbolRVASet ehContTargets; | 
|  | for (ObjFile *file : ctx.objFileInstances) { | 
|  | // If the object was compiled with /guard:cf, the address taken symbols | 
|  | // are in .gfids$y sections, and the longjmp targets are in .gljmp$y | 
|  | // sections. If the object was not compiled with /guard:cf, we assume there | 
|  | // were no setjmp targets, and that all code symbols with relocations are | 
|  | // possibly address-taken. | 
|  | if (file->hasGuardCF()) { | 
|  | markSymbolsForRVATable(file, file->getGuardFidChunks(), addressTakenSyms); | 
|  | markSymbolsForRVATable(file, file->getGuardIATChunks(), giatsRVASet); | 
|  | getSymbolsFromSections(file, file->getGuardIATChunks(), giatsSymbols); | 
|  | markSymbolsForRVATable(file, file->getGuardLJmpChunks(), longJmpTargets); | 
|  | } else { | 
|  | markSymbolsWithRelocations(file, addressTakenSyms); | 
|  | } | 
|  | // If the object was compiled with /guard:ehcont, the ehcont targets are in | 
|  | // .gehcont$y sections. | 
|  | if (file->hasGuardEHCont()) | 
|  | markSymbolsForRVATable(file, file->getGuardEHContChunks(), ehContTargets); | 
|  | } | 
|  |  | 
|  | // Mark the image entry as address-taken. | 
|  | if (config->entry) | 
|  | maybeAddAddressTakenFunction(addressTakenSyms, config->entry); | 
|  |  | 
|  | // Mark exported symbols in executable sections as address-taken. | 
|  | for (Export &e : config->exports) | 
|  | maybeAddAddressTakenFunction(addressTakenSyms, e.sym); | 
|  |  | 
|  | // For each entry in the .giats table, check if it has a corresponding load | 
|  | // thunk (e.g. because the DLL that defines it will be delay-loaded) and, if | 
|  | // so, add the load thunk to the address taken (.gfids) table. | 
|  | for (Symbol *s : giatsSymbols) { | 
|  | if (auto *di = dyn_cast<DefinedImportData>(s)) { | 
|  | if (di->loadThunkSym) | 
|  | addSymbolToRVASet(addressTakenSyms, di->loadThunkSym); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Ensure sections referenced in the gfid table are 16-byte aligned. | 
|  | for (const ChunkAndOffset &c : addressTakenSyms) | 
|  | if (c.inputChunk->getAlignment() < 16) | 
|  | c.inputChunk->setAlignment(16); | 
|  |  | 
|  | maybeAddRVATable(std::move(addressTakenSyms), "__guard_fids_table", | 
|  | "__guard_fids_count"); | 
|  |  | 
|  | // Add the Guard Address Taken IAT Entry Table (.giats). | 
|  | maybeAddRVATable(std::move(giatsRVASet), "__guard_iat_table", | 
|  | "__guard_iat_count"); | 
|  |  | 
|  | // Add the longjmp target table unless the user told us not to. | 
|  | if (config->guardCF & GuardCFLevel::LongJmp) | 
|  | maybeAddRVATable(std::move(longJmpTargets), "__guard_longjmp_table", | 
|  | "__guard_longjmp_count"); | 
|  |  | 
|  | // Add the ehcont target table unless the user told us not to. | 
|  | if (config->guardCF & GuardCFLevel::EHCont) | 
|  | maybeAddRVATable(std::move(ehContTargets), "__guard_eh_cont_table", | 
|  | "__guard_eh_cont_count"); | 
|  |  | 
|  | // Set __guard_flags, which will be used in the load config to indicate that | 
|  | // /guard:cf was enabled. | 
|  | uint32_t guardFlags = uint32_t(GuardFlags::CF_INSTRUMENTED) | | 
|  | uint32_t(GuardFlags::CF_FUNCTION_TABLE_PRESENT); | 
|  | if (config->guardCF & GuardCFLevel::LongJmp) | 
|  | guardFlags |= uint32_t(GuardFlags::CF_LONGJUMP_TABLE_PRESENT); | 
|  | if (config->guardCF & GuardCFLevel::EHCont) | 
|  | guardFlags |= uint32_t(GuardFlags::EH_CONTINUATION_TABLE_PRESENT); | 
|  | Symbol *flagSym = ctx.symtab.findUnderscore("__guard_flags"); | 
|  | cast<DefinedAbsolute>(flagSym)->setVA(guardFlags); | 
|  | } | 
|  |  | 
|  | // Take a list of input sections containing symbol table indices and add those | 
|  | // symbols to a vector. The challenge is that symbol RVAs are not known and | 
|  | // depend on the table size, so we can't directly build a set of integers. | 
|  | void Writer::getSymbolsFromSections(ObjFile *file, | 
|  | ArrayRef<SectionChunk *> symIdxChunks, | 
|  | std::vector<Symbol *> &symbols) { | 
|  | for (SectionChunk *c : symIdxChunks) { | 
|  | // Skip sections discarded by linker GC. This comes up when a .gfids section | 
|  | // is associated with something like a vtable and the vtable is discarded. | 
|  | // In this case, the associated gfids section is discarded, and we don't | 
|  | // mark the virtual member functions as address-taken by the vtable. | 
|  | if (!c->live) | 
|  | continue; | 
|  |  | 
|  | // Validate that the contents look like symbol table indices. | 
|  | ArrayRef<uint8_t> data = c->getContents(); | 
|  | if (data.size() % 4 != 0) { | 
|  | warn("ignoring " + c->getSectionName() + | 
|  | " symbol table index section in object " + toString(file)); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Read each symbol table index and check if that symbol was included in the | 
|  | // final link. If so, add it to the vector of symbols. | 
|  | ArrayRef<ulittle32_t> symIndices( | 
|  | reinterpret_cast<const ulittle32_t *>(data.data()), data.size() / 4); | 
|  | ArrayRef<Symbol *> objSymbols = file->getSymbols(); | 
|  | for (uint32_t symIndex : symIndices) { | 
|  | if (symIndex >= objSymbols.size()) { | 
|  | warn("ignoring invalid symbol table index in section " + | 
|  | c->getSectionName() + " in object " + toString(file)); | 
|  | continue; | 
|  | } | 
|  | if (Symbol *s = objSymbols[symIndex]) { | 
|  | if (s->isLive()) | 
|  | symbols.push_back(cast<Symbol>(s)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Take a list of input sections containing symbol table indices and add those | 
|  | // symbols to an RVA table. | 
|  | void Writer::markSymbolsForRVATable(ObjFile *file, | 
|  | ArrayRef<SectionChunk *> symIdxChunks, | 
|  | SymbolRVASet &tableSymbols) { | 
|  | std::vector<Symbol *> syms; | 
|  | getSymbolsFromSections(file, symIdxChunks, syms); | 
|  |  | 
|  | for (Symbol *s : syms) | 
|  | addSymbolToRVASet(tableSymbols, cast<Defined>(s)); | 
|  | } | 
|  |  | 
|  | // Replace the absolute table symbol with a synthetic symbol pointing to | 
|  | // tableChunk so that we can emit base relocations for it and resolve section | 
|  | // relative relocations. | 
|  | void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, | 
|  | StringRef countSym, bool hasFlag) { | 
|  | if (tableSymbols.empty()) | 
|  | return; | 
|  |  | 
|  | NonSectionChunk *tableChunk; | 
|  | if (hasFlag) | 
|  | tableChunk = make<RVAFlagTableChunk>(std::move(tableSymbols)); | 
|  | else | 
|  | tableChunk = make<RVATableChunk>(std::move(tableSymbols)); | 
|  | rdataSec->addChunk(tableChunk); | 
|  |  | 
|  | Symbol *t = ctx.symtab.findUnderscore(tableSym); | 
|  | Symbol *c = ctx.symtab.findUnderscore(countSym); | 
|  | replaceSymbol<DefinedSynthetic>(t, t->getName(), tableChunk); | 
|  | cast<DefinedAbsolute>(c)->setVA(tableChunk->getSize() / (hasFlag ? 5 : 4)); | 
|  | } | 
|  |  | 
|  | // Create CHPE metadata chunks. | 
|  | void Writer::createECChunks() { | 
|  | auto codeMapChunk = make<ECCodeMapChunk>(codeMap); | 
|  | rdataSec->addChunk(codeMapChunk); | 
|  | Symbol *codeMapSym = ctx.symtab.findUnderscore("__hybrid_code_map"); | 
|  | replaceSymbol<DefinedSynthetic>(codeMapSym, codeMapSym->getName(), | 
|  | codeMapChunk); | 
|  | } | 
|  |  | 
|  | // MinGW specific. Gather all relocations that are imported from a DLL even | 
|  | // though the code didn't expect it to, produce the table that the runtime | 
|  | // uses for fixing them up, and provide the synthetic symbols that the | 
|  | // runtime uses for finding the table. | 
|  | void Writer::createRuntimePseudoRelocs() { | 
|  | std::vector<RuntimePseudoReloc> rels; | 
|  |  | 
|  | for (Chunk *c : ctx.symtab.getChunks()) { | 
|  | auto *sc = dyn_cast<SectionChunk>(c); | 
|  | if (!sc || !sc->live) | 
|  | continue; | 
|  | // Don't create pseudo relocations for sections that won't be | 
|  | // mapped at runtime. | 
|  | if (sc->header->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) | 
|  | continue; | 
|  | sc->getRuntimePseudoRelocs(rels); | 
|  | } | 
|  |  | 
|  | if (!ctx.config.pseudoRelocs) { | 
|  | // Not writing any pseudo relocs; if some were needed, error out and | 
|  | // indicate what required them. | 
|  | for (const RuntimePseudoReloc &rpr : rels) | 
|  | error("automatic dllimport of " + rpr.sym->getName() + " in " + | 
|  | toString(rpr.target->file) + " requires pseudo relocations"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!rels.empty()) { | 
|  | log("Writing " + Twine(rels.size()) + " runtime pseudo relocations"); | 
|  | const char *symbolName = "_pei386_runtime_relocator"; | 
|  | Symbol *relocator = ctx.symtab.findUnderscore(symbolName); | 
|  | if (!relocator) | 
|  | error("output image has runtime pseudo relocations, but the function " + | 
|  | Twine(symbolName) + | 
|  | " is missing; it is needed for fixing the relocations at runtime"); | 
|  | } | 
|  |  | 
|  | PseudoRelocTableChunk *table = make<PseudoRelocTableChunk>(rels); | 
|  | rdataSec->addChunk(table); | 
|  | EmptyChunk *endOfList = make<EmptyChunk>(); | 
|  | rdataSec->addChunk(endOfList); | 
|  |  | 
|  | Symbol *headSym = ctx.symtab.findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__"); | 
|  | Symbol *endSym = | 
|  | ctx.symtab.findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__"); | 
|  | replaceSymbol<DefinedSynthetic>(headSym, headSym->getName(), table); | 
|  | replaceSymbol<DefinedSynthetic>(endSym, endSym->getName(), endOfList); | 
|  | } | 
|  |  | 
|  | // MinGW specific. | 
|  | // The MinGW .ctors and .dtors lists have sentinels at each end; | 
|  | // a (uintptr_t)-1 at the start and a (uintptr_t)0 at the end. | 
|  | // There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__ | 
|  | // and __DTOR_LIST__ respectively. | 
|  | void Writer::insertCtorDtorSymbols() { | 
|  | AbsolutePointerChunk *ctorListHead = make<AbsolutePointerChunk>(ctx, -1); | 
|  | AbsolutePointerChunk *ctorListEnd = make<AbsolutePointerChunk>(ctx, 0); | 
|  | AbsolutePointerChunk *dtorListHead = make<AbsolutePointerChunk>(ctx, -1); | 
|  | AbsolutePointerChunk *dtorListEnd = make<AbsolutePointerChunk>(ctx, 0); | 
|  | ctorsSec->insertChunkAtStart(ctorListHead); | 
|  | ctorsSec->addChunk(ctorListEnd); | 
|  | dtorsSec->insertChunkAtStart(dtorListHead); | 
|  | dtorsSec->addChunk(dtorListEnd); | 
|  |  | 
|  | Symbol *ctorListSym = ctx.symtab.findUnderscore("__CTOR_LIST__"); | 
|  | Symbol *dtorListSym = ctx.symtab.findUnderscore("__DTOR_LIST__"); | 
|  | replaceSymbol<DefinedSynthetic>(ctorListSym, ctorListSym->getName(), | 
|  | ctorListHead); | 
|  | replaceSymbol<DefinedSynthetic>(dtorListSym, dtorListSym->getName(), | 
|  | dtorListHead); | 
|  | } | 
|  |  | 
|  | // Handles /section options to allow users to overwrite | 
|  | // section attributes. | 
|  | void Writer::setSectionPermissions() { | 
|  | llvm::TimeTraceScope timeScope("Sections permissions"); | 
|  | for (auto &p : ctx.config.section) { | 
|  | StringRef name = p.first; | 
|  | uint32_t perm = p.second; | 
|  | for (OutputSection *sec : ctx.outputSections) | 
|  | if (sec->name == name) | 
|  | sec->setPermissions(perm); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Set symbols used by ARM64EC metadata. | 
|  | void Writer::setECSymbols() { | 
|  | if (!isArm64EC(ctx.config.machine)) | 
|  | return; | 
|  |  | 
|  | Symbol *rfeTableSym = ctx.symtab.findUnderscore("__arm64x_extra_rfe_table"); | 
|  | replaceSymbol<DefinedSynthetic>(rfeTableSym, "__arm64x_extra_rfe_table", | 
|  | pdata.first); | 
|  |  | 
|  | if (pdata.first) { | 
|  | Symbol *rfeSizeSym = | 
|  | ctx.symtab.findUnderscore("__arm64x_extra_rfe_table_size"); | 
|  | cast<DefinedAbsolute>(rfeSizeSym) | 
|  | ->setVA(pdata.last->getRVA() + pdata.last->getSize() - | 
|  | pdata.first->getRVA()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Write section contents to a mmap'ed file. | 
|  | void Writer::writeSections() { | 
|  | llvm::TimeTraceScope timeScope("Write sections"); | 
|  | uint8_t *buf = buffer->getBufferStart(); | 
|  | for (OutputSection *sec : ctx.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). Only fill the gaps between chunks. Most | 
|  | // chunks overwrite it anyway, but uninitialized data chunks | 
|  | // merged into a code section don't. | 
|  | if ((sec->header.Characteristics & IMAGE_SCN_CNT_CODE) && | 
|  | (ctx.config.machine == AMD64 || ctx.config.machine == I386)) { | 
|  | uint32_t prevEnd = 0; | 
|  | for (Chunk *c : sec->chunks) { | 
|  | uint32_t off = c->getRVA() - sec->getRVA(); | 
|  | memset(secBuf + prevEnd, 0xCC, off - prevEnd); | 
|  | prevEnd = off + c->getSize(); | 
|  | } | 
|  | memset(secBuf + prevEnd, 0xCC, sec->getRawSize() - prevEnd); | 
|  | } | 
|  |  | 
|  | parallelForEach(sec->chunks, [&](Chunk *c) { | 
|  | c->writeTo(secBuf + c->getRVA() - sec->getRVA()); | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Writer::writeBuildId() { | 
|  | llvm::TimeTraceScope timeScope("Write build ID"); | 
|  |  | 
|  | // There are two important parts to the build ID. | 
|  | // 1) If building with debug info, the COFF debug directory contains a | 
|  | //    timestamp as well as a Guid and Age of the PDB. | 
|  | // 2) In all cases, the PE COFF file header also contains a timestamp. | 
|  | // For reproducibility, instead of a timestamp we want to use a hash of the | 
|  | // PE contents. | 
|  | Configuration *config = &ctx.config; | 
|  | bool generateSyntheticBuildId = config->buildIDHash == BuildIDHash::Binary; | 
|  | if (generateSyntheticBuildId) { | 
|  | assert(buildId && "BuildId is not set!"); | 
|  | // BuildId->BuildId was filled in when the PDB was written. | 
|  | } | 
|  |  | 
|  | // At this point the only fields in the COFF file which remain unset are the | 
|  | // "timestamp" in the COFF file header, and the ones in the coff debug | 
|  | // directory.  Now we can hash the file and write that hash to the various | 
|  | // timestamp fields in the file. | 
|  | StringRef outputFileData( | 
|  | reinterpret_cast<const char *>(buffer->getBufferStart()), | 
|  | buffer->getBufferSize()); | 
|  |  | 
|  | uint32_t timestamp = config->timestamp; | 
|  | uint64_t hash = 0; | 
|  |  | 
|  | if (config->repro || generateSyntheticBuildId) | 
|  | hash = xxh3_64bits(outputFileData); | 
|  |  | 
|  | if (config->repro) | 
|  | timestamp = static_cast<uint32_t>(hash); | 
|  |  | 
|  | if (generateSyntheticBuildId) { | 
|  | buildId->buildId->PDB70.CVSignature = OMF::Signature::PDB70; | 
|  | buildId->buildId->PDB70.Age = 1; | 
|  | memcpy(buildId->buildId->PDB70.Signature, &hash, 8); | 
|  | // xxhash only gives us 8 bytes, so put some fixed data in the other half. | 
|  | memcpy(&buildId->buildId->PDB70.Signature[8], "LLD PDB.", 8); | 
|  | } | 
|  |  | 
|  | if (debugDirectory) | 
|  | debugDirectory->setTimeDateStamp(timestamp); | 
|  |  | 
|  | uint8_t *buf = buffer->getBufferStart(); | 
|  | buf += dosStubSize + sizeof(PEMagic); | 
|  | object::coff_file_header *coffHeader = | 
|  | reinterpret_cast<coff_file_header *>(buf); | 
|  | coffHeader->TimeDateStamp = timestamp; | 
|  | } | 
|  |  | 
|  | // Sort .pdata section contents according to PE/COFF spec 5.5. | 
|  | template <typename T> | 
|  | void Writer::sortExceptionTable(ChunkRange &exceptionTable) { | 
|  | if (!exceptionTable.first) | 
|  | return; | 
|  |  | 
|  | // We assume .pdata contains function table entries only. | 
|  | auto bufAddr = [&](Chunk *c) { | 
|  | OutputSection *os = ctx.getOutputSection(c); | 
|  | return buffer->getBufferStart() + os->getFileOff() + c->getRVA() - | 
|  | os->getRVA(); | 
|  | }; | 
|  | uint8_t *begin = bufAddr(exceptionTable.first); | 
|  | uint8_t *end = bufAddr(exceptionTable.last) + exceptionTable.last->getSize(); | 
|  | if ((end - begin) % sizeof(T) != 0) { | 
|  | fatal("unexpected .pdata size: " + Twine(end - begin) + | 
|  | " is not a multiple of " + Twine(sizeof(T))); | 
|  | } | 
|  |  | 
|  | parallelSort(MutableArrayRef<T>(reinterpret_cast<T *>(begin), | 
|  | reinterpret_cast<T *>(end)), | 
|  | [](const T &a, const T &b) { return a.begin < b.begin; }); | 
|  | } | 
|  |  | 
|  | // Sort .pdata section contents according to PE/COFF spec 5.5. | 
|  | void Writer::sortExceptionTables() { | 
|  | llvm::TimeTraceScope timeScope("Sort exception table"); | 
|  |  | 
|  | struct EntryX64 { | 
|  | ulittle32_t begin, end, unwind; | 
|  | }; | 
|  | struct EntryArm { | 
|  | ulittle32_t begin, unwind; | 
|  | }; | 
|  |  | 
|  | switch (ctx.config.machine) { | 
|  | case AMD64: | 
|  | sortExceptionTable<EntryX64>(pdata); | 
|  | break; | 
|  | case ARM64EC: | 
|  | case ARM64X: | 
|  | sortExceptionTable<EntryX64>(hybridPdata); | 
|  | [[fallthrough]]; | 
|  | case ARMNT: | 
|  | case ARM64: | 
|  | sortExceptionTable<EntryArm>(pdata); | 
|  | break; | 
|  | default: | 
|  | if (pdata.first) | 
|  | lld::errs() << "warning: don't know how to handle .pdata.\n"; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // The CRT section contains, among other things, the array of function | 
|  | // pointers that initialize every global variable that is not trivially | 
|  | // constructed. The CRT calls them one after the other prior to invoking | 
|  | // main(). | 
|  | // | 
|  | // As per C++ spec, 3.6.2/2.3, | 
|  | // "Variables with ordered initialization defined within a single | 
|  | // translation unit shall be initialized in the order of their definitions | 
|  | // in the translation unit" | 
|  | // | 
|  | // It is therefore critical to sort the chunks containing the function | 
|  | // pointers in the order that they are listed in the object file (top to | 
|  | // bottom), otherwise global objects might not be initialized in the | 
|  | // correct order. | 
|  | void Writer::sortCRTSectionChunks(std::vector<Chunk *> &chunks) { | 
|  | auto sectionChunkOrder = [](const Chunk *a, const Chunk *b) { | 
|  | auto sa = dyn_cast<SectionChunk>(a); | 
|  | auto sb = dyn_cast<SectionChunk>(b); | 
|  | assert(sa && sb && "Non-section chunks in CRT section!"); | 
|  |  | 
|  | StringRef sAObj = sa->file->mb.getBufferIdentifier(); | 
|  | StringRef sBObj = sb->file->mb.getBufferIdentifier(); | 
|  |  | 
|  | return sAObj == sBObj && sa->getSectionNumber() < sb->getSectionNumber(); | 
|  | }; | 
|  | llvm::stable_sort(chunks, sectionChunkOrder); | 
|  |  | 
|  | if (ctx.config.verbose) { | 
|  | for (auto &c : chunks) { | 
|  | auto sc = dyn_cast<SectionChunk>(c); | 
|  | log("  " + sc->file->mb.getBufferIdentifier().str() + | 
|  | ", SectionID: " + Twine(sc->getSectionNumber())); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | OutputSection *Writer::findSection(StringRef name) { | 
|  | for (OutputSection *sec : ctx.outputSections) | 
|  | if (sec->name == name) | 
|  | return sec; | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | uint32_t Writer::getSizeOfInitializedData() { | 
|  | uint32_t res = 0; | 
|  | for (OutputSection *s : ctx.outputSections) | 
|  | if (s->header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) | 
|  | res += s->getRawSize(); | 
|  | return res; | 
|  | } | 
|  |  | 
|  | // Add base relocations to .reloc section. | 
|  | void Writer::addBaserels() { | 
|  | if (!ctx.config.relocatable) | 
|  | return; | 
|  | relocSec->chunks.clear(); | 
|  | std::vector<Baserel> v; | 
|  | for (OutputSection *sec : ctx.outputSections) { | 
|  | if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) | 
|  | continue; | 
|  | llvm::TimeTraceScope timeScope("Base relocations: ", sec->name); | 
|  | // Collect all locations for base relocations. | 
|  | for (Chunk *c : sec->chunks) | 
|  | c->getBaserels(&v); | 
|  | // Add the addresses to .reloc section. | 
|  | if (!v.empty()) | 
|  | addBaserelBlocks(v); | 
|  | v.clear(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Add addresses to .reloc section. Note that addresses are grouped by page. | 
|  | void Writer::addBaserelBlocks(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; | 
|  | relocSec->addChunk(make<BaserelChunk>(page, &v[i], &v[0] + j)); | 
|  | i = j; | 
|  | page = p; | 
|  | } | 
|  | if (i == j) | 
|  | return; | 
|  | relocSec->addChunk(make<BaserelChunk>(page, &v[i], &v[0] + j)); | 
|  | } | 
|  |  | 
|  | PartialSection *Writer::createPartialSection(StringRef name, | 
|  | uint32_t outChars) { | 
|  | PartialSection *&pSec = partialSections[{name, outChars}]; | 
|  | if (pSec) | 
|  | return pSec; | 
|  | pSec = make<PartialSection>(name, outChars); | 
|  | return pSec; | 
|  | } | 
|  |  | 
|  | PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) { | 
|  | auto it = partialSections.find({name, outChars}); | 
|  | if (it != partialSections.end()) | 
|  | return it->second; | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | void Writer::fixTlsAlignment() { | 
|  | Defined *tlsSym = | 
|  | dyn_cast_or_null<Defined>(ctx.symtab.findUnderscore("_tls_used")); | 
|  | if (!tlsSym) | 
|  | return; | 
|  |  | 
|  | OutputSection *sec = ctx.getOutputSection(tlsSym->getChunk()); | 
|  | assert(sec && tlsSym->getRVA() >= sec->getRVA() && | 
|  | "no output section for _tls_used"); | 
|  |  | 
|  | uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff(); | 
|  | uint64_t tlsOffset = tlsSym->getRVA() - sec->getRVA(); | 
|  | uint64_t directorySize = ctx.config.is64() | 
|  | ? sizeof(object::coff_tls_directory64) | 
|  | : sizeof(object::coff_tls_directory32); | 
|  |  | 
|  | if (tlsOffset + directorySize > sec->getRawSize()) | 
|  | fatal("_tls_used sym is malformed"); | 
|  |  | 
|  | if (ctx.config.is64()) { | 
|  | object::coff_tls_directory64 *tlsDir = | 
|  | reinterpret_cast<object::coff_tls_directory64 *>(&secBuf[tlsOffset]); | 
|  | tlsDir->setAlignment(tlsAlignment); | 
|  | } else { | 
|  | object::coff_tls_directory32 *tlsDir = | 
|  | reinterpret_cast<object::coff_tls_directory32 *>(&secBuf[tlsOffset]); | 
|  | tlsDir->setAlignment(tlsAlignment); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Writer::prepareLoadConfig() { | 
|  | Symbol *sym = ctx.symtab.findUnderscore("_load_config_used"); | 
|  | auto *b = cast_if_present<DefinedRegular>(sym); | 
|  | if (!b) { | 
|  | if (ctx.config.guardCF != GuardCFLevel::Off) | 
|  | warn("Control Flow Guard is enabled but '_load_config_used' is missing"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | OutputSection *sec = ctx.getOutputSection(b->getChunk()); | 
|  | uint8_t *buf = buffer->getBufferStart(); | 
|  | uint8_t *secBuf = buf + sec->getFileOff(); | 
|  | uint8_t *symBuf = secBuf + (b->getRVA() - sec->getRVA()); | 
|  | uint32_t expectedAlign = ctx.config.is64() ? 8 : 4; | 
|  | if (b->getChunk()->getAlignment() < expectedAlign) | 
|  | warn("'_load_config_used' is misaligned (expected alignment to be " + | 
|  | Twine(expectedAlign) + " bytes, got " + | 
|  | Twine(b->getChunk()->getAlignment()) + " instead)"); | 
|  | else if (!isAligned(Align(expectedAlign), b->getRVA())) | 
|  | warn("'_load_config_used' is misaligned (RVA is 0x" + | 
|  | Twine::utohexstr(b->getRVA()) + " not aligned to " + | 
|  | Twine(expectedAlign) + " bytes)"); | 
|  |  | 
|  | if (ctx.config.is64()) | 
|  | prepareLoadConfig(reinterpret_cast<coff_load_configuration64 *>(symBuf)); | 
|  | else | 
|  | prepareLoadConfig(reinterpret_cast<coff_load_configuration32 *>(symBuf)); | 
|  | } | 
|  |  | 
|  | template <typename T> void Writer::prepareLoadConfig(T *loadConfig) { | 
|  | if (ctx.config.dependentLoadFlags) | 
|  | loadConfig->DependentLoadFlags = ctx.config.dependentLoadFlags; | 
|  |  | 
|  | checkLoadConfigGuardData(loadConfig); | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | void Writer::checkLoadConfigGuardData(const T *loadConfig) { | 
|  | size_t loadConfigSize = loadConfig->Size; | 
|  |  | 
|  | #define RETURN_IF_NOT_CONTAINS(field)                                          \ | 
|  | if (loadConfigSize < offsetof(T, field) + sizeof(T::field)) {                \ | 
|  | warn("'_load_config_used' structure too small to include " #field);        \ | 
|  | return;                                                                    \ | 
|  | } | 
|  |  | 
|  | #define IF_CONTAINS(field)                                                     \ | 
|  | if (loadConfigSize >= offsetof(T, field) + sizeof(T::field)) | 
|  |  | 
|  | #define CHECK_VA(field, sym)                                                   \ | 
|  | if (auto *s = dyn_cast<DefinedSynthetic>(ctx.symtab.findUnderscore(sym)))    \ | 
|  | if (loadConfig->field != ctx.config.imageBase + s->getRVA())               \ | 
|  | warn(#field " not set correctly in '_load_config_used'"); | 
|  |  | 
|  | #define CHECK_ABSOLUTE(field, sym)                                             \ | 
|  | if (auto *s = dyn_cast<DefinedAbsolute>(ctx.symtab.findUnderscore(sym)))     \ | 
|  | if (loadConfig->field != s->getVA())                                       \ | 
|  | warn(#field " not set correctly in '_load_config_used'"); | 
|  |  | 
|  | if (ctx.config.guardCF == GuardCFLevel::Off) | 
|  | return; | 
|  | RETURN_IF_NOT_CONTAINS(GuardFlags) | 
|  | CHECK_VA(GuardCFFunctionTable, "__guard_fids_table") | 
|  | CHECK_ABSOLUTE(GuardCFFunctionCount, "__guard_fids_count") | 
|  | CHECK_ABSOLUTE(GuardFlags, "__guard_flags") | 
|  | IF_CONTAINS(GuardAddressTakenIatEntryCount) { | 
|  | CHECK_VA(GuardAddressTakenIatEntryTable, "__guard_iat_table") | 
|  | CHECK_ABSOLUTE(GuardAddressTakenIatEntryCount, "__guard_iat_count") | 
|  | } | 
|  |  | 
|  | if (!(ctx.config.guardCF & GuardCFLevel::LongJmp)) | 
|  | return; | 
|  | RETURN_IF_NOT_CONTAINS(GuardLongJumpTargetCount) | 
|  | CHECK_VA(GuardLongJumpTargetTable, "__guard_longjmp_table") | 
|  | CHECK_ABSOLUTE(GuardLongJumpTargetCount, "__guard_longjmp_count") | 
|  |  | 
|  | if (!(ctx.config.guardCF & GuardCFLevel::EHCont)) | 
|  | return; | 
|  | RETURN_IF_NOT_CONTAINS(GuardEHContinuationCount) | 
|  | CHECK_VA(GuardEHContinuationTable, "__guard_eh_cont_table") | 
|  | CHECK_ABSOLUTE(GuardEHContinuationCount, "__guard_eh_cont_count") | 
|  |  | 
|  | #undef RETURN_IF_NOT_CONTAINS | 
|  | #undef IF_CONTAINS | 
|  | #undef CHECK_VA | 
|  | #undef CHECK_ABSOLUTE | 
|  | } |