| //===- 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/ADT/StringSwitch.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/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(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; |
| 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 xxHash64 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; |
| |
| COFFLinkerContext &ctx; |
| }; |
| |
| class CVDebugRecordChunk : public NonSectionChunk { |
| public: |
| size_t getSize() const override { |
| return sizeof(codeview::DebugInfo) + 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 (!config->pdbAltPath.empty()) |
| memcpy(p, config->pdbAltPath.data(), config->pdbAltPath.size()); |
| p[config->pdbAltPath.size()] = '\0'; |
| } |
| |
| mutable codeview::DebugInfo *buildId = nullptr; |
| }; |
| |
| 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 == 1) |
| return false; |
| if (c == 0) |
| return characteristics < other.characteristics; |
| return true; |
| } |
| }; |
| |
| // The writer writes a SymbolTable result to a file. |
| class Writer { |
| public: |
| Writer(COFFLinkerContext &c) : buffer(errorHandler().outputBuffer), ctx(c) {} |
| void run(); |
| |
| private: |
| void createSections(); |
| void createMiscChunks(); |
| void createImportTables(); |
| void appendImportThunks(); |
| void locateImportTables(); |
| void createExportTable(); |
| void mergeSections(); |
| void removeUnusedSections(); |
| void assignAddresses(); |
| void finalizeAddresses(); |
| void removeEmptySections(); |
| void assignOutputSectionIndices(); |
| void createSymbolAndStringTable(); |
| void openFile(StringRef outputPath); |
| template <typename PEHeaderTy> void writeHeader(); |
| void createSEHTable(); |
| void createRuntimePseudoRelocs(); |
| void insertCtorDtorSymbols(); |
| 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 writeSections(); |
| void writeBuildId(); |
| void sortSections(); |
| void sortExceptionTable(); |
| void sortCRTSectionChunks(std::vector<Chunk *> &chunks); |
| void addSyntheticIdata(); |
| 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); |
| |
| llvm::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(); |
| |
| std::unique_ptr<FileOutputBuffer> &buffer; |
| std::map<PartialSectionKey, PartialSection *> partialSections; |
| std::vector<char> strtab; |
| std::vector<llvm::object::coff_symbol16> outputSymtab; |
| 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; |
| |
| // The first and last .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. |
| Chunk *firstPdata = nullptr; |
| Chunk *lastPdata; |
| |
| COFFLinkerContext &ctx; |
| }; |
| } // anonymous namespace |
| |
| void lld::coff::writeResult(COFFLinkerContext &ctx) { 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(); |
| } |
| |
| // Write the section header to a given buffer. |
| void OutputSection::writeHeaderTo(uint8_t *buf) { |
| auto *hdr = reinterpret_cast<coff_section *>(buf); |
| *hdr = header; |
| if (stringTableOff) { |
| // If name is too long, write offset into the string table as a name. |
| sprintf(hdr->Name, "/%d", stringTableOff); |
| } else { |
| assert(!config->debug || name.size() <= COFF::NameSize || |
| (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. |
| static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) { |
| if (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 (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. |
| static std::pair<Defined *, bool> |
| 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 (config->machine) { |
| case ARMNT: |
| c = make<RangeExtensionThunkARM>(target); |
| break; |
| case ARM64: |
| c = make<RangeExtensionThunkARM64>(target); |
| break; |
| default: |
| llvm_unreachable("Unexpected architecture"); |
| } |
| Defined *d = make<DefinedSynthetic>("", 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. |
| static bool 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. |
| Defined *thunk; |
| bool wasNew; |
| std::tie(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.push_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 = makeMutableArrayRef( |
| bAlloc.Allocate<coff_relocation>(originalRelocs.size()), |
| originalRelocs.size()); |
| } else { |
| newRelocs = makeMutableArrayRef( |
| 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; |
| } |
| |
| // Verify that all relocations are in range, with no extra margin requirements. |
| static bool 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 (size_t j = 0, e = relocs.size(); j < e; ++j) { |
| const coff_relocation &rel = relocs[j]; |
| 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 (config->machine != ARMNT && 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) { |
| // 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; |
| 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; |
| 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++; |
| } |
| } |
| |
| // The main function of the writer. |
| void Writer::run() { |
| ScopedTimer t1(ctx.codeLayoutTimer); |
| |
| createImportTables(); |
| createSections(); |
| appendImportThunks(); |
| // Import thunks must be added before the Control Flow Guard tables are added. |
| createMiscChunks(); |
| createExportTable(); |
| mergeSections(); |
| removeUnusedSections(); |
| finalizeAddresses(); |
| removeEmptySections(); |
| assignOutputSectionIndices(); |
| setSectionPermissions(); |
| createSymbolAndStringTable(); |
| |
| if (fileSize > UINT32_MAX) |
| fatal("image size (" + Twine(fileSize) + ") " + |
| "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")"); |
| |
| openFile(config->outputFile); |
| if (config->is64()) { |
| writeHeader<pe32plus_header>(); |
| } else { |
| writeHeader<pe32_header>(); |
| } |
| writeSections(); |
| sortExceptionTable(); |
| |
| // Fix up the alignment in the TLS Directory's characteristic field, |
| // if a specific alignment value is needed |
| if (tlsAlignment) |
| fixTlsAlignment(); |
| |
| t1.stop(); |
| |
| if (!config->pdbPath.empty() && config->debug) { |
| assert(buildId); |
| createPDB(ctx, sectionTable, buildId->buildId); |
| } |
| writeBuildId(); |
| |
| writeLLDMapFile(ctx); |
| writeMapFile(ctx); |
| |
| if (errorCount()) |
| return; |
| |
| ScopedTimer t2(ctx.outputCommitTimer); |
| if (auto e = buffer->commit()) |
| fatal("failed to write the output file: " + 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. |
| static void sortBySectionOrder(std::vector<Chunk *> &chunks) { |
| auto getPriority = [](const Chunk *c) { |
| if (auto *sec = dyn_cast<SectionChunk>(c)) |
| if (sec->sym) |
| return 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.startswith("$"))) |
| 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 fils within a library. |
| for (auto it : partialSections) { |
| PartialSection *pSec = it.second; |
| if (!pSec->name.startswith(".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(); |
| |
| // 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) { |
| // 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 (!config->mingw) |
| return false; |
| if (!sc || !sc->isCOMDAT()) |
| return false; |
| return name.startswith(".text$") || name.startswith(".data$") || |
| name.startswith(".rdata$") || name.startswith(".pdata$") || |
| name.startswith(".xdata$") || name.startswith(".eh_frame$"); |
| } |
| |
| void Writer::sortSections() { |
| if (!config->callGraphProfile.empty()) { |
| DenseMap<const SectionChunk *, int> order = |
| computeCallGraphProfileOrder(ctx); |
| for (auto it : order) { |
| if (DefinedRegular *sym = it.first->sym) |
| config->order[sym->getName()] = it.second; |
| } |
| } |
| if (!config->order.empty()) |
| for (auto it : partialSections) |
| sortBySectionOrder(it.second->chunks); |
| } |
| |
| // Create output section objects and add them to OutputSections. |
| void Writer::createSections() { |
| // 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 (config->verbose) |
| sc->printDiscardedMessage(); |
| continue; |
| } |
| StringRef name = c->getSectionName(); |
| if (shouldStripSectionSuffix(sc, name)) |
| name = name.split('$').first; |
| |
| if (name.startswith(".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) |
| 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() { |
| 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 |
| OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec; |
| if (config->debug || config->repro || config->cetCompat) { |
| debugDirectory = |
| make<DebugDirectoryChunk>(ctx, debugRecords, config->repro); |
| debugDirectory->setAlignment(4); |
| debugInfoSec->addChunk(debugDirectory); |
| } |
| |
| if (config->debug) { |
| // 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>(); |
| debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId}); |
| } |
| |
| if (config->cetCompat) { |
| debugRecords.push_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 (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() { |
| // 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 (config->dllOrder.count(dll) == 0) |
| config->dllOrder[dll] = config->dllOrder.size(); |
| |
| if (file->impSym && !isa<DefinedImportData>(file->impSym)) |
| fatal(toString(*file->impSym) + " was replaced"); |
| DefinedImportData *impSym = cast_or_null<DefinedImportData>(file->impSym); |
| if (config->delayLoads.count(StringRef(file->dllName).lower())) { |
| if (!file->thunkSym) |
| fatal("cannot delay-load " + toString(file) + |
| " due to import of data: " + toString(*impSym)); |
| delayIdata.add(impSym); |
| } else { |
| idata.add(impSym); |
| } |
| } |
| } |
| |
| void Writer::appendImportThunks() { |
| if (ctx.importFileInstances.empty()) |
| return; |
| |
| for (ImportFile *file : ctx.importFileInstances) { |
| if (!file->live) |
| continue; |
| |
| if (!file->thunkSym) |
| continue; |
| |
| if (!isa<DefinedImportThunk>(file->thunkSym)) |
| fatal(toString(*file->thunkSym) + " was replaced"); |
| DefinedImportThunk *thunk = cast<DefinedImportThunk>(file->thunkSym); |
| if (file->thunkLive) |
| textSec->addChunk(thunk->getChunk()); |
| } |
| |
| if (!delayIdata.empty()) { |
| Defined *helper = cast<Defined>(config->delayLoadHelper); |
| delayIdata.create(ctx, 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); |
| } |
| } |
| |
| void Writer::createExportTable() { |
| if (!edataSec->chunks.empty()) { |
| // Allow using a custom built export table from input object files, instead |
| // of having the linker synthesize the tables. |
| if (config->hadExplicitExports) |
| warn("literal .edata sections override exports"); |
| } else if (!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 : config->exports) |
| if (e.sym && e.sym->getName().startswith("??_G")) |
| warn("export of deleting dtor: " + toString(*e.sym)); |
| } |
| |
| void Writer::removeUnusedSections() { |
| // 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() { |
| auto isEmpty = [](OutputSection *s) { return s->getVirtualSize() == 0; }; |
| llvm::erase_if(ctx.outputSections, isEmpty); |
| } |
| |
| void Writer::assignOutputSectionIndices() { |
| // 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; |
| } |
| |
| Optional<coff_symbol16> Writer::createSymbol(Defined *def) { |
| coff_symbol16 sym; |
| switch (def->kind()) { |
| case Symbol::DefinedAbsoluteKind: |
| sym.Value = def->getRVA(); |
| sym.SectionNumber = IMAGE_SYM_ABSOLUTE; |
| break; |
| case Symbol::DefinedSyntheticKind: |
| // Relative symbols are unrepresentable in a COFF symbol table. |
| return None; |
| default: { |
| // Don't write symbols that won't be written to the output to the symbol |
| // table. |
| Chunk *c = def->getChunk(); |
| if (!c) |
| return None; |
| OutputSection *os = ctx.getOutputSection(c); |
| if (!os) |
| return None; |
| |
| 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 None; |
| |
| StringRef name = def->getName(); |
| if (name.size() > COFF::NameSize) { |
| sym.Name.Offset.Zeroes = 0; |
| sym.Name.Offset.Offset = addEntryToStringTable(name); |
| } else { |
| memset(sym.Name.ShortName, 0, COFF::NameSize); |
| memcpy(sym.Name.ShortName, name.data(), name.size()); |
| } |
| |
| if (auto *d = dyn_cast<DefinedCOFF>(def)) { |
| COFFSymbolRef ref = d->getCOFFSymbol(); |
| sym.Type = ref.getType(); |
| sym.StorageClass = ref.getStorageClass(); |
| } else { |
| sym.Type = IMAGE_SYM_TYPE_NULL; |
| sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; |
| } |
| sym.NumberOfAuxSymbols = 0; |
| return sym; |
| } |
| |
| void Writer::createSymbolAndStringTable() { |
| // 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 (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 (config->debugDwarf || config->debugSymtab) { |
| 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 (Optional<coff_symbol16> sym = createSymbol(d)) |
| 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, config->fileAlign); |
| } |
| |
| void Writer::mergeSections() { |
| if (!pdataSec->chunks.empty()) { |
| firstPdata = pdataSec->chunks.front(); |
| lastPdata = pdataSec->chunks.back(); |
| } |
| |
| for (auto &p : config->merge) { |
| StringRef toName = p.second; |
| if (p.first == toName) |
| continue; |
| StringSet<> names; |
| while (1) { |
| if (!names.insert(toName).second) |
| fatal("/merge: cycle found for section '" + p.first + "'"); |
| auto i = config->merge.find(toName); |
| if (i == 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); |
| } |
| } |
| |
| // Visits all sections to assign incremental, non-overlapping RVAs and |
| // file offsets. |
| void Writer::assignAddresses() { |
| sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + |
| sizeof(data_directory) * 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) { |
| 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. |
| const bool isCodeSection = |
| (sec->header.Characteristics & IMAGE_SCN_CNT_CODE) && |
| (sec->header.Characteristics & IMAGE_SCN_MEM_READ) && |
| (sec->header.Characteristics & IMAGE_SCN_MEM_EXECUTE); |
| uint32_t padding = isCodeSection ? config->functionPadMin : 0; |
| |
| for (Chunk *c : sec->chunks) { |
| if (padding && c->isHotPatchable()) |
| virtualSize += padding; |
| 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. |
| 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); |
| 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(); |
| } |
| if (firstPdata) { |
| dir[EXCEPTION_TABLE].RelativeVirtualAddress = firstPdata->getRVA(); |
| dir[EXCEPTION_TABLE].Size = |
| lastPdata->getRVA() + lastPdata->getSize() - firstPdata->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); |
| 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 (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. |
| static void 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 (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() { |
| 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, the longjmp targets are in .gljmp$y sections, |
| // and ehcont targets are in .gehcont$y sections. If the object was not |
| // compiled with /guard:cf, we assume there were no setjmp and ehcont |
| // 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); |
| markSymbolsForRVATable(file, file->getGuardEHContChunks(), ehContTargets); |
| } else { |
| markSymbolsWithRelocations(file, addressTakenSyms); |
| } |
| } |
| |
| // 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", true); |
| |
| // Set __guard_flags, which will be used in the load config to indicate that |
| // /guard:cf was enabled. |
| uint32_t guardFlags = uint32_t(coff_guard_flags::CFInstrumented) | |
| uint32_t(coff_guard_flags::HasFidTable); |
| if (config->guardCF & GuardCFLevel::LongJmp) |
| guardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable); |
| if (config->guardCF & GuardCFLevel::EHCont) |
| guardFlags |= uint32_t(coff_guard_flags::HasEHContTable); |
| 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)); |
| } |
| |
| // 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; |
| sc->getRuntimePseudoRelocs(rels); |
| } |
| |
| if (!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"); |
| 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>(-1); |
| AbsolutePointerChunk *ctorListEnd = make<AbsolutePointerChunk>(0); |
| AbsolutePointerChunk *dtorListHead = make<AbsolutePointerChunk>(-1); |
| AbsolutePointerChunk *dtorListEnd = make<AbsolutePointerChunk>(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() { |
| for (auto &p : config->section) { |
| StringRef name = p.first; |
| uint32_t perm = p.second; |
| for (OutputSection *sec : ctx.outputSections) |
| if (sec->name == name) |
| sec->setPermissions(perm); |
| } |
| } |
| |
| // Write section contents to a mmap'ed file. |
| void Writer::writeSections() { |
| // Record the number of sections to apply section index relocations |
| // against absolute symbols. See applySecIdx in Chunks.cpp.. |
| DefinedAbsolute::numOutputSections = ctx.outputSections.size(); |
| |
| 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). |
| if (sec->header.Characteristics & IMAGE_SCN_CNT_CODE) |
| memset(secBuf, 0xCC, sec->getRawSize()); |
| parallelForEach(sec->chunks, [&](Chunk *c) { |
| c->writeTo(secBuf + c->getRVA() - sec->getRVA()); |
| }); |
| } |
| } |
| |
| void Writer::writeBuildId() { |
| // 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. |
| if (config->debug) { |
| 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; |
| bool generateSyntheticBuildId = |
| config->mingw && config->debug && config->pdbPath.empty(); |
| |
| if (config->repro || generateSyntheticBuildId) |
| hash = xxHash64(outputFileData); |
| |
| if (config->repro) |
| timestamp = static_cast<uint32_t>(hash); |
| |
| if (generateSyntheticBuildId) { |
| // For MinGW builds without a PDB file, we still generate a build id |
| // to allow associating a crash dump to the executable. |
| 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. |
| void Writer::sortExceptionTable() { |
| if (!firstPdata) |
| 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(firstPdata); |
| uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize(); |
| if (config->machine == AMD64) { |
| struct Entry { ulittle32_t begin, end, unwind; }; |
| if ((end - begin) % sizeof(Entry) != 0) { |
| fatal("unexpected .pdata size: " + Twine(end - begin) + |
| " is not a multiple of " + Twine(sizeof(Entry))); |
| } |
| parallelSort( |
| MutableArrayRef<Entry>((Entry *)begin, (Entry *)end), |
| [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); |
| return; |
| } |
| if (config->machine == ARMNT || config->machine == ARM64) { |
| struct Entry { ulittle32_t begin, unwind; }; |
| if ((end - begin) % sizeof(Entry) != 0) { |
| fatal("unexpected .pdata size: " + Twine(end - begin) + |
| " is not a multiple of " + Twine(sizeof(Entry))); |
| } |
| parallelSort( |
| MutableArrayRef<Entry>((Entry *)begin, (Entry *)end), |
| [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); |
| return; |
| } |
| lld::errs() << "warning: don't know how to handle .pdata.\n"; |
| } |
| |
| // 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 (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 (!config->relocatable) |
| return; |
| relocSec->chunks.clear(); |
| std::vector<Baserel> v; |
| for (OutputSection *sec : ctx.outputSections) { |
| if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) |
| continue; |
| // 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 = config->is64() |
| ? sizeof(object::coff_tls_directory64) |
| : sizeof(object::coff_tls_directory32); |
| |
| if (tlsOffset + directorySize > sec->getRawSize()) |
| fatal("_tls_used sym is malformed"); |
| |
| if (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); |
| } |
| } |