3107 lines · cpp
1//===- Writer.cpp ---------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "Writer.h"10#include "COFFLinkerContext.h"11#include "CallGraphSort.h"12#include "Config.h"13#include "DLL.h"14#include "InputFiles.h"15#include "LLDMapFile.h"16#include "MapFile.h"17#include "PDB.h"18#include "SymbolTable.h"19#include "Symbols.h"20#include "lld/Common/ErrorHandler.h"21#include "lld/Common/Memory.h"22#include "lld/Common/Timer.h"23#include "llvm/ADT/DenseMap.h"24#include "llvm/ADT/STLExtras.h"25#include "llvm/ADT/StringSet.h"26#include "llvm/BinaryFormat/COFF.h"27#include "llvm/MC/StringTableBuilder.h"28#include "llvm/Support/Endian.h"29#include "llvm/Support/FileOutputBuffer.h"30#include "llvm/Support/FormatAdapters.h"31#include "llvm/Support/FormatVariadic.h"32#include "llvm/Support/Parallel.h"33#include "llvm/Support/RandomNumberGenerator.h"34#include "llvm/Support/TimeProfiler.h"35#include "llvm/Support/xxhash.h"36#include <algorithm>37#include <cstdio>38#include <map>39#include <memory>40#include <utility>41 42using namespace llvm;43using namespace llvm::COFF;44using namespace llvm::object;45using namespace llvm::support;46using namespace llvm::support::endian;47using namespace lld;48using namespace lld::coff;49 50/* To re-generate DOSProgram:51$ cat > /tmp/DOSProgram.asm52org 053 ; Copy cs to ds.54 push cs55 pop ds56 ; Point ds:dx at the $-terminated string.57 mov dx, str58 ; Int 21/AH=09h: Write string to standard output.59 mov ah, 0x960 int 0x2161 ; Int 21/AH=4Ch: Exit with return code (in AL).62 mov ax, 0x4C0163 int 0x2164str:65 db 'This program cannot be run in DOS mode.$'66align 8, db 067$ nasm -fbin /tmp/DOSProgram.asm -o /tmp/DOSProgram.bin68$ xxd -i /tmp/DOSProgram.bin69*/70static unsigned char dosProgram[] = {71 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c,72 0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,73 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65,74 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20,75 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x24, 0x00, 0x0076};77static_assert(sizeof(dosProgram) % 8 == 0,78 "DOSProgram size must be multiple of 8");79static_assert((sizeof(dos_header) + sizeof(dosProgram)) % 8 == 0,80 "DOSStub size must be multiple of 8");81 82static const int numberOfDataDirectory = 16;83 84namespace {85 86class DebugDirectoryChunk : public NonSectionChunk {87public:88 DebugDirectoryChunk(const COFFLinkerContext &c,89 const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,90 bool writeRepro)91 : records(r), writeRepro(writeRepro), ctx(c) {}92 93 size_t getSize() const override {94 return (records.size() + int(writeRepro)) * sizeof(debug_directory);95 }96 97 void writeTo(uint8_t *b) const override {98 auto *d = reinterpret_cast<debug_directory *>(b);99 100 for (const std::pair<COFF::DebugType, Chunk *>& record : records) {101 Chunk *c = record.second;102 const OutputSection *os = ctx.getOutputSection(c);103 uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA());104 fillEntry(d, record.first, c->getSize(), c->getRVA(), offs);105 ++d;106 }107 108 if (writeRepro) {109 // FIXME: The COFF spec allows either a 0-sized entry to just say110 // "the timestamp field is really a hash", or a 4-byte size field111 // followed by that many bytes containing a longer hash (with the112 // lowest 4 bytes usually being the timestamp in little-endian order).113 // Consider storing the full 8 bytes computed by xxh3_64bits here.114 fillEntry(d, COFF::IMAGE_DEBUG_TYPE_REPRO, 0, 0, 0);115 }116 }117 118 void setTimeDateStamp(uint32_t timeDateStamp) {119 for (support::ulittle32_t *tds : timeDateStamps)120 *tds = timeDateStamp;121 }122 123private:124 void fillEntry(debug_directory *d, COFF::DebugType debugType, size_t size,125 uint64_t rva, uint64_t offs) const {126 d->Characteristics = 0;127 d->TimeDateStamp = 0;128 d->MajorVersion = 0;129 d->MinorVersion = 0;130 d->Type = debugType;131 d->SizeOfData = size;132 d->AddressOfRawData = rva;133 d->PointerToRawData = offs;134 135 timeDateStamps.push_back(&d->TimeDateStamp);136 }137 138 mutable std::vector<support::ulittle32_t *> timeDateStamps;139 const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;140 bool writeRepro;141 const COFFLinkerContext &ctx;142};143 144class CVDebugRecordChunk : public NonSectionChunk {145public:146 CVDebugRecordChunk(const COFFLinkerContext &c) : ctx(c) {}147 148 size_t getSize() const override {149 return sizeof(codeview::DebugInfo) + ctx.config.pdbAltPath.size() + 1;150 }151 152 void writeTo(uint8_t *b) const override {153 // Save off the DebugInfo entry to backfill the file signature (build id)154 // in Writer::writeBuildId155 buildId = reinterpret_cast<codeview::DebugInfo *>(b);156 157 // variable sized field (PDB Path)158 char *p = reinterpret_cast<char *>(b + sizeof(*buildId));159 if (!ctx.config.pdbAltPath.empty())160 memcpy(p, ctx.config.pdbAltPath.data(), ctx.config.pdbAltPath.size());161 p[ctx.config.pdbAltPath.size()] = '\0';162 }163 164 mutable codeview::DebugInfo *buildId = nullptr;165 166private:167 const COFFLinkerContext &ctx;168};169 170class ExtendedDllCharacteristicsChunk : public NonSectionChunk {171public:172 ExtendedDllCharacteristicsChunk(uint32_t c) : characteristics(c) {}173 174 size_t getSize() const override { return 4; }175 176 void writeTo(uint8_t *buf) const override { write32le(buf, characteristics); }177 178 uint32_t characteristics = 0;179};180 181// PartialSection represents a group of chunks that contribute to an182// OutputSection. Collating a collection of PartialSections of same name and183// characteristics constitutes the OutputSection.184class PartialSectionKey {185public:186 StringRef name;187 unsigned characteristics;188 189 bool operator<(const PartialSectionKey &other) const {190 int c = name.compare(other.name);191 if (c > 0)192 return false;193 if (c == 0)194 return characteristics < other.characteristics;195 return true;196 }197};198 199struct ChunkRange {200 Chunk *first = nullptr, *last;201};202 203// The writer writes a SymbolTable result to a file.204class Writer {205public:206 Writer(COFFLinkerContext &c)207 : buffer(c.e.outputBuffer), strtab(StringTableBuilder::WinCOFF),208 delayIdata(c), ctx(c) {}209 void run();210 211private:212 void calculateStubDependentSizes();213 void createSections();214 void createMiscChunks();215 void createImportTables();216 void appendImportThunks();217 void locateImportTables();218 void createExportTable();219 void mergeSection(const std::map<StringRef, StringRef>::value_type &p);220 void mergeSections();221 void sortECChunks();222 void appendECImportTables();223 void removeUnusedSections();224 void layoutSections();225 void assignAddresses();226 bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin,227 MachineTypes machine);228 std::pair<Defined *, bool> getThunk(DenseMap<uint64_t, Defined *> &lastThunks,229 Defined *target, uint64_t p,230 uint16_t type, int margin,231 MachineTypes machine);232 bool createThunks(OutputSection *os, int margin);233 bool verifyRanges(const std::vector<Chunk *> chunks);234 void createECCodeMap();235 void finalizeAddresses();236 void removeEmptySections();237 void assignOutputSectionIndices();238 void createSymbolAndStringTable();239 void openFile(StringRef outputPath);240 template <typename PEHeaderTy> void writeHeader();241 void createSEHTable();242 void createRuntimePseudoRelocs();243 void createECChunks();244 void insertCtorDtorSymbols();245 void insertBssDataStartEndSymbols();246 void markSymbolsWithRelocations(ObjFile *file, SymbolRVASet &usedSymbols);247 void createGuardCFTables();248 void markSymbolsForRVATable(ObjFile *file,249 ArrayRef<SectionChunk *> symIdxChunks,250 SymbolRVASet &tableSymbols);251 void getSymbolsFromSections(ObjFile *file,252 ArrayRef<SectionChunk *> symIdxChunks,253 std::vector<Symbol *> &symbols);254 void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,255 StringRef countSym, bool hasFlag=false);256 void setSectionPermissions();257 void setECSymbols();258 void writeSections();259 void writeBuildId();260 void writePEChecksum();261 void sortSections();262 template <typename T> void sortExceptionTable(ChunkRange &exceptionTable);263 void sortExceptionTables();264 void sortCRTSectionChunks(std::vector<Chunk *> &chunks);265 void addSyntheticIdata();266 void sortBySectionOrder(std::vector<Chunk *> &chunks);267 void fixPartialSectionChars(StringRef name, uint32_t chars);268 bool fixGnuImportChunks();269 void fixTlsAlignment();270 PartialSection *createPartialSection(StringRef name, uint32_t outChars);271 PartialSection *findPartialSection(StringRef name, uint32_t outChars);272 273 std::optional<coff_symbol16> createSymbol(Defined *d);274 size_t addEntryToStringTable(StringRef str);275 276 OutputSection *findSection(StringRef name);277 void addBaserels();278 void addBaserelBlocks(std::vector<Baserel> &v);279 void createDynamicRelocs();280 281 uint32_t getSizeOfInitializedData();282 283 void prepareLoadConfig();284 template <typename T>285 void prepareLoadConfig(SymbolTable &symtab, T *loadConfig);286 287 void printSummary();288 289 std::unique_ptr<FileOutputBuffer> &buffer;290 std::map<PartialSectionKey, PartialSection *> partialSections;291 StringTableBuilder strtab;292 std::vector<llvm::object::coff_symbol16> outputSymtab;293 std::vector<ECCodeMapEntry> codeMap;294 IdataContents idata;295 Chunk *importTableStart = nullptr;296 uint64_t importTableSize = 0;297 Chunk *iatStart = nullptr;298 uint64_t iatSize = 0;299 DelayLoadContents delayIdata;300 bool setNoSEHCharacteristic = false;301 uint32_t tlsAlignment = 0;302 303 DebugDirectoryChunk *debugDirectory = nullptr;304 std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;305 CVDebugRecordChunk *buildId = nullptr;306 ArrayRef<uint8_t> sectionTable;307 308 // List of Arm64EC export thunks.309 std::vector<std::pair<Chunk *, Defined *>> exportThunks;310 311 uint64_t fileSize;312 uint32_t pointerToSymbolTable = 0;313 uint64_t sizeOfImage;314 uint64_t sizeOfHeaders;315 316 uint32_t dosStubSize;317 uint32_t coffHeaderOffset;318 uint32_t peHeaderOffset;319 uint32_t dataDirOffset64;320 321 OutputSection *textSec;322 OutputSection *wowthkSec;323 OutputSection *hexpthkSec;324 OutputSection *bssSec;325 OutputSection *rdataSec;326 OutputSection *buildidSec;327 OutputSection *cvinfoSec;328 OutputSection *dataSec;329 OutputSection *pdataSec;330 OutputSection *idataSec;331 OutputSection *edataSec;332 OutputSection *didatSec;333 OutputSection *a64xrmSec;334 OutputSection *rsrcSec;335 OutputSection *relocSec;336 OutputSection *ctorsSec;337 OutputSection *dtorsSec;338 // Either .rdata section or .buildid section.339 OutputSection *debugInfoSec;340 341 // The range of .pdata sections in the output file.342 //343 // We need to keep track of the location of .pdata in whichever section it344 // gets merged into so that we can sort its contents and emit a correct data345 // directory entry for the exception table. This is also the case for some346 // other sections (such as .edata) but because the contents of those sections347 // are entirely linker-generated we can keep track of their locations using348 // the chunks that the linker creates. All .pdata chunks come from input349 // files, so we need to keep track of them separately.350 ChunkRange pdata;351 352 // x86_64 .pdata sections on ARM64EC/ARM64X targets.353 ChunkRange hybridPdata;354 355 // CHPE metadata symbol on ARM64C target.356 DefinedRegular *chpeSym = nullptr;357 358 COFFLinkerContext &ctx;359};360} // anonymous namespace361 362void lld::coff::writeResult(COFFLinkerContext &ctx) {363 llvm::TimeTraceScope timeScope("Write output(s)");364 Writer(ctx).run();365}366 367void OutputSection::addChunk(Chunk *c) {368 chunks.push_back(c);369}370 371void OutputSection::insertChunkAtStart(Chunk *c) {372 chunks.insert(chunks.begin(), c);373}374 375void OutputSection::setPermissions(uint32_t c) {376 header.Characteristics &= ~permMask;377 header.Characteristics |= c;378}379 380void OutputSection::merge(OutputSection *other) {381 chunks.insert(chunks.end(), other->chunks.begin(), other->chunks.end());382 other->chunks.clear();383 contribSections.insert(contribSections.end(), other->contribSections.begin(),384 other->contribSections.end());385 other->contribSections.clear();386 387 // MS link.exe compatibility: when merging a code section into a data section,388 // mark the target section as a code section.389 if (other->header.Characteristics & IMAGE_SCN_CNT_CODE) {390 header.Characteristics |= IMAGE_SCN_CNT_CODE;391 header.Characteristics &=392 ~(IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_CNT_UNINITIALIZED_DATA);393 }394}395 396// Write the section header to a given buffer.397void OutputSection::writeHeaderTo(uint8_t *buf, bool isDebug) {398 auto *hdr = reinterpret_cast<coff_section *>(buf);399 *hdr = header;400 if (stringTableOff) {401 // If name is too long, write offset into the string table as a name.402 encodeSectionName(hdr->Name, stringTableOff);403 } else {404 assert(!isDebug || name.size() <= COFF::NameSize ||405 (hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0);406 strncpy(hdr->Name, name.data(),407 std::min(name.size(), (size_t)COFF::NameSize));408 }409}410 411void OutputSection::addContributingPartialSection(PartialSection *sec) {412 contribSections.push_back(sec);413}414 415void OutputSection::splitECChunks() {416 llvm::stable_sort(chunks, [=](const Chunk *a, const Chunk *b) {417 return (a->getMachine() != ARM64) < (b->getMachine() != ARM64);418 });419}420 421// Check whether the target address S is in range from a relocation422// of type relType at address P.423bool Writer::isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin,424 MachineTypes machine) {425 if (machine == ARMNT) {426 int64_t diff = AbsoluteDifference(s, p + 4) + margin;427 switch (relType) {428 case IMAGE_REL_ARM_BRANCH20T:429 return isInt<21>(diff);430 case IMAGE_REL_ARM_BRANCH24T:431 case IMAGE_REL_ARM_BLX23T:432 return isInt<25>(diff);433 default:434 return true;435 }436 } else if (isAnyArm64(machine)) {437 int64_t diff = AbsoluteDifference(s, p) + margin;438 switch (relType) {439 case IMAGE_REL_ARM64_BRANCH26:440 return isInt<28>(diff);441 case IMAGE_REL_ARM64_BRANCH19:442 return isInt<21>(diff);443 case IMAGE_REL_ARM64_BRANCH14:444 return isInt<16>(diff);445 default:446 return true;447 }448 } else {449 return true;450 }451}452 453// Return the last thunk for the given target if it is in range,454// or create a new one.455std::pair<Defined *, bool>456Writer::getThunk(DenseMap<uint64_t, Defined *> &lastThunks, Defined *target,457 uint64_t p, uint16_t type, int margin, MachineTypes machine) {458 Defined *&lastThunk = lastThunks[target->getRVA()];459 if (lastThunk && isInRange(type, lastThunk->getRVA(), p, margin, machine))460 return {lastThunk, false};461 Chunk *c;462 switch (getMachineArchType(machine)) {463 case Triple::thumb:464 c = make<RangeExtensionThunkARM>(ctx, target);465 break;466 case Triple::aarch64:467 c = make<RangeExtensionThunkARM64>(machine, target);468 break;469 default:470 llvm_unreachable("Unexpected architecture");471 }472 Defined *d = make<DefinedSynthetic>("range_extension_thunk", c);473 lastThunk = d;474 return {d, true};475}476 477// This checks all relocations, and for any relocation which isn't in range478// it adds a thunk after the section chunk that contains the relocation.479// If the latest thunk for the specific target is in range, that is used480// instead of creating a new thunk. All range checks are done with the481// specified margin, to make sure that relocations that originally are in482// range, but only barely, also get thunks - in case other added thunks makes483// the target go out of range.484//485// After adding thunks, we verify that all relocations are in range (with486// no extra margin requirements). If this failed, we restart (throwing away487// the previously created thunks) and retry with a wider margin.488bool Writer::createThunks(OutputSection *os, int margin) {489 bool addressesChanged = false;490 DenseMap<uint64_t, Defined *> lastThunks;491 DenseMap<std::pair<ObjFile *, Defined *>, uint32_t> thunkSymtabIndices;492 size_t thunksSize = 0;493 // Recheck Chunks.size() each iteration, since we can insert more494 // elements into it.495 for (size_t i = 0; i != os->chunks.size(); ++i) {496 SectionChunk *sc = dyn_cast<SectionChunk>(os->chunks[i]);497 if (!sc) {498 auto chunk = cast<NonSectionChunk>(os->chunks[i]);499 if (uint32_t size = chunk->extendRanges()) {500 thunksSize += size;501 addressesChanged = true;502 }503 continue;504 }505 MachineTypes machine = sc->getMachine();506 size_t thunkInsertionSpot = i + 1;507 508 // Try to get a good enough estimate of where new thunks will be placed.509 // Offset this by the size of the new thunks added so far, to make the510 // estimate slightly better.511 size_t thunkInsertionRVA = sc->getRVA() + sc->getSize() + thunksSize;512 ObjFile *file = sc->file;513 std::vector<std::pair<uint32_t, uint32_t>> relocReplacements;514 ArrayRef<coff_relocation> originalRelocs =515 file->getCOFFObj()->getRelocations(sc->header);516 for (size_t j = 0, e = originalRelocs.size(); j < e; ++j) {517 const coff_relocation &rel = originalRelocs[j];518 Symbol *relocTarget = file->getSymbol(rel.SymbolTableIndex);519 520 // The estimate of the source address P should be pretty accurate,521 // but we don't know whether the target Symbol address should be522 // offset by thunksSize or not (or by some of thunksSize but not all of523 // it), giving us some uncertainty once we have added one thunk.524 uint64_t p = sc->getRVA() + rel.VirtualAddress + thunksSize;525 526 Defined *sym = dyn_cast_or_null<Defined>(relocTarget);527 if (!sym)528 continue;529 530 uint64_t s = sym->getRVA();531 532 if (isInRange(rel.Type, s, p, margin, machine))533 continue;534 535 // If the target isn't in range, hook it up to an existing or new thunk.536 auto [thunk, wasNew] =537 getThunk(lastThunks, sym, p, rel.Type, margin, machine);538 if (wasNew) {539 Chunk *thunkChunk = thunk->getChunk();540 thunkChunk->setRVA(541 thunkInsertionRVA); // Estimate of where it will be located.542 os->chunks.insert(os->chunks.begin() + thunkInsertionSpot, thunkChunk);543 thunkInsertionSpot++;544 thunksSize += thunkChunk->getSize();545 thunkInsertionRVA += thunkChunk->getSize();546 addressesChanged = true;547 }548 549 // To redirect the relocation, add a symbol to the parent object file's550 // symbol table, and replace the relocation symbol table index with the551 // new index.552 auto insertion = thunkSymtabIndices.insert({{file, thunk}, ~0U});553 uint32_t &thunkSymbolIndex = insertion.first->second;554 if (insertion.second)555 thunkSymbolIndex = file->addRangeThunkSymbol(thunk);556 relocReplacements.emplace_back(j, thunkSymbolIndex);557 }558 559 // Get a writable copy of this section's relocations so they can be560 // modified. If the relocations point into the object file, allocate new561 // memory. Otherwise, this must be previously allocated memory that can be562 // modified in place.563 ArrayRef<coff_relocation> curRelocs = sc->getRelocs();564 MutableArrayRef<coff_relocation> newRelocs;565 if (originalRelocs.data() == curRelocs.data()) {566 newRelocs = MutableArrayRef(567 bAlloc().Allocate<coff_relocation>(originalRelocs.size()),568 originalRelocs.size());569 } else {570 newRelocs = MutableArrayRef(571 const_cast<coff_relocation *>(curRelocs.data()), curRelocs.size());572 }573 574 // Copy each relocation, but replace the symbol table indices which need575 // thunks.576 auto nextReplacement = relocReplacements.begin();577 auto endReplacement = relocReplacements.end();578 for (size_t i = 0, e = originalRelocs.size(); i != e; ++i) {579 newRelocs[i] = originalRelocs[i];580 if (nextReplacement != endReplacement && nextReplacement->first == i) {581 newRelocs[i].SymbolTableIndex = nextReplacement->second;582 ++nextReplacement;583 }584 }585 586 sc->setRelocs(newRelocs);587 }588 return addressesChanged;589}590 591// Create a code map for CHPE metadata.592void Writer::createECCodeMap() {593 if (!ctx.symtab.isEC())594 return;595 596 // Clear the map in case we were're recomputing the map after adding597 // a range extension thunk.598 codeMap.clear();599 600 std::optional<chpe_range_type> lastType;601 Chunk *first, *last;602 603 auto closeRange = [&]() {604 if (lastType) {605 codeMap.push_back({first, last, *lastType});606 lastType.reset();607 }608 };609 610 for (OutputSection *sec : ctx.outputSections) {611 for (Chunk *c : sec->chunks) {612 // Skip empty section chunks. MS link.exe does not seem to do that and613 // generates empty code ranges in some cases.614 if (isa<SectionChunk>(c) && !c->getSize())615 continue;616 617 std::optional<chpe_range_type> chunkType = c->getArm64ECRangeType();618 if (chunkType != lastType) {619 closeRange();620 first = c;621 lastType = chunkType;622 }623 last = c;624 }625 }626 627 closeRange();628 629 Symbol *tableCountSym = ctx.symtab.findUnderscore("__hybrid_code_map_count");630 cast<DefinedAbsolute>(tableCountSym)->setVA(codeMap.size());631}632 633// Verify that all relocations are in range, with no extra margin requirements.634bool Writer::verifyRanges(const std::vector<Chunk *> chunks) {635 for (Chunk *c : chunks) {636 SectionChunk *sc = dyn_cast<SectionChunk>(c);637 if (!sc) {638 if (!cast<NonSectionChunk>(c)->verifyRanges())639 return false;640 continue;641 }642 MachineTypes machine = sc->getMachine();643 644 ArrayRef<coff_relocation> relocs = sc->getRelocs();645 for (const coff_relocation &rel : relocs) {646 Symbol *relocTarget = sc->file->getSymbol(rel.SymbolTableIndex);647 648 Defined *sym = dyn_cast_or_null<Defined>(relocTarget);649 if (!sym)650 continue;651 652 uint64_t p = sc->getRVA() + rel.VirtualAddress;653 uint64_t s = sym->getRVA();654 655 if (!isInRange(rel.Type, s, p, 0, machine))656 return false;657 }658 }659 return true;660}661 662// Assign addresses and add thunks if necessary.663void Writer::finalizeAddresses() {664 assignAddresses();665 if (ctx.config.machine != ARMNT && !isAnyArm64(ctx.config.machine))666 return;667 668 size_t origNumChunks = 0;669 for (OutputSection *sec : ctx.outputSections) {670 sec->origChunks = sec->chunks;671 origNumChunks += sec->chunks.size();672 }673 674 int pass = 0;675 int margin = 1024 * 100;676 while (true) {677 llvm::TimeTraceScope timeScope2("Add thunks pass");678 679 // First check whether we need thunks at all, or if the previous pass of680 // adding them turned out ok.681 bool rangesOk = true;682 size_t numChunks = 0;683 {684 llvm::TimeTraceScope timeScope3("Verify ranges");685 for (OutputSection *sec : ctx.outputSections) {686 if (!verifyRanges(sec->chunks)) {687 rangesOk = false;688 break;689 }690 numChunks += sec->chunks.size();691 }692 }693 if (rangesOk) {694 if (pass > 0)695 Log(ctx) << "Added " << (numChunks - origNumChunks) << " thunks with "696 << "margin " << margin << " in " << pass << " passes";697 return;698 }699 700 if (pass >= 10)701 Fatal(ctx) << "adding thunks hasn't converged after " << pass702 << " passes";703 704 if (pass > 0) {705 // If the previous pass didn't work out, reset everything back to the706 // original conditions before retrying with a wider margin. This should707 // ideally never happen under real circumstances.708 for (OutputSection *sec : ctx.outputSections)709 sec->chunks = sec->origChunks;710 margin *= 2;711 }712 713 // Try adding thunks everywhere where it is needed, with a margin714 // to avoid things going out of range due to the added thunks.715 bool addressesChanged = false;716 {717 llvm::TimeTraceScope timeScope3("Create thunks");718 for (OutputSection *sec : ctx.outputSections)719 addressesChanged |= createThunks(sec, margin);720 }721 // If the verification above thought we needed thunks, we should have722 // added some.723 assert(addressesChanged);724 (void)addressesChanged;725 726 // Recalculate the layout for the whole image (and verify the ranges at727 // the start of the next round).728 assignAddresses();729 730 pass++;731 }732}733 734void Writer::writePEChecksum() {735 if (!ctx.config.writeCheckSum) {736 return;737 }738 739 llvm::TimeTraceScope timeScope("PE checksum");740 741 // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#checksum742 uint32_t *buf = (uint32_t *)buffer->getBufferStart();743 uint32_t size = (uint32_t)(buffer->getBufferSize());744 745 pe32_header *peHeader = (pe32_header *)((uint8_t *)buf + coffHeaderOffset +746 sizeof(coff_file_header));747 748 uint64_t sum = 0;749 uint32_t count = size;750 ulittle16_t *addr = (ulittle16_t *)buf;751 752 // The PE checksum algorithm, implemented as suggested in RFC1071753 while (count > 1) {754 sum += *addr++;755 count -= 2;756 }757 758 // Add left-over byte, if any759 if (count > 0)760 sum += *(unsigned char *)addr;761 762 // Fold 32-bit sum to 16 bits763 while (sum >> 16) {764 sum = (sum & 0xffff) + (sum >> 16);765 }766 767 sum += size;768 peHeader->CheckSum = sum;769}770 771// The main function of the writer.772void Writer::run() {773 {774 llvm::TimeTraceScope timeScope("Write PE");775 ScopedTimer t1(ctx.codeLayoutTimer);776 777 calculateStubDependentSizes();778 if (ctx.config.machine == ARM64X)779 ctx.dynamicRelocs = make<DynamicRelocsChunk>();780 createImportTables();781 createSections();782 appendImportThunks();783 // Import thunks must be added before the Control Flow Guard tables are784 // added.785 createMiscChunks();786 createExportTable();787 mergeSections();788 sortECChunks();789 appendECImportTables();790 createDynamicRelocs();791 removeUnusedSections();792 layoutSections();793 finalizeAddresses();794 removeEmptySections();795 assignOutputSectionIndices();796 setSectionPermissions();797 setECSymbols();798 createSymbolAndStringTable();799 800 if (fileSize > UINT32_MAX)801 Fatal(ctx) << "image size (" << fileSize << ") "802 << "exceeds maximum allowable size (" << UINT32_MAX << ")";803 804 openFile(ctx.config.outputFile);805 if (ctx.config.is64()) {806 writeHeader<pe32plus_header>();807 } else {808 writeHeader<pe32_header>();809 }810 writeSections();811 prepareLoadConfig();812 sortExceptionTables();813 814 // Fix up the alignment in the TLS Directory's characteristic field,815 // if a specific alignment value is needed816 if (tlsAlignment)817 fixTlsAlignment();818 }819 820 if (!ctx.config.pdbPath.empty() && ctx.config.debug) {821 assert(buildId);822 createPDB(ctx, sectionTable, buildId->buildId);823 }824 writeBuildId();825 826 writeLLDMapFile(ctx);827 writeMapFile(ctx);828 829 writePEChecksum();830 831 printSummary();832 833 if (errorCount())834 return;835 836 llvm::TimeTraceScope timeScope("Commit PE to disk");837 ScopedTimer t2(ctx.outputCommitTimer);838 if (auto e = buffer->commit())839 Fatal(ctx) << "failed to write output '" << buffer->getPath()840 << "': " << toString(std::move(e));841}842 843static StringRef getOutputSectionName(StringRef name) {844 StringRef s = name.split('$').first;845 846 // Treat a later period as a separator for MinGW, for sections like847 // ".ctors.01234".848 return s.substr(0, s.find('.', 1));849}850 851// For /order.852void Writer::sortBySectionOrder(std::vector<Chunk *> &chunks) {853 auto getPriority = [&ctx = ctx](const Chunk *c) {854 if (auto *sec = dyn_cast<SectionChunk>(c))855 if (sec->sym)856 return ctx.config.order.lookup(sec->sym->getName());857 return 0;858 };859 860 llvm::stable_sort(chunks, [=](const Chunk *a, const Chunk *b) {861 return getPriority(a) < getPriority(b);862 });863}864 865// Change the characteristics of existing PartialSections that belong to the866// section Name to Chars.867void Writer::fixPartialSectionChars(StringRef name, uint32_t chars) {868 for (auto it : partialSections) {869 PartialSection *pSec = it.second;870 StringRef curName = pSec->name;871 if (!curName.consume_front(name) ||872 (!curName.empty() && !curName.starts_with("$")))873 continue;874 if (pSec->characteristics == chars)875 continue;876 PartialSection *destSec = createPartialSection(pSec->name, chars);877 destSec->chunks.insert(destSec->chunks.end(), pSec->chunks.begin(),878 pSec->chunks.end());879 pSec->chunks.clear();880 }881}882 883// Sort concrete section chunks from GNU import libraries.884//885// GNU binutils doesn't use short import files, but instead produces import886// libraries that consist of object files, with section chunks for the .idata$*887// sections. These are linked just as regular static libraries. Each import888// library consists of one header object, one object file for every imported889// symbol, and one trailer object. In order for the .idata tables/lists to890// be formed correctly, the section chunks within each .idata$* section need891// to be grouped by library, and sorted alphabetically within each library892// (which makes sure the header comes first and the trailer last).893bool Writer::fixGnuImportChunks() {894 uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;895 896 // Make sure all .idata$* section chunks are mapped as RDATA in order to897 // be sorted into the same sections as our own synthesized .idata chunks.898 fixPartialSectionChars(".idata", rdata);899 900 bool hasIdata = false;901 // Sort all .idata$* chunks, grouping chunks from the same library,902 // with alphabetical ordering of the object files within a library.903 for (auto it : partialSections) {904 PartialSection *pSec = it.second;905 if (!pSec->name.starts_with(".idata"))906 continue;907 908 if (!pSec->chunks.empty())909 hasIdata = true;910 llvm::stable_sort(pSec->chunks, [&](Chunk *s, Chunk *t) {911 SectionChunk *sc1 = dyn_cast<SectionChunk>(s);912 SectionChunk *sc2 = dyn_cast<SectionChunk>(t);913 if (!sc1 || !sc2) {914 // if SC1, order them ascending. If SC2 or both null,915 // S is not less than T.916 return sc1 != nullptr;917 }918 // Make a string with "libraryname/objectfile" for sorting, achieving919 // both grouping by library and sorting of objects within a library,920 // at once.921 std::string key1 =922 (sc1->file->parentName + "/" + sc1->file->getName()).str();923 std::string key2 =924 (sc2->file->parentName + "/" + sc2->file->getName()).str();925 return key1 < key2;926 });927 }928 return hasIdata;929}930 931// Add generated idata chunks, for imported symbols and DLLs, and a932// terminator in .idata$2.933void Writer::addSyntheticIdata() {934 uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;935 idata.create(ctx);936 937 // Add the .idata content in the right section groups, to allow938 // chunks from other linked in object files to be grouped together.939 // See Microsoft PE/COFF spec 5.4 for details.940 auto add = [&](StringRef n, std::vector<Chunk *> &v) {941 PartialSection *pSec = createPartialSection(n, rdata);942 pSec->chunks.insert(pSec->chunks.end(), v.begin(), v.end());943 };944 945 // The loader assumes a specific order of data.946 // Add each type in the correct order.947 add(".idata$2", idata.dirs);948 add(".idata$4", idata.lookups);949 add(".idata$5", idata.addresses);950 if (!idata.hints.empty())951 add(".idata$6", idata.hints);952 add(".idata$7", idata.dllNames);953 if (!idata.auxIat.empty())954 add(".idata$9", idata.auxIat);955 if (!idata.auxIatCopy.empty())956 add(".idata$a", idata.auxIatCopy);957}958 959void Writer::appendECImportTables() {960 if (!isArm64EC(ctx.config.machine))961 return;962 963 const uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;964 965 // IAT is always placed at the beginning of .rdata section and its size966 // is aligned to 4KB. Insert it here, after all merges all done.967 if (PartialSection *importAddresses = findPartialSection(".idata$5", rdata)) {968 if (!rdataSec->chunks.empty())969 rdataSec->chunks.front()->setAlignment(970 std::max(0x1000u, rdataSec->chunks.front()->getAlignment()));971 iatSize = alignTo(iatSize, 0x1000);972 973 rdataSec->chunks.insert(rdataSec->chunks.begin(),974 importAddresses->chunks.begin(),975 importAddresses->chunks.end());976 rdataSec->contribSections.insert(rdataSec->contribSections.begin(),977 importAddresses);978 }979 980 // The auxiliary IAT is always placed at the end of the .rdata section981 // and is aligned to 4KB.982 if (PartialSection *auxIat = findPartialSection(".idata$9", rdata)) {983 auxIat->chunks.front()->setAlignment(0x1000);984 rdataSec->chunks.insert(rdataSec->chunks.end(), auxIat->chunks.begin(),985 auxIat->chunks.end());986 rdataSec->addContributingPartialSection(auxIat);987 }988 989 if (!delayIdata.getAuxIat().empty()) {990 delayIdata.getAuxIat().front()->setAlignment(0x1000);991 rdataSec->chunks.insert(rdataSec->chunks.end(),992 delayIdata.getAuxIat().begin(),993 delayIdata.getAuxIat().end());994 }995}996 997// Locate the first Chunk and size of the import directory list and the998// IAT.999void Writer::locateImportTables() {1000 uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;1001 1002 if (PartialSection *importDirs = findPartialSection(".idata$2", rdata)) {1003 if (!importDirs->chunks.empty())1004 importTableStart = importDirs->chunks.front();1005 for (Chunk *c : importDirs->chunks)1006 importTableSize += c->getSize();1007 }1008 1009 if (PartialSection *importAddresses = findPartialSection(".idata$5", rdata)) {1010 if (!importAddresses->chunks.empty())1011 iatStart = importAddresses->chunks.front();1012 for (Chunk *c : importAddresses->chunks)1013 iatSize += c->getSize();1014 }1015}1016 1017// Return whether a SectionChunk's suffix (the dollar and any trailing1018// suffix) should be removed and sorted into the main suffixless1019// PartialSection.1020static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name,1021 bool isMinGW) {1022 // On MinGW, comdat groups are formed by putting the comdat group name1023 // after the '$' in the section name. For .eh_frame$<symbol>, that must1024 // still be sorted before the .eh_frame trailer from crtend.o, thus just1025 // strip the section name trailer. For other sections, such as1026 // .tls$$<symbol> (where non-comdat .tls symbols are otherwise stored in1027 // ".tls$"), they must be strictly sorted after .tls. And for the1028 // hypothetical case of comdat .CRT$XCU, we definitely need to keep the1029 // suffix for sorting. Thus, to play it safe, only strip the suffix for1030 // the standard sections.1031 if (!isMinGW)1032 return false;1033 if (!sc || !sc->isCOMDAT())1034 return false;1035 return name.starts_with(".text$") || name.starts_with(".data$") ||1036 name.starts_with(".rdata$") || name.starts_with(".pdata$") ||1037 name.starts_with(".xdata$") || name.starts_with(".eh_frame$");1038}1039 1040void Writer::sortSections() {1041 if (!ctx.config.callGraphProfile.empty()) {1042 DenseMap<const SectionChunk *, int> order =1043 computeCallGraphProfileOrder(ctx);1044 for (auto it : order) {1045 if (DefinedRegular *sym = it.first->sym)1046 ctx.config.order[sym->getName()] = it.second;1047 }1048 }1049 if (!ctx.config.order.empty())1050 for (auto it : partialSections)1051 sortBySectionOrder(it.second->chunks);1052}1053 1054void Writer::calculateStubDependentSizes() {1055 if (ctx.config.dosStub)1056 dosStubSize = alignTo(ctx.config.dosStub->getBufferSize(), 8);1057 else1058 dosStubSize = sizeof(dos_header) + sizeof(dosProgram);1059 1060 coffHeaderOffset = dosStubSize + sizeof(PEMagic);1061 peHeaderOffset = coffHeaderOffset + sizeof(coff_file_header);1062 dataDirOffset64 = peHeaderOffset + sizeof(pe32plus_header);1063}1064 1065// Create output section objects and add them to OutputSections.1066void Writer::createSections() {1067 llvm::TimeTraceScope timeScope("Output sections");1068 // First, create the builtin sections.1069 const uint32_t data = IMAGE_SCN_CNT_INITIALIZED_DATA;1070 const uint32_t bss = IMAGE_SCN_CNT_UNINITIALIZED_DATA;1071 const uint32_t code = IMAGE_SCN_CNT_CODE;1072 const uint32_t discardable = IMAGE_SCN_MEM_DISCARDABLE;1073 const uint32_t r = IMAGE_SCN_MEM_READ;1074 const uint32_t w = IMAGE_SCN_MEM_WRITE;1075 const uint32_t x = IMAGE_SCN_MEM_EXECUTE;1076 1077 SmallDenseMap<std::pair<StringRef, uint32_t>, OutputSection *> sections;1078 auto createSection = [&](StringRef name, uint32_t outChars) {1079 OutputSection *&sec = sections[{name, outChars}];1080 if (!sec) {1081 sec = make<OutputSection>(name, outChars);1082 ctx.outputSections.push_back(sec);1083 }1084 return sec;1085 };1086 1087 // Try to match the section order used by link.exe.1088 textSec = createSection(".text", code | r | x);1089 if (isArm64EC(ctx.config.machine)) {1090 wowthkSec = createSection(".wowthk", code | r | x);1091 hexpthkSec = createSection(".hexpthk", code | r | x);1092 }1093 bssSec = createSection(".bss", bss | r | w);1094 rdataSec = createSection(".rdata", data | r);1095 buildidSec = createSection(".buildid", data | r);1096 cvinfoSec = createSection(".cvinfo", data | r);1097 dataSec = createSection(".data", data | r | w);1098 pdataSec = createSection(".pdata", data | r);1099 idataSec = createSection(".idata", data | r);1100 edataSec = createSection(".edata", data | r);1101 didatSec = createSection(".didat", data | r);1102 if (isArm64EC(ctx.config.machine))1103 a64xrmSec = createSection(".a64xrm", data | r);1104 rsrcSec = createSection(".rsrc", data | r);1105 relocSec = createSection(".reloc", data | discardable | r);1106 ctorsSec = createSection(".ctors", data | r | w);1107 dtorsSec = createSection(".dtors", data | r | w);1108 1109 // Then bin chunks by name and output characteristics.1110 for (Chunk *c : ctx.driver.getChunks()) {1111 auto *sc = dyn_cast<SectionChunk>(c);1112 if (sc && !sc->live) {1113 if (ctx.config.verbose)1114 sc->printDiscardedMessage();1115 continue;1116 }1117 if (auto *cc = dyn_cast<CommonChunk>(c)) {1118 if (!cc->live)1119 continue;1120 }1121 StringRef name = c->getSectionName();1122 if (shouldStripSectionSuffix(sc, name, ctx.config.mingw))1123 name = name.split('$').first;1124 1125 if (name.starts_with(".tls"))1126 tlsAlignment = std::max(tlsAlignment, c->getAlignment());1127 1128 PartialSection *pSec = createPartialSection(name,1129 c->getOutputCharacteristics());1130 pSec->chunks.push_back(c);1131 }1132 1133 fixPartialSectionChars(".rsrc", data | r);1134 fixPartialSectionChars(".edata", data | r);1135 // Even in non MinGW cases, we might need to link against GNU import1136 // libraries.1137 bool hasIdata = fixGnuImportChunks();1138 if (!idata.empty())1139 hasIdata = true;1140 1141 if (hasIdata)1142 addSyntheticIdata();1143 1144 sortSections();1145 1146 if (hasIdata)1147 locateImportTables();1148 1149 for (auto thunk : ctx.symtab.sameAddressThunks)1150 wowthkSec->addChunk(thunk);1151 1152 // Then create an OutputSection for each section.1153 // '$' and all following characters in input section names are1154 // discarded when determining output section. So, .text$foo1155 // contributes to .text, for example. See PE/COFF spec 3.2.1156 for (auto it : partialSections) {1157 PartialSection *pSec = it.second;1158 StringRef name = getOutputSectionName(pSec->name);1159 uint32_t outChars = pSec->characteristics;1160 1161 if (name == ".CRT") {1162 // In link.exe, there is a special case for the I386 target where .CRT1163 // sections are treated as if they have output characteristics DATA | R if1164 // their characteristics are DATA | R | W. This implements the same1165 // special case for all architectures.1166 outChars = data | r;1167 1168 Log(ctx) << "Processing section " << pSec->name << " -> " << name;1169 1170 sortCRTSectionChunks(pSec->chunks);1171 }1172 1173 // ARM64EC has specific placement and alignment requirements for the IAT.1174 // Delay adding its chunks until appendECImportTables.1175 if (isArm64EC(ctx.config.machine) &&1176 (pSec->name == ".idata$5" || pSec->name == ".idata$9"))1177 continue;1178 1179 OutputSection *sec = createSection(name, outChars);1180 for (Chunk *c : pSec->chunks)1181 sec->addChunk(c);1182 1183 sec->addContributingPartialSection(pSec);1184 }1185 1186 if (ctx.hybridSymtab) {1187 if (OutputSection *sec = findSection(".CRT"))1188 sec->splitECChunks();1189 }1190 1191 // Finally, move some output sections to the end.1192 auto sectionOrder = [&](const OutputSection *s) {1193 // Move DISCARDABLE (or non-memory-mapped) sections to the end of file1194 // because the loader cannot handle holes. Stripping can remove other1195 // discardable ones than .reloc, which is first of them (created early).1196 if (s->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) {1197 // Move discardable sections named .debug_ to the end, after other1198 // discardable sections. Stripping only removes the sections named1199 // .debug_* - thus try to avoid leaving holes after stripping.1200 if (s->name.starts_with(".debug_"))1201 return 3;1202 return 2;1203 }1204 // .rsrc should come at the end of the non-discardable sections because its1205 // size may change by the Win32 UpdateResources() function, causing1206 // subsequent sections to move (see https://crbug.com/827082).1207 if (s == rsrcSec)1208 return 1;1209 return 0;1210 };1211 llvm::stable_sort(ctx.outputSections,1212 [&](const OutputSection *s, const OutputSection *t) {1213 return sectionOrder(s) < sectionOrder(t);1214 });1215}1216 1217void Writer::createMiscChunks() {1218 llvm::TimeTraceScope timeScope("Misc chunks");1219 Configuration *config = &ctx.config;1220 1221 for (MergeChunk *p : ctx.mergeChunkInstances) {1222 if (p) {1223 p->finalizeContents();1224 rdataSec->addChunk(p);1225 }1226 }1227 1228 // Create thunks for locally-dllimported symbols.1229 ctx.forEachSymtab([&](SymbolTable &symtab) {1230 if (!symtab.localImportChunks.empty()) {1231 for (Chunk *c : symtab.localImportChunks)1232 rdataSec->addChunk(c);1233 }1234 });1235 1236 // Create Debug Information Chunks1237 if (config->mingw) {1238 debugInfoSec = buildidSec;1239 } else if (!config->mergeDebugDirectory) {1240 debugInfoSec = cvinfoSec;1241 } else {1242 debugInfoSec = rdataSec;1243 }1244 if (config->buildIDHash != BuildIDHash::None || config->debug ||1245 config->repro || config->cetCompat || config->cetCompatStrict ||1246 config->cetCompatIpValidationRelaxed ||1247 config->cetCompatDynamicApisInProcOnly || config->hotpatchCompat) {1248 debugDirectory =1249 make<DebugDirectoryChunk>(ctx, debugRecords, config->repro);1250 debugDirectory->setAlignment(4);1251 debugInfoSec->addChunk(debugDirectory);1252 }1253 1254 if (config->debug || config->buildIDHash != BuildIDHash::None) {1255 // Make a CVDebugRecordChunk even when /DEBUG:CV is not specified. We1256 // output a PDB no matter what, and this chunk provides the only means of1257 // allowing a debugger to match a PDB and an executable. So we need it even1258 // if we're ultimately not going to write CodeView data to the PDB.1259 buildId = make<CVDebugRecordChunk>(ctx);1260 debugRecords.emplace_back(COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId);1261 ctx.forEachSymtab([&](SymbolTable &symtab) {1262 if (Symbol *buildidSym = symtab.findUnderscore("__buildid"))1263 replaceSymbol<DefinedSynthetic>(buildidSym, buildidSym->getName(),1264 buildId, 4);1265 });1266 }1267 1268 uint16_t ex_characteristics_flags = 0;1269 if (config->cetCompat)1270 ex_characteristics_flags |= IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT;1271 if (config->cetCompatStrict)1272 ex_characteristics_flags |=1273 IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE;1274 if (config->cetCompatIpValidationRelaxed)1275 ex_characteristics_flags |=1276 IMAGE_DLL_CHARACTERISTICS_EX_CET_SET_CONTEXT_IP_VALIDATION_RELAXED_MODE;1277 if (config->cetCompatDynamicApisInProcOnly)1278 ex_characteristics_flags |=1279 IMAGE_DLL_CHARACTERISTICS_EX_CET_DYNAMIC_APIS_ALLOW_IN_PROC_ONLY;1280 if (config->hotpatchCompat)1281 ex_characteristics_flags |=1282 IMAGE_DLL_CHARACTERISTICS_EX_HOTPATCH_COMPATIBLE;1283 1284 if (ex_characteristics_flags) {1285 debugRecords.emplace_back(1286 COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS,1287 make<ExtendedDllCharacteristicsChunk>(ex_characteristics_flags));1288 }1289 1290 // Align and add each chunk referenced by the debug data directory.1291 for (std::pair<COFF::DebugType, Chunk *> r : debugRecords) {1292 r.second->setAlignment(4);1293 debugInfoSec->addChunk(r.second);1294 }1295 1296 // Create SEH table. x86-only.1297 if (config->safeSEH)1298 createSEHTable();1299 1300 // Create /guard:cf tables if requested.1301 createGuardCFTables();1302 1303 createECChunks();1304 1305 if (config->autoImport)1306 createRuntimePseudoRelocs();1307 1308 if (config->mingw) {1309 insertCtorDtorSymbols();1310 insertBssDataStartEndSymbols();1311 }1312}1313 1314// Create .idata section for the DLL-imported symbol table.1315// The format of this section is inherently Windows-specific.1316// IdataContents class abstracted away the details for us,1317// so we just let it create chunks and add them to the section.1318void Writer::createImportTables() {1319 llvm::TimeTraceScope timeScope("Import tables");1320 // Initialize DLLOrder so that import entries are ordered in1321 // the same order as in the command line. (That affects DLL1322 // initialization order, and this ordering is MSVC-compatible.)1323 for (ImportFile *file : ctx.importFileInstances) {1324 if (!file->live)1325 continue;1326 1327 std::string dll = StringRef(file->dllName).lower();1328 ctx.config.dllOrder.try_emplace(dll, ctx.config.dllOrder.size());1329 1330 if (file->impSym && !isa<DefinedImportData>(file->impSym))1331 Fatal(ctx) << file->symtab.printSymbol(file->impSym) << " was replaced";1332 DefinedImportData *impSym = cast_or_null<DefinedImportData>(file->impSym);1333 if (ctx.config.delayLoads.count(StringRef(file->dllName).lower())) {1334 if (!file->thunkSym)1335 Fatal(ctx) << "cannot delay-load " << toString(file)1336 << " due to import of data: "1337 << file->symtab.printSymbol(impSym);1338 delayIdata.add(impSym);1339 } else {1340 idata.add(impSym);1341 }1342 }1343}1344 1345void Writer::appendImportThunks() {1346 if (ctx.importFileInstances.empty())1347 return;1348 1349 llvm::TimeTraceScope timeScope("Import thunks");1350 for (ImportFile *file : ctx.importFileInstances) {1351 if (!file->live)1352 continue;1353 1354 if (file->thunkSym) {1355 if (!isa<DefinedImportThunk>(file->thunkSym))1356 Fatal(ctx) << file->symtab.printSymbol(file->thunkSym)1357 << " was replaced";1358 auto *chunk = cast<DefinedImportThunk>(file->thunkSym)->getChunk();1359 if (chunk->live)1360 textSec->addChunk(chunk);1361 }1362 1363 if (file->auxThunkSym) {1364 if (!isa<DefinedImportThunk>(file->auxThunkSym))1365 Fatal(ctx) << file->symtab.printSymbol(file->auxThunkSym)1366 << " was replaced";1367 auto *chunk = cast<DefinedImportThunk>(file->auxThunkSym)->getChunk();1368 if (chunk->live)1369 textSec->addChunk(chunk);1370 }1371 1372 if (file->impchkThunk)1373 textSec->addChunk(file->impchkThunk);1374 }1375 1376 if (!delayIdata.empty()) {1377 delayIdata.create();1378 for (Chunk *c : delayIdata.getChunks())1379 didatSec->addChunk(c);1380 for (Chunk *c : delayIdata.getDataChunks())1381 dataSec->addChunk(c);1382 for (Chunk *c : delayIdata.getCodeChunks())1383 textSec->addChunk(c);1384 for (Chunk *c : delayIdata.getCodePData())1385 pdataSec->addChunk(c);1386 for (Chunk *c : delayIdata.getAuxIatCopy())1387 rdataSec->addChunk(c);1388 for (Chunk *c : delayIdata.getCodeUnwindInfo())1389 rdataSec->addChunk(c);1390 }1391}1392 1393void Writer::createExportTable() {1394 llvm::TimeTraceScope timeScope("Export table");1395 if (!edataSec->chunks.empty()) {1396 // Allow using a custom built export table from input object files, instead1397 // of having the linker synthesize the tables.1398 if (!ctx.hybridSymtab) {1399 ctx.symtab.edataStart = edataSec->chunks.front();1400 ctx.symtab.edataEnd = edataSec->chunks.back();1401 } else {1402 // On hybrid target, split EC and native chunks.1403 llvm::stable_sort(edataSec->chunks, [=](const Chunk *a, const Chunk *b) {1404 return (a->getMachine() != ARM64) < (b->getMachine() != ARM64);1405 });1406 1407 for (auto chunk : edataSec->chunks) {1408 if (chunk->getMachine() != ARM64) {1409 ctx.symtab.edataStart = chunk;1410 ctx.symtab.edataEnd = edataSec->chunks.back();1411 break;1412 }1413 1414 if (!ctx.hybridSymtab->edataStart)1415 ctx.hybridSymtab->edataStart = chunk;1416 ctx.hybridSymtab->edataEnd = chunk;1417 }1418 }1419 }1420 ctx.forEachActiveSymtab([&](SymbolTable &symtab) {1421 if (symtab.edataStart) {1422 if (symtab.hadExplicitExports)1423 Warn(ctx) << "literal .edata sections override exports";1424 } else if (!symtab.exports.empty()) {1425 std::vector<Chunk *> edataChunks;1426 createEdataChunks(symtab, edataChunks);1427 for (Chunk *c : edataChunks)1428 edataSec->addChunk(c);1429 symtab.edataStart = edataChunks.front();1430 symtab.edataEnd = edataChunks.back();1431 }1432 1433 // Warn on exported deleting destructor.1434 for (auto e : symtab.exports)1435 if (e.sym && e.sym->getName().starts_with("??_G"))1436 Warn(ctx) << "export of deleting dtor: " << toString(ctx, *e.sym);1437 });1438}1439 1440void Writer::removeUnusedSections() {1441 llvm::TimeTraceScope timeScope("Remove unused sections");1442 // Remove sections that we can be sure won't get content, to avoid1443 // allocating space for their section headers.1444 auto isUnused = [this](OutputSection *s) {1445 if (s == relocSec)1446 return false; // This section is populated later.1447 // MergeChunks have zero size at this point, as their size is finalized1448 // later. Only remove sections that have no Chunks at all.1449 return s->chunks.empty();1450 };1451 llvm::erase_if(ctx.outputSections, isUnused);1452}1453 1454void Writer::layoutSections() {1455 llvm::TimeTraceScope timeScope("Layout sections");1456 if (ctx.config.sectionOrder.empty())1457 return;1458 1459 llvm::stable_sort(ctx.outputSections,1460 [this](const OutputSection *a, const OutputSection *b) {1461 auto itA = ctx.config.sectionOrder.find(a->name.str());1462 auto itB = ctx.config.sectionOrder.find(b->name.str());1463 bool aInOrder = itA != ctx.config.sectionOrder.end();1464 bool bInOrder = itB != ctx.config.sectionOrder.end();1465 1466 // Put unspecified sections after all specified sections1467 if (aInOrder && bInOrder) {1468 return itA->second < itB->second;1469 } else if (aInOrder && !bInOrder) {1470 return true; // ordered sections come before unordered1471 } else {1472 // (!aInOrder && bInOrder): unordered comes after1473 // ordered1474 // (!aInOrder && !bInOrder): both unspecified, preserve1475 // the original order1476 return false;1477 }1478 });1479}1480 1481// The Windows loader doesn't seem to like empty sections,1482// so we remove them if any.1483void Writer::removeEmptySections() {1484 llvm::TimeTraceScope timeScope("Remove empty sections");1485 auto isEmpty = [](OutputSection *s) { return s->getVirtualSize() == 0; };1486 llvm::erase_if(ctx.outputSections, isEmpty);1487}1488 1489void Writer::assignOutputSectionIndices() {1490 llvm::TimeTraceScope timeScope("Output sections indices");1491 // Assign final output section indices, and assign each chunk to its output1492 // section.1493 uint32_t idx = 1;1494 for (OutputSection *os : ctx.outputSections) {1495 os->sectionIndex = idx;1496 for (Chunk *c : os->chunks)1497 c->setOutputSectionIdx(idx);1498 ++idx;1499 }1500 1501 // Merge chunks are containers of chunks, so assign those an output section1502 // too.1503 for (MergeChunk *mc : ctx.mergeChunkInstances)1504 if (mc)1505 for (SectionChunk *sc : mc->sections)1506 if (sc && sc->live)1507 sc->setOutputSectionIdx(mc->getOutputSectionIdx());1508}1509 1510std::optional<coff_symbol16> Writer::createSymbol(Defined *def) {1511 coff_symbol16 sym;1512 switch (def->kind()) {1513 case Symbol::DefinedAbsoluteKind: {1514 auto *da = dyn_cast<DefinedAbsolute>(def);1515 // Note: COFF symbol can only store 32-bit values, so 64-bit absolute1516 // values will be truncated.1517 sym.Value = da->getVA();1518 sym.SectionNumber = IMAGE_SYM_ABSOLUTE;1519 break;1520 }1521 default: {1522 // Don't write symbols that won't be written to the output to the symbol1523 // table.1524 // We also try to write DefinedSynthetic as a normal symbol. Some of these1525 // symbols do point to an actual chunk, like __safe_se_handler_table. Others1526 // like __ImageBase are outside of sections and thus cannot be represented.1527 Chunk *c = def->getChunk();1528 if (!c)1529 return std::nullopt;1530 OutputSection *os = ctx.getOutputSection(c);1531 if (!os)1532 return std::nullopt;1533 1534 sym.Value = def->getRVA() - os->getRVA();1535 sym.SectionNumber = os->sectionIndex;1536 break;1537 }1538 }1539 1540 // Symbols that are runtime pseudo relocations don't point to the actual1541 // symbol data itself (as they are imported), but points to the IAT entry1542 // instead. Avoid emitting them to the symbol table, as they can confuse1543 // debuggers.1544 if (def->isRuntimePseudoReloc)1545 return std::nullopt;1546 1547 StringRef name = def->getName();1548 if (name.size() > COFF::NameSize) {1549 sym.Name.Offset.Zeroes = 0;1550 sym.Name.Offset.Offset = 0; // Filled in later.1551 strtab.add(name);1552 } else {1553 memset(sym.Name.ShortName, 0, COFF::NameSize);1554 memcpy(sym.Name.ShortName, name.data(), name.size());1555 }1556 1557 if (auto *d = dyn_cast<DefinedCOFF>(def)) {1558 COFFSymbolRef ref = d->getCOFFSymbol();1559 sym.Type = ref.getType();1560 sym.StorageClass = ref.getStorageClass();1561 } else if (def->kind() == Symbol::DefinedImportThunkKind) {1562 sym.Type = (IMAGE_SYM_DTYPE_FUNCTION << SCT_COMPLEX_TYPE_SHIFT) |1563 IMAGE_SYM_TYPE_NULL;1564 sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL;1565 } else {1566 sym.Type = IMAGE_SYM_TYPE_NULL;1567 sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL;1568 }1569 sym.NumberOfAuxSymbols = 0;1570 return sym;1571}1572 1573void Writer::createSymbolAndStringTable() {1574 llvm::TimeTraceScope timeScope("Symbol and string table");1575 // PE/COFF images are limited to 8 byte section names. Longer names can be1576 // supported by writing a non-standard string table, but this string table is1577 // not mapped at runtime and the long names will therefore be inaccessible.1578 // link.exe always truncates section names to 8 bytes, whereas binutils always1579 // preserves long section names via the string table. LLD adopts a hybrid1580 // solution where discardable sections have long names preserved and1581 // non-discardable sections have their names truncated, to ensure that any1582 // section which is mapped at runtime also has its name mapped at runtime.1583 SmallVector<OutputSection *> longNameSections;1584 for (OutputSection *sec : ctx.outputSections) {1585 if (sec->name.size() <= COFF::NameSize)1586 continue;1587 if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0)1588 continue;1589 if (ctx.config.warnLongSectionNames) {1590 Warn(ctx)1591 << "section name " << sec->name1592 << " is longer than 8 characters and will use a non-standard string "1593 "table";1594 }1595 // Put the section name in the begin of strtab so that its offset is less1596 // than Max7DecimalOffset otherwise lldb/gdb will not read it.1597 strtab.add(sec->name, /*Priority=*/UINT8_MAX);1598 longNameSections.push_back(sec);1599 }1600 1601 std::vector<std::pair<size_t, StringRef>> longNameSymbols;1602 if (ctx.config.writeSymtab) {1603 for (ObjFile *file : ctx.objFileInstances) {1604 for (Symbol *b : file->getSymbols()) {1605 auto *d = dyn_cast_or_null<Defined>(b);1606 if (!d || d->writtenToSymtab)1607 continue;1608 d->writtenToSymtab = true;1609 if (auto *dc = dyn_cast_or_null<DefinedCOFF>(d)) {1610 COFFSymbolRef symRef = dc->getCOFFSymbol();1611 if (symRef.isSectionDefinition() ||1612 symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_LABEL)1613 continue;1614 }1615 1616 if (std::optional<coff_symbol16> sym = createSymbol(d)) {1617 if (d->getName().size() > COFF::NameSize)1618 longNameSymbols.emplace_back(outputSymtab.size(), d->getName());1619 outputSymtab.push_back(*sym);1620 }1621 1622 if (auto *dthunk = dyn_cast<DefinedImportThunk>(d)) {1623 if (!dthunk->wrappedSym->writtenToSymtab) {1624 dthunk->wrappedSym->writtenToSymtab = true;1625 if (std::optional<coff_symbol16> sym =1626 createSymbol(dthunk->wrappedSym)) {1627 if (dthunk->wrappedSym->getName().size() > COFF::NameSize)1628 longNameSymbols.emplace_back(outputSymtab.size(),1629 dthunk->wrappedSym->getName());1630 outputSymtab.push_back(*sym);1631 }1632 }1633 }1634 }1635 }1636 }1637 1638 if (outputSymtab.empty() && strtab.empty())1639 return;1640 1641 strtab.finalize();1642 for (OutputSection *sec : longNameSections)1643 sec->setStringTableOff(strtab.getOffset(sec->name));1644 for (auto P : longNameSymbols) {1645 coff_symbol16 &sym = outputSymtab[P.first];1646 sym.Name.Offset.Offset = strtab.getOffset(P.second);1647 }1648 1649 // We position the symbol table to be adjacent to the end of the last section.1650 uint64_t fileOff = fileSize;1651 pointerToSymbolTable = fileOff;1652 fileOff += outputSymtab.size() * sizeof(coff_symbol16);1653 fileOff += strtab.getSize();1654 fileSize = alignTo(fileOff, ctx.config.fileAlign);1655}1656 1657void Writer::mergeSection(const std::map<StringRef, StringRef>::value_type &p) {1658 StringRef toName = p.second;1659 if (p.first == toName)1660 return;1661 StringSet<> names;1662 while (true) {1663 if (!names.insert(toName).second)1664 Fatal(ctx) << "/merge: cycle found for section '" << p.first << "'";1665 auto i = ctx.config.merge.find(toName);1666 if (i == ctx.config.merge.end())1667 break;1668 toName = i->second;1669 }1670 OutputSection *from = findSection(p.first);1671 OutputSection *to = findSection(toName);1672 if (!from)1673 return;1674 if (!to) {1675 from->name = toName;1676 return;1677 }1678 to->merge(from);1679}1680 1681void Writer::mergeSections() {1682 llvm::TimeTraceScope timeScope("Merge sections");1683 if (!pdataSec->chunks.empty()) {1684 if (isArm64EC(ctx.config.machine)) {1685 // On ARM64EC .pdata may contain both ARM64 and X64 data. Split them by1686 // sorting and store their regions separately.1687 llvm::stable_sort(pdataSec->chunks, [=](const Chunk *a, const Chunk *b) {1688 return (a->getMachine() == AMD64) < (b->getMachine() == AMD64);1689 });1690 1691 for (auto chunk : pdataSec->chunks) {1692 if (chunk->getMachine() == AMD64) {1693 hybridPdata.first = chunk;1694 hybridPdata.last = pdataSec->chunks.back();1695 break;1696 }1697 1698 if (!pdata.first)1699 pdata.first = chunk;1700 pdata.last = chunk;1701 }1702 } else {1703 pdata.first = pdataSec->chunks.front();1704 pdata.last = pdataSec->chunks.back();1705 }1706 }1707 1708 for (auto &p : ctx.config.merge) {1709 if (p.first != ".bss")1710 mergeSection(p);1711 }1712 1713 // Because .bss contains all zeros, it should be merged at the end of1714 // whatever section it is being merged into (usually .data) so that the image1715 // need not actually contain all of the zeros.1716 auto it = ctx.config.merge.find(".bss");1717 if (it != ctx.config.merge.end())1718 mergeSection(*it);1719}1720 1721// EC targets may have chunks of various architectures mixed together at this1722// point. Group code chunks of the same architecture together by sorting chunks1723// by their EC range type.1724void Writer::sortECChunks() {1725 if (!isArm64EC(ctx.config.machine))1726 return;1727 1728 for (OutputSection *sec : ctx.outputSections) {1729 if (sec->isCodeSection())1730 llvm::stable_sort(sec->chunks, [=](const Chunk *a, const Chunk *b) {1731 std::optional<chpe_range_type> aType = a->getArm64ECRangeType(),1732 bType = b->getArm64ECRangeType();1733 return bType && (!aType || *aType < *bType);1734 });1735 }1736}1737 1738// Visits all sections to assign incremental, non-overlapping RVAs and1739// file offsets.1740void Writer::assignAddresses() {1741 llvm::TimeTraceScope timeScope("Assign addresses");1742 Configuration *config = &ctx.config;1743 1744 // We need to create EC code map so that ECCodeMapChunk knows its size.1745 // We do it here to make sure that we account for range extension chunks.1746 createECCodeMap();1747 1748 sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +1749 sizeof(data_directory) * numberOfDataDirectory +1750 sizeof(coff_section) * ctx.outputSections.size();1751 sizeOfHeaders +=1752 config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header);1753 sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign);1754 fileSize = sizeOfHeaders;1755 1756 // The first page is kept unmapped.1757 uint64_t rva = alignTo(sizeOfHeaders, config->align);1758 1759 for (OutputSection *sec : ctx.outputSections) {1760 llvm::TimeTraceScope timeScope("Section: ", sec->name);1761 if (sec == relocSec) {1762 sec->chunks.clear();1763 addBaserels();1764 if (ctx.dynamicRelocs) {1765 ctx.dynamicRelocs->finalize();1766 relocSec->addChunk(ctx.dynamicRelocs);1767 }1768 }1769 uint64_t rawSize = 0, virtualSize = 0;1770 sec->header.VirtualAddress = rva;1771 1772 // If /FUNCTIONPADMIN is used, functions are padded in order to create a1773 // hotpatchable image.1774 uint32_t padding = sec->isCodeSection() ? config->functionPadMin : 0;1775 std::optional<chpe_range_type> prevECRange;1776 1777 for (Chunk *c : sec->chunks) {1778 // Alignment EC code range baudaries.1779 if (isArm64EC(ctx.config.machine) && sec->isCodeSection()) {1780 std::optional<chpe_range_type> rangeType = c->getArm64ECRangeType();1781 if (rangeType != prevECRange) {1782 virtualSize = alignTo(virtualSize, 4096);1783 prevECRange = rangeType;1784 }1785 }1786 if (padding && c->isHotPatchable())1787 virtualSize += padding;1788 // If chunk has EC entry thunk, reserve a space for an offset to the1789 // thunk.1790 if (c->getEntryThunk())1791 virtualSize += sizeof(uint32_t);1792 virtualSize = alignTo(virtualSize, c->getAlignment());1793 c->setRVA(rva + virtualSize);1794 virtualSize += c->getSize();1795 if (c->hasData)1796 rawSize = alignTo(virtualSize, config->fileAlign);1797 }1798 if (virtualSize > UINT32_MAX)1799 Err(ctx) << "section larger than 4 GiB: " << sec->name;1800 sec->header.VirtualSize = virtualSize;1801 sec->header.SizeOfRawData = rawSize;1802 if (rawSize != 0)1803 sec->header.PointerToRawData = fileSize;1804 rva += alignTo(virtualSize, config->align);1805 fileSize += alignTo(rawSize, config->fileAlign);1806 }1807 sizeOfImage = alignTo(rva, config->align);1808 1809 // Assign addresses to sections in MergeChunks.1810 for (MergeChunk *mc : ctx.mergeChunkInstances)1811 if (mc)1812 mc->assignSubsectionRVAs();1813}1814 1815template <typename PEHeaderTy> void Writer::writeHeader() {1816 // Write DOS header. For backwards compatibility, the first part of a PE/COFF1817 // executable consists of an MS-DOS MZ executable. If the executable is run1818 // under DOS, that program gets run (usually to just print an error message).1819 // When run under Windows, the loader looks at AddressOfNewExeHeader and uses1820 // the PE header instead.1821 Configuration *config = &ctx.config;1822 1823 uint8_t *buf = buffer->getBufferStart();1824 auto *dos = reinterpret_cast<dos_header *>(buf);1825 1826 // Write DOS program.1827 if (config->dosStub) {1828 memcpy(buf, config->dosStub->getBufferStart(),1829 config->dosStub->getBufferSize());1830 // MS link.exe accepts an invalid `e_lfanew` (AddressOfNewExeHeader) and1831 // updates it automatically. Replicate the same behaviour.1832 dos->AddressOfNewExeHeader = alignTo(config->dosStub->getBufferSize(), 8);1833 // Unlike MS link.exe, LLD accepts non-8-byte-aligned stubs.1834 // In that case, we add zero paddings ourselves.1835 buf += alignTo(config->dosStub->getBufferSize(), 8);1836 } else {1837 buf += sizeof(dos_header);1838 dos->Magic[0] = 'M';1839 dos->Magic[1] = 'Z';1840 dos->UsedBytesInTheLastPage = dosStubSize % 512;1841 dos->FileSizeInPages = divideCeil(dosStubSize, 512);1842 dos->HeaderSizeInParagraphs = sizeof(dos_header) / 16;1843 1844 dos->AddressOfRelocationTable = sizeof(dos_header);1845 dos->AddressOfNewExeHeader = dosStubSize;1846 1847 memcpy(buf, dosProgram, sizeof(dosProgram));1848 buf += sizeof(dosProgram);1849 }1850 1851 // Make sure DOS stub is aligned to 8 bytes at this point1852 assert((buf - buffer->getBufferStart()) % 8 == 0);1853 1854 // Write PE magic1855 memcpy(buf, PEMagic, sizeof(PEMagic));1856 buf += sizeof(PEMagic);1857 1858 // Write COFF header1859 assert(coffHeaderOffset == buf - buffer->getBufferStart());1860 auto *coff = reinterpret_cast<coff_file_header *>(buf);1861 buf += sizeof(*coff);1862 SymbolTable &symtab =1863 ctx.config.machine == ARM64X ? *ctx.hybridSymtab : ctx.symtab;1864 coff->Machine = symtab.isEC() ? AMD64 : symtab.machine;1865 coff->NumberOfSections = ctx.outputSections.size();1866 coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE;1867 if (config->largeAddressAware)1868 coff->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE;1869 if (!config->is64())1870 coff->Characteristics |= IMAGE_FILE_32BIT_MACHINE;1871 if (config->dll)1872 coff->Characteristics |= IMAGE_FILE_DLL;1873 if (config->driverUponly)1874 coff->Characteristics |= IMAGE_FILE_UP_SYSTEM_ONLY;1875 if (!config->relocatable)1876 coff->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED;1877 if (config->swaprunCD)1878 coff->Characteristics |= IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP;1879 if (config->swaprunNet)1880 coff->Characteristics |= IMAGE_FILE_NET_RUN_FROM_SWAP;1881 coff->SizeOfOptionalHeader =1882 sizeof(PEHeaderTy) + sizeof(data_directory) * numberOfDataDirectory;1883 1884 // Write PE header1885 assert(peHeaderOffset == buf - buffer->getBufferStart());1886 auto *pe = reinterpret_cast<PEHeaderTy *>(buf);1887 buf += sizeof(*pe);1888 pe->Magic = config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32;1889 1890 // If {Major,Minor}LinkerVersion is left at 0.0, then for some1891 // reason signing the resulting PE file with Authenticode produces a1892 // signature that fails to validate on Windows 7 (but is OK on 10).1893 // Set it to 14.0, which is what VS2015 outputs, and which avoids1894 // that problem.1895 pe->MajorLinkerVersion = 14;1896 pe->MinorLinkerVersion = 0;1897 1898 pe->ImageBase = config->imageBase;1899 pe->SectionAlignment = config->align;1900 pe->FileAlignment = config->fileAlign;1901 pe->MajorImageVersion = config->majorImageVersion;1902 pe->MinorImageVersion = config->minorImageVersion;1903 pe->MajorOperatingSystemVersion = config->majorOSVersion;1904 pe->MinorOperatingSystemVersion = config->minorOSVersion;1905 pe->MajorSubsystemVersion = config->majorSubsystemVersion;1906 pe->MinorSubsystemVersion = config->minorSubsystemVersion;1907 pe->Subsystem = config->subsystem;1908 pe->SizeOfImage = sizeOfImage;1909 pe->SizeOfHeaders = sizeOfHeaders;1910 if (!config->noEntry) {1911 Defined *entry = cast<Defined>(symtab.entry);1912 pe->AddressOfEntryPoint = entry->getRVA();1913 // Pointer to thumb code must have the LSB set, so adjust it.1914 if (config->machine == ARMNT)1915 pe->AddressOfEntryPoint |= 1;1916 }1917 pe->SizeOfStackReserve = config->stackReserve;1918 pe->SizeOfStackCommit = config->stackCommit;1919 pe->SizeOfHeapReserve = config->heapReserve;1920 pe->SizeOfHeapCommit = config->heapCommit;1921 if (config->appContainer)1922 pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER;1923 if (config->driverWdm)1924 pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_WDM_DRIVER;1925 if (config->dynamicBase)1926 pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;1927 if (config->highEntropyVA)1928 pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA;1929 if (!config->allowBind)1930 pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_BIND;1931 if (config->nxCompat)1932 pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;1933 if (!config->allowIsolation)1934 pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;1935 if (config->guardCF != GuardCFLevel::Off)1936 pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF;1937 if (config->integrityCheck)1938 pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY;1939 if (setNoSEHCharacteristic || config->noSEH)1940 pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH;1941 if (config->terminalServerAware)1942 pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;1943 pe->NumberOfRvaAndSize = numberOfDataDirectory;1944 if (textSec->getVirtualSize()) {1945 pe->BaseOfCode = textSec->getRVA();1946 pe->SizeOfCode = textSec->getRawSize();1947 }1948 pe->SizeOfInitializedData = getSizeOfInitializedData();1949 1950 // Write data directory1951 assert(!ctx.config.is64() ||1952 dataDirOffset64 == buf - buffer->getBufferStart());1953 auto *dir = reinterpret_cast<data_directory *>(buf);1954 buf += sizeof(*dir) * numberOfDataDirectory;1955 if (symtab.edataStart) {1956 dir[EXPORT_TABLE].RelativeVirtualAddress = symtab.edataStart->getRVA();1957 dir[EXPORT_TABLE].Size = symtab.edataEnd->getRVA() +1958 symtab.edataEnd->getSize() -1959 symtab.edataStart->getRVA();1960 }1961 if (importTableStart) {1962 dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA();1963 dir[IMPORT_TABLE].Size = importTableSize;1964 }1965 if (iatStart) {1966 dir[IAT].RelativeVirtualAddress = iatStart->getRVA();1967 dir[IAT].Size = iatSize;1968 }1969 if (rsrcSec->getVirtualSize()) {1970 dir[RESOURCE_TABLE].RelativeVirtualAddress = rsrcSec->getRVA();1971 dir[RESOURCE_TABLE].Size = rsrcSec->getVirtualSize();1972 }1973 // ARM64EC (but not ARM64X) contains x86_64 exception table in data directory.1974 ChunkRange &exceptionTable =1975 ctx.config.machine == ARM64EC ? hybridPdata : pdata;1976 if (exceptionTable.first) {1977 dir[EXCEPTION_TABLE].RelativeVirtualAddress =1978 exceptionTable.first->getRVA();1979 dir[EXCEPTION_TABLE].Size = exceptionTable.last->getRVA() +1980 exceptionTable.last->getSize() -1981 exceptionTable.first->getRVA();1982 }1983 size_t relocSize = relocSec->getVirtualSize();1984 if (ctx.dynamicRelocs)1985 relocSize -= ctx.dynamicRelocs->getSize();1986 if (relocSize) {1987 dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA();1988 dir[BASE_RELOCATION_TABLE].Size = relocSize;1989 }1990 if (Symbol *sym = symtab.findUnderscore("_tls_used")) {1991 if (Defined *b = dyn_cast<Defined>(sym)) {1992 dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA();1993 dir[TLS_TABLE].Size = config->is64()1994 ? sizeof(object::coff_tls_directory64)1995 : sizeof(object::coff_tls_directory32);1996 }1997 }1998 if (debugDirectory) {1999 dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA();2000 dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize();2001 }2002 if (symtab.loadConfigSym) {2003 dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress =2004 symtab.loadConfigSym->getRVA();2005 dir[LOAD_CONFIG_TABLE].Size = symtab.loadConfigSize;2006 }2007 if (!delayIdata.empty()) {2008 dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress =2009 delayIdata.getDirRVA();2010 dir[DELAY_IMPORT_DESCRIPTOR].Size = delayIdata.getDirSize();2011 }2012 2013 // Write section table2014 for (OutputSection *sec : ctx.outputSections) {2015 sec->writeHeaderTo(buf, config->debug);2016 buf += sizeof(coff_section);2017 }2018 sectionTable = ArrayRef<uint8_t>(2019 buf - ctx.outputSections.size() * sizeof(coff_section), buf);2020 2021 if (outputSymtab.empty() && strtab.empty())2022 return;2023 2024 coff->PointerToSymbolTable = pointerToSymbolTable;2025 uint32_t numberOfSymbols = outputSymtab.size();2026 coff->NumberOfSymbols = numberOfSymbols;2027 auto *symbolTable = reinterpret_cast<coff_symbol16 *>(2028 buffer->getBufferStart() + coff->PointerToSymbolTable);2029 for (size_t i = 0; i != numberOfSymbols; ++i)2030 symbolTable[i] = outputSymtab[i];2031 // Create the string table, it follows immediately after the symbol table.2032 // The first 4 bytes is length including itself.2033 buf = reinterpret_cast<uint8_t *>(&symbolTable[numberOfSymbols]);2034 strtab.write(buf);2035}2036 2037void Writer::openFile(StringRef path) {2038 buffer = CHECK(2039 FileOutputBuffer::create(path, fileSize, FileOutputBuffer::F_executable),2040 "failed to open " + path);2041}2042 2043void Writer::createSEHTable() {2044 SymbolRVASet handlers;2045 for (ObjFile *file : ctx.objFileInstances) {2046 if (!file->hasSafeSEH())2047 Err(ctx) << "/safeseh: " << file->getName()2048 << " is not compatible with SEH";2049 markSymbolsForRVATable(file, file->getSXDataChunks(), handlers);2050 }2051 2052 // Set the "no SEH" characteristic if there really were no handlers, or if2053 // there is no load config object to point to the table of handlers.2054 setNoSEHCharacteristic =2055 handlers.empty() || !ctx.symtab.findUnderscore("_load_config_used");2056 2057 maybeAddRVATable(std::move(handlers), "__safe_se_handler_table",2058 "__safe_se_handler_count");2059}2060 2061// Add a symbol to an RVA set. Two symbols may have the same RVA, but an RVA set2062// cannot contain duplicates. Therefore, the set is uniqued by Chunk and the2063// symbol's offset into that Chunk.2064static void addSymbolToRVASet(SymbolRVASet &rvaSet, Defined *s) {2065 Chunk *c = s->getChunk();2066 if (!c)2067 return;2068 if (auto *sc = dyn_cast<SectionChunk>(c))2069 c = sc->repl; // Look through ICF replacement.2070 uint32_t off = s->getRVA() - (c ? c->getRVA() : 0);2071 rvaSet.insert({c, off});2072}2073 2074// Given a symbol, add it to the GFIDs table if it is a live, defined, function2075// symbol in an executable section.2076static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms,2077 Symbol *s) {2078 if (!s)2079 return;2080 2081 switch (s->kind()) {2082 case Symbol::DefinedLocalImportKind:2083 case Symbol::DefinedImportDataKind:2084 // Defines an __imp_ pointer, so it is data, so it is ignored.2085 break;2086 case Symbol::DefinedCommonKind:2087 // Common is always data, so it is ignored.2088 break;2089 case Symbol::DefinedAbsoluteKind:2090 // Absolute is never code, synthetic generally isn't and usually isn't2091 // determinable.2092 break;2093 case Symbol::DefinedSyntheticKind:2094 // For EC export thunks, mark both the thunk itself and its target.2095 if (auto expChunk = dyn_cast_or_null<ECExportThunkChunk>(2096 cast<Defined>(s)->getChunk())) {2097 addSymbolToRVASet(addressTakenSyms, cast<Defined>(s));2098 addSymbolToRVASet(addressTakenSyms, expChunk->target);2099 }2100 break;2101 case Symbol::LazyArchiveKind:2102 case Symbol::LazyObjectKind:2103 case Symbol::LazyDLLSymbolKind:2104 case Symbol::UndefinedKind:2105 // Undefined symbols resolve to zero, so they don't have an RVA. Lazy2106 // symbols shouldn't have relocations.2107 break;2108 2109 case Symbol::DefinedImportThunkKind:2110 // Thunks are always code, include them.2111 addSymbolToRVASet(addressTakenSyms, cast<Defined>(s));2112 break;2113 2114 case Symbol::DefinedRegularKind: {2115 // This is a regular, defined, symbol from a COFF file. Mark the symbol as2116 // address taken if the symbol type is function and it's in an executable2117 // section.2118 auto *d = cast<DefinedRegular>(s);2119 if (d->getCOFFSymbol().getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION) {2120 SectionChunk *sc = dyn_cast<SectionChunk>(d->getChunk());2121 if (sc && sc->live &&2122 sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)2123 addSymbolToRVASet(addressTakenSyms, d);2124 }2125 break;2126 }2127 }2128}2129 2130// Visit all relocations from all section contributions of this object file and2131// mark the relocation target as address-taken.2132void Writer::markSymbolsWithRelocations(ObjFile *file,2133 SymbolRVASet &usedSymbols) {2134 for (Chunk *c : file->getChunks()) {2135 // We only care about live section chunks. Common chunks and other chunks2136 // don't generally contain relocations.2137 SectionChunk *sc = dyn_cast<SectionChunk>(c);2138 if (!sc || !sc->live)2139 continue;2140 2141 for (const coff_relocation &reloc : sc->getRelocs()) {2142 if (ctx.config.machine == I386 &&2143 reloc.Type == COFF::IMAGE_REL_I386_REL32)2144 // Ignore relative relocations on x86. On x86_64 they can't be ignored2145 // since they're also used to compute absolute addresses.2146 continue;2147 2148 Symbol *ref = sc->file->getSymbol(reloc.SymbolTableIndex);2149 maybeAddAddressTakenFunction(usedSymbols, ref);2150 }2151 }2152}2153 2154// Create the guard function id table. This is a table of RVAs of all2155// address-taken functions. It is sorted and uniqued, just like the safe SEH2156// table.2157void Writer::createGuardCFTables() {2158 Configuration *config = &ctx.config;2159 2160 if (config->guardCF == GuardCFLevel::Off) {2161 // MSVC marks the entire image as instrumented if any input object was built2162 // with /guard:cf.2163 for (ObjFile *file : ctx.objFileInstances) {2164 if (file->hasGuardCF()) {2165 ctx.forEachSymtab([&](SymbolTable &symtab) {2166 Symbol *flagSym = symtab.findUnderscore("__guard_flags");2167 cast<DefinedAbsolute>(flagSym)->setVA(2168 uint32_t(GuardFlags::CF_INSTRUMENTED));2169 });2170 break;2171 }2172 }2173 return;2174 }2175 2176 SymbolRVASet addressTakenSyms;2177 SymbolRVASet giatsRVASet;2178 std::vector<Symbol *> giatsSymbols;2179 SymbolRVASet longJmpTargets;2180 SymbolRVASet ehContTargets;2181 for (ObjFile *file : ctx.objFileInstances) {2182 // If the object was compiled with /guard:cf, the address taken symbols2183 // are in .gfids$y sections, and the longjmp targets are in .gljmp$y2184 // sections. If the object was not compiled with /guard:cf, we assume there2185 // were no setjmp targets, and that all code symbols with relocations are2186 // possibly address-taken.2187 if (file->hasGuardCF()) {2188 markSymbolsForRVATable(file, file->getGuardFidChunks(), addressTakenSyms);2189 markSymbolsForRVATable(file, file->getGuardIATChunks(), giatsRVASet);2190 getSymbolsFromSections(file, file->getGuardIATChunks(), giatsSymbols);2191 markSymbolsForRVATable(file, file->getGuardLJmpChunks(), longJmpTargets);2192 } else {2193 markSymbolsWithRelocations(file, addressTakenSyms);2194 }2195 // If the object was compiled with /guard:ehcont, the ehcont targets are in2196 // .gehcont$y sections.2197 if (file->hasGuardEHCont())2198 markSymbolsForRVATable(file, file->getGuardEHContChunks(), ehContTargets);2199 }2200 2201 // Mark the image entry as address-taken.2202 ctx.forEachSymtab([&](SymbolTable &symtab) {2203 if (symtab.entry)2204 maybeAddAddressTakenFunction(addressTakenSyms, symtab.entry);2205 2206 // Mark exported symbols in executable sections as address-taken.2207 for (Export &e : symtab.exports)2208 maybeAddAddressTakenFunction(addressTakenSyms, e.sym);2209 });2210 2211 // For each entry in the .giats table, check if it has a corresponding load2212 // thunk (e.g. because the DLL that defines it will be delay-loaded) and, if2213 // so, add the load thunk to the address taken (.gfids) table.2214 for (Symbol *s : giatsSymbols) {2215 if (auto *di = dyn_cast<DefinedImportData>(s)) {2216 if (di->loadThunkSym)2217 addSymbolToRVASet(addressTakenSyms, di->loadThunkSym);2218 }2219 }2220 2221 // Ensure sections referenced in the gfid table are 16-byte aligned.2222 for (const ChunkAndOffset &c : addressTakenSyms)2223 if (c.inputChunk->getAlignment() < 16)2224 c.inputChunk->setAlignment(16);2225 2226 maybeAddRVATable(std::move(addressTakenSyms), "__guard_fids_table",2227 "__guard_fids_count");2228 2229 // Add the Guard Address Taken IAT Entry Table (.giats).2230 maybeAddRVATable(std::move(giatsRVASet), "__guard_iat_table",2231 "__guard_iat_count");2232 2233 // Add the longjmp target table unless the user told us not to.2234 if (config->guardCF & GuardCFLevel::LongJmp)2235 maybeAddRVATable(std::move(longJmpTargets), "__guard_longjmp_table",2236 "__guard_longjmp_count");2237 2238 // Add the ehcont target table unless the user told us not to.2239 if (config->guardCF & GuardCFLevel::EHCont)2240 maybeAddRVATable(std::move(ehContTargets), "__guard_eh_cont_table",2241 "__guard_eh_cont_count");2242 2243 // Set __guard_flags, which will be used in the load config to indicate that2244 // /guard:cf was enabled.2245 uint32_t guardFlags = uint32_t(GuardFlags::CF_INSTRUMENTED) |2246 uint32_t(GuardFlags::CF_FUNCTION_TABLE_PRESENT);2247 if (config->guardCF & GuardCFLevel::LongJmp)2248 guardFlags |= uint32_t(GuardFlags::CF_LONGJUMP_TABLE_PRESENT);2249 if (config->guardCF & GuardCFLevel::EHCont)2250 guardFlags |= uint32_t(GuardFlags::EH_CONTINUATION_TABLE_PRESENT);2251 ctx.forEachSymtab([guardFlags](SymbolTable &symtab) {2252 Symbol *flagSym = symtab.findUnderscore("__guard_flags");2253 cast<DefinedAbsolute>(flagSym)->setVA(guardFlags);2254 });2255}2256 2257// Take a list of input sections containing symbol table indices and add those2258// symbols to a vector. The challenge is that symbol RVAs are not known and2259// depend on the table size, so we can't directly build a set of integers.2260void Writer::getSymbolsFromSections(ObjFile *file,2261 ArrayRef<SectionChunk *> symIdxChunks,2262 std::vector<Symbol *> &symbols) {2263 for (SectionChunk *c : symIdxChunks) {2264 // Skip sections discarded by linker GC. This comes up when a .gfids section2265 // is associated with something like a vtable and the vtable is discarded.2266 // In this case, the associated gfids section is discarded, and we don't2267 // mark the virtual member functions as address-taken by the vtable.2268 if (!c->live)2269 continue;2270 2271 // Validate that the contents look like symbol table indices.2272 ArrayRef<uint8_t> data = c->getContents();2273 if (data.size() % 4 != 0) {2274 Warn(ctx) << "ignoring " << c->getSectionName()2275 << " symbol table index section in object " << file;2276 continue;2277 }2278 2279 // Read each symbol table index and check if that symbol was included in the2280 // final link. If so, add it to the vector of symbols.2281 ArrayRef<ulittle32_t> symIndices(2282 reinterpret_cast<const ulittle32_t *>(data.data()), data.size() / 4);2283 ArrayRef<Symbol *> objSymbols = file->getSymbols();2284 for (uint32_t symIndex : symIndices) {2285 if (symIndex >= objSymbols.size()) {2286 Warn(ctx) << "ignoring invalid symbol table index in section "2287 << c->getSectionName() << " in object " << file;2288 continue;2289 }2290 if (Symbol *s = objSymbols[symIndex]) {2291 if (s->isLive())2292 symbols.push_back(cast<Symbol>(s));2293 }2294 }2295 }2296}2297 2298// Take a list of input sections containing symbol table indices and add those2299// symbols to an RVA table.2300void Writer::markSymbolsForRVATable(ObjFile *file,2301 ArrayRef<SectionChunk *> symIdxChunks,2302 SymbolRVASet &tableSymbols) {2303 std::vector<Symbol *> syms;2304 getSymbolsFromSections(file, symIdxChunks, syms);2305 2306 for (Symbol *s : syms)2307 addSymbolToRVASet(tableSymbols, cast<Defined>(s));2308}2309 2310// Replace the absolute table symbol with a synthetic symbol pointing to2311// tableChunk so that we can emit base relocations for it and resolve section2312// relative relocations.2313void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,2314 StringRef countSym, bool hasFlag) {2315 if (tableSymbols.empty())2316 return;2317 2318 NonSectionChunk *tableChunk;2319 if (hasFlag)2320 tableChunk = make<RVAFlagTableChunk>(std::move(tableSymbols));2321 else2322 tableChunk = make<RVATableChunk>(std::move(tableSymbols));2323 rdataSec->addChunk(tableChunk);2324 2325 ctx.forEachSymtab([&](SymbolTable &symtab) {2326 Symbol *t = symtab.findUnderscore(tableSym);2327 Symbol *c = symtab.findUnderscore(countSym);2328 replaceSymbol<DefinedSynthetic>(t, t->getName(), tableChunk);2329 cast<DefinedAbsolute>(c)->setVA(tableChunk->getSize() / (hasFlag ? 5 : 4));2330 });2331}2332 2333// Create CHPE metadata chunks.2334void Writer::createECChunks() {2335 if (!ctx.symtab.isEC())2336 return;2337 2338 for (Symbol *s : ctx.symtab.expSymbols) {2339 auto sym = dyn_cast<Defined>(s);2340 if (!sym || !sym->getChunk())2341 continue;2342 if (auto thunk = dyn_cast<ECExportThunkChunk>(sym->getChunk())) {2343 hexpthkSec->addChunk(thunk);2344 exportThunks.push_back({thunk, thunk->target});2345 } else if (auto def = dyn_cast<DefinedRegular>(sym)) {2346 // Allow section chunk to be treated as an export thunk if it looks like2347 // one.2348 SectionChunk *chunk = def->getChunk();2349 if (!chunk->live || chunk->getMachine() != AMD64)2350 continue;2351 assert(sym->getName().starts_with("EXP+"));2352 StringRef targetName = sym->getName().substr(strlen("EXP+"));2353 // If EXP+#foo is an export thunk of a hybrid patchable function,2354 // we should use the #foo$hp_target symbol as the redirection target.2355 // First, try to look up the $hp_target symbol. If it can't be found,2356 // assume it's a regular function and look for #foo instead.2357 Symbol *targetSym = ctx.symtab.find((targetName + "$hp_target").str());2358 if (!targetSym)2359 targetSym = ctx.symtab.find(targetName);2360 Defined *t = dyn_cast_or_null<Defined>(targetSym);2361 if (t && isArm64EC(t->getChunk()->getMachine()))2362 exportThunks.push_back({chunk, t});2363 }2364 }2365 2366 auto codeMapChunk = make<ECCodeMapChunk>(codeMap);2367 rdataSec->addChunk(codeMapChunk);2368 Symbol *codeMapSym = ctx.symtab.findUnderscore("__hybrid_code_map");2369 replaceSymbol<DefinedSynthetic>(codeMapSym, codeMapSym->getName(),2370 codeMapChunk);2371 2372 CHPECodeRangesChunk *ranges = make<CHPECodeRangesChunk>(exportThunks);2373 rdataSec->addChunk(ranges);2374 Symbol *rangesSym =2375 ctx.symtab.findUnderscore("__x64_code_ranges_to_entry_points");2376 replaceSymbol<DefinedSynthetic>(rangesSym, rangesSym->getName(), ranges);2377 2378 CHPERedirectionChunk *entryPoints = make<CHPERedirectionChunk>(exportThunks);2379 a64xrmSec->addChunk(entryPoints);2380 Symbol *entryPointsSym =2381 ctx.symtab.findUnderscore("__arm64x_redirection_metadata");2382 replaceSymbol<DefinedSynthetic>(entryPointsSym, entryPointsSym->getName(),2383 entryPoints);2384 2385 for (auto thunk : ctx.symtab.sameAddressThunks) {2386 // Relocation values are set later in setECSymbols.2387 ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),2388 thunk);2389 ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),2390 Arm64XRelocVal(thunk, sizeof(uint32_t)));2391 }2392}2393 2394// MinGW specific. Gather all relocations that are imported from a DLL even2395// though the code didn't expect it to, produce the table that the runtime2396// uses for fixing them up, and provide the synthetic symbols that the2397// runtime uses for finding the table.2398void Writer::createRuntimePseudoRelocs() {2399 ctx.forEachSymtab([&](SymbolTable &symtab) {2400 std::vector<RuntimePseudoReloc> rels;2401 2402 for (Chunk *c : ctx.driver.getChunks()) {2403 auto *sc = dyn_cast<SectionChunk>(c);2404 if (!sc || !sc->live || &sc->file->symtab != &symtab)2405 continue;2406 // Don't create pseudo relocations for sections that won't be2407 // mapped at runtime.2408 if (sc->header->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)2409 continue;2410 sc->getRuntimePseudoRelocs(rels);2411 }2412 2413 if (!ctx.config.pseudoRelocs) {2414 // Not writing any pseudo relocs; if some were needed, error out and2415 // indicate what required them.2416 for (const RuntimePseudoReloc &rpr : rels)2417 Err(ctx) << "automatic dllimport of " << rpr.sym->getName() << " in "2418 << toString(rpr.target->file)2419 << " requires pseudo relocations";2420 return;2421 }2422 2423 if (!rels.empty()) {2424 Log(ctx) << "Writing " << Twine(rels.size())2425 << " runtime pseudo relocations";2426 const char *symbolName = "_pei386_runtime_relocator";2427 Symbol *relocator = symtab.findUnderscore(symbolName);2428 if (!relocator)2429 Err(ctx)2430 << "output image has runtime pseudo relocations, but the function "2431 << symbolName2432 << " is missing; it is needed for fixing the relocations at "2433 "runtime";2434 }2435 2436 PseudoRelocTableChunk *table = make<PseudoRelocTableChunk>(rels);2437 rdataSec->addChunk(table);2438 EmptyChunk *endOfList = make<EmptyChunk>();2439 rdataSec->addChunk(endOfList);2440 2441 Symbol *headSym = symtab.findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__");2442 Symbol *endSym = symtab.findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__");2443 replaceSymbol<DefinedSynthetic>(headSym, headSym->getName(), table);2444 replaceSymbol<DefinedSynthetic>(endSym, endSym->getName(), endOfList);2445 });2446}2447 2448// MinGW specific.2449// The MinGW .ctors and .dtors lists have sentinels at each end;2450// a (uintptr_t)-1 at the start and a (uintptr_t)0 at the end.2451// There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__2452// and __DTOR_LIST__ respectively.2453void Writer::insertCtorDtorSymbols() {2454 ctx.forEachSymtab([&](SymbolTable &symtab) {2455 AbsolutePointerChunk *ctorListHead = make<AbsolutePointerChunk>(symtab, -1);2456 AbsolutePointerChunk *ctorListEnd = make<AbsolutePointerChunk>(symtab, 0);2457 AbsolutePointerChunk *dtorListHead = make<AbsolutePointerChunk>(symtab, -1);2458 AbsolutePointerChunk *dtorListEnd = make<AbsolutePointerChunk>(symtab, 0);2459 ctorsSec->insertChunkAtStart(ctorListHead);2460 ctorsSec->addChunk(ctorListEnd);2461 dtorsSec->insertChunkAtStart(dtorListHead);2462 dtorsSec->addChunk(dtorListEnd);2463 2464 Symbol *ctorListSym = symtab.findUnderscore("__CTOR_LIST__");2465 Symbol *dtorListSym = symtab.findUnderscore("__DTOR_LIST__");2466 replaceSymbol<DefinedSynthetic>(ctorListSym, ctorListSym->getName(),2467 ctorListHead);2468 replaceSymbol<DefinedSynthetic>(dtorListSym, dtorListSym->getName(),2469 dtorListHead);2470 });2471 2472 if (ctx.hybridSymtab) {2473 ctorsSec->splitECChunks();2474 dtorsSec->splitECChunks();2475 }2476}2477 2478// MinGW (really, Cygwin) specific.2479// The Cygwin startup code uses __data_start__ __data_end__ __bss_start__2480// and __bss_end__ to know what to copy during fork emulation.2481void Writer::insertBssDataStartEndSymbols() {2482 if (!dataSec->chunks.empty()) {2483 Symbol *dataStartSym = ctx.symtab.find("__data_start__");2484 Symbol *dataEndSym = ctx.symtab.find("__data_end__");2485 Chunk *endChunk = dataSec->chunks.back();2486 replaceSymbol<DefinedSynthetic>(dataStartSym, dataStartSym->getName(),2487 dataSec->chunks.front());2488 replaceSymbol<DefinedSynthetic>(dataEndSym, dataEndSym->getName(), endChunk,2489 endChunk->getSize());2490 }2491 2492 if (!bssSec->chunks.empty()) {2493 Symbol *bssStartSym = ctx.symtab.find("__bss_start__");2494 Symbol *bssEndSym = ctx.symtab.find("__bss_end__");2495 Chunk *endChunk = bssSec->chunks.back();2496 replaceSymbol<DefinedSynthetic>(bssStartSym, bssStartSym->getName(),2497 bssSec->chunks.front());2498 replaceSymbol<DefinedSynthetic>(bssEndSym, bssEndSym->getName(), endChunk,2499 endChunk->getSize());2500 }2501}2502 2503// Handles /section options to allow users to overwrite2504// section attributes.2505void Writer::setSectionPermissions() {2506 llvm::TimeTraceScope timeScope("Sections permissions");2507 for (auto &p : ctx.config.section) {2508 StringRef name = p.first;2509 uint32_t perm = p.second;2510 for (OutputSection *sec : ctx.outputSections)2511 if (sec->name == name)2512 sec->setPermissions(perm);2513 }2514}2515 2516// Set symbols used by ARM64EC metadata.2517void Writer::setECSymbols() {2518 if (!ctx.symtab.isEC())2519 return;2520 2521 llvm::stable_sort(exportThunks, [](const std::pair<Chunk *, Defined *> &a,2522 const std::pair<Chunk *, Defined *> &b) {2523 return a.first->getRVA() < b.first->getRVA();2524 });2525 2526 ChunkRange &chpePdata = ctx.config.machine == ARM64X ? hybridPdata : pdata;2527 Symbol *rfeTableSym = ctx.symtab.findUnderscore("__arm64x_extra_rfe_table");2528 replaceSymbol<DefinedSynthetic>(rfeTableSym, "__arm64x_extra_rfe_table",2529 chpePdata.first);2530 2531 if (chpePdata.first) {2532 Symbol *rfeSizeSym =2533 ctx.symtab.findUnderscore("__arm64x_extra_rfe_table_size");2534 cast<DefinedAbsolute>(rfeSizeSym)2535 ->setVA(chpePdata.last->getRVA() + chpePdata.last->getSize() -2536 chpePdata.first->getRVA());2537 }2538 2539 Symbol *rangesCountSym =2540 ctx.symtab.findUnderscore("__x64_code_ranges_to_entry_points_count");2541 cast<DefinedAbsolute>(rangesCountSym)->setVA(exportThunks.size());2542 2543 Symbol *entryPointCountSym =2544 ctx.symtab.findUnderscore("__arm64x_redirection_metadata_count");2545 cast<DefinedAbsolute>(entryPointCountSym)->setVA(exportThunks.size());2546 2547 Symbol *iatSym = ctx.symtab.findUnderscore("__hybrid_auxiliary_iat");2548 replaceSymbol<DefinedSynthetic>(iatSym, "__hybrid_auxiliary_iat",2549 idata.auxIat.empty() ? nullptr2550 : idata.auxIat.front());2551 2552 Symbol *iatCopySym = ctx.symtab.findUnderscore("__hybrid_auxiliary_iat_copy");2553 replaceSymbol<DefinedSynthetic>(2554 iatCopySym, "__hybrid_auxiliary_iat_copy",2555 idata.auxIatCopy.empty() ? nullptr : idata.auxIatCopy.front());2556 2557 Symbol *delayIatSym =2558 ctx.symtab.findUnderscore("__hybrid_auxiliary_delayload_iat");2559 replaceSymbol<DefinedSynthetic>(2560 delayIatSym, "__hybrid_auxiliary_delayload_iat",2561 delayIdata.getAuxIat().empty() ? nullptr2562 : delayIdata.getAuxIat().front());2563 2564 Symbol *delayIatCopySym =2565 ctx.symtab.findUnderscore("__hybrid_auxiliary_delayload_iat_copy");2566 replaceSymbol<DefinedSynthetic>(2567 delayIatCopySym, "__hybrid_auxiliary_delayload_iat_copy",2568 delayIdata.getAuxIatCopy().empty() ? nullptr2569 : delayIdata.getAuxIatCopy().front());2570 2571 if (ctx.config.machine == ARM64X) {2572 // For the hybrid image, set the alternate entry point to the EC entry2573 // point. In the hybrid view, it is swapped to the native entry point2574 // using ARM64X relocations.2575 if (auto altEntrySym = cast_or_null<Defined>(ctx.symtab.entry)) {2576 // If the entry is an EC export thunk, use its target instead.2577 if (auto thunkChunk =2578 dyn_cast<ECExportThunkChunk>(altEntrySym->getChunk()))2579 altEntrySym = thunkChunk->target;2580 ctx.symtab.findUnderscore("__arm64x_native_entrypoint")2581 ->replaceKeepingName(altEntrySym, sizeof(SymbolUnion));2582 }2583 2584 if (ctx.symtab.edataStart)2585 ctx.dynamicRelocs->set(2586 dataDirOffset64 + EXPORT_TABLE * sizeof(data_directory) +2587 offsetof(data_directory, Size),2588 ctx.symtab.edataEnd->getRVA() - ctx.symtab.edataStart->getRVA() +2589 ctx.symtab.edataEnd->getSize());2590 if (hybridPdata.first)2591 ctx.dynamicRelocs->set(2592 dataDirOffset64 + EXCEPTION_TABLE * sizeof(data_directory) +2593 offsetof(data_directory, Size),2594 hybridPdata.last->getRVA() - hybridPdata.first->getRVA() +2595 hybridPdata.last->getSize());2596 if (chpeSym && pdata.first)2597 ctx.dynamicRelocs->set(2598 chpeSym->getRVA() + offsetof(chpe_metadata, ExtraRFETableSize),2599 pdata.last->getRVA() + pdata.last->getSize() - pdata.first->getRVA());2600 }2601 2602 for (SameAddressThunkARM64EC *thunk : ctx.symtab.sameAddressThunks)2603 thunk->setDynamicRelocs(ctx);2604}2605 2606// Write section contents to a mmap'ed file.2607void Writer::writeSections() {2608 llvm::TimeTraceScope timeScope("Write sections");2609 uint8_t *buf = buffer->getBufferStart();2610 for (OutputSection *sec : ctx.outputSections) {2611 uint8_t *secBuf = buf + sec->getFileOff();2612 // Fill gaps between functions in .text with INT3 instructions2613 // instead of leaving as NUL bytes (which can be interpreted as2614 // ADD instructions). Only fill the gaps between chunks. Most2615 // chunks overwrite it anyway, but uninitialized data chunks2616 // merged into a code section don't.2617 if ((sec->header.Characteristics & IMAGE_SCN_CNT_CODE) &&2618 (ctx.config.machine == AMD64 || ctx.config.machine == I386)) {2619 uint32_t prevEnd = 0;2620 for (Chunk *c : sec->chunks) {2621 uint32_t off = c->getRVA() - sec->getRVA();2622 memset(secBuf + prevEnd, 0xCC, off - prevEnd);2623 prevEnd = off + c->getSize();2624 }2625 memset(secBuf + prevEnd, 0xCC, sec->getRawSize() - prevEnd);2626 }2627 2628 parallelForEach(sec->chunks, [&](Chunk *c) {2629 uint8_t *buf = secBuf + c->getRVA() - sec->getRVA();2630 c->writeTo(buf);2631 2632 // Write the offset to EC entry thunk preceding section contents. The low2633 // bit is always set, so it's effectively an offset from the last byte of2634 // the offset.2635 if (Defined *entryThunk = c->getEntryThunk())2636 write32le(buf - sizeof(uint32_t),2637 entryThunk->getRVA() - c->getRVA() + 1);2638 });2639 }2640}2641 2642void Writer::writeBuildId() {2643 llvm::TimeTraceScope timeScope("Write build ID");2644 2645 // There are two important parts to the build ID.2646 // 1) If building with debug info, the COFF debug directory contains a2647 // timestamp as well as a Guid and Age of the PDB.2648 // 2) In all cases, the PE COFF file header also contains a timestamp.2649 // For reproducibility, instead of a timestamp we want to use a hash of the2650 // PE contents.2651 Configuration *config = &ctx.config;2652 bool generateSyntheticBuildId = config->buildIDHash == BuildIDHash::Binary;2653 if (generateSyntheticBuildId) {2654 assert(buildId && "BuildId is not set!");2655 // BuildId->BuildId was filled in when the PDB was written.2656 }2657 2658 // At this point the only fields in the COFF file which remain unset are the2659 // "timestamp" in the COFF file header, and the ones in the coff debug2660 // directory. Now we can hash the file and write that hash to the various2661 // timestamp fields in the file.2662 StringRef outputFileData(2663 reinterpret_cast<const char *>(buffer->getBufferStart()),2664 buffer->getBufferSize());2665 2666 uint32_t timestamp = config->timestamp;2667 uint64_t hash = 0;2668 2669 if (config->repro || generateSyntheticBuildId)2670 hash = xxh3_64bits(outputFileData);2671 2672 if (config->repro)2673 timestamp = static_cast<uint32_t>(hash);2674 2675 if (generateSyntheticBuildId) {2676 buildId->buildId->PDB70.CVSignature = OMF::Signature::PDB70;2677 buildId->buildId->PDB70.Age = 1;2678 memcpy(buildId->buildId->PDB70.Signature, &hash, 8);2679 // xxhash only gives us 8 bytes, so put some fixed data in the other half.2680 memcpy(&buildId->buildId->PDB70.Signature[8], "LLD PDB.", 8);2681 }2682 2683 if (debugDirectory)2684 debugDirectory->setTimeDateStamp(timestamp);2685 2686 uint8_t *buf = buffer->getBufferStart();2687 buf += dosStubSize + sizeof(PEMagic);2688 object::coff_file_header *coffHeader =2689 reinterpret_cast<coff_file_header *>(buf);2690 coffHeader->TimeDateStamp = timestamp;2691}2692 2693// Sort .pdata section contents according to PE/COFF spec 5.5.2694template <typename T>2695void Writer::sortExceptionTable(ChunkRange &exceptionTable) {2696 if (!exceptionTable.first)2697 return;2698 2699 // We assume .pdata contains function table entries only.2700 auto bufAddr = [&](Chunk *c) {2701 OutputSection *os = ctx.getOutputSection(c);2702 return buffer->getBufferStart() + os->getFileOff() + c->getRVA() -2703 os->getRVA();2704 };2705 uint8_t *begin = bufAddr(exceptionTable.first);2706 uint8_t *end = bufAddr(exceptionTable.last) + exceptionTable.last->getSize();2707 if ((end - begin) % sizeof(T) != 0) {2708 Fatal(ctx) << "unexpected .pdata size: " << (end - begin)2709 << " is not a multiple of " << sizeof(T);2710 }2711 2712 parallelSort(MutableArrayRef<T>(reinterpret_cast<T *>(begin),2713 reinterpret_cast<T *>(end)),2714 [](const T &a, const T &b) { return a.begin < b.begin; });2715}2716 2717// Sort .pdata section contents according to PE/COFF spec 5.5.2718void Writer::sortExceptionTables() {2719 llvm::TimeTraceScope timeScope("Sort exception table");2720 2721 struct EntryX64 {2722 ulittle32_t begin, end, unwind;2723 };2724 struct EntryArm {2725 ulittle32_t begin, unwind;2726 };2727 2728 switch (ctx.config.machine) {2729 case AMD64:2730 sortExceptionTable<EntryX64>(pdata);2731 break;2732 case ARM64EC:2733 case ARM64X:2734 sortExceptionTable<EntryX64>(hybridPdata);2735 [[fallthrough]];2736 case ARMNT:2737 case ARM64:2738 sortExceptionTable<EntryArm>(pdata);2739 break;2740 default:2741 if (pdata.first)2742 ctx.e.errs() << "warning: don't know how to handle .pdata\n";2743 break;2744 }2745}2746 2747// The CRT section contains, among other things, the array of function2748// pointers that initialize every global variable that is not trivially2749// constructed. The CRT calls them one after the other prior to invoking2750// main().2751//2752// As per C++ spec, 3.6.2/2.3,2753// "Variables with ordered initialization defined within a single2754// translation unit shall be initialized in the order of their definitions2755// in the translation unit"2756//2757// It is therefore critical to sort the chunks containing the function2758// pointers in the order that they are listed in the object file (top to2759// bottom), otherwise global objects might not be initialized in the2760// correct order.2761void Writer::sortCRTSectionChunks(std::vector<Chunk *> &chunks) {2762 auto sectionChunkOrder = [](const Chunk *a, const Chunk *b) {2763 auto sa = dyn_cast<SectionChunk>(a);2764 auto sb = dyn_cast<SectionChunk>(b);2765 assert(sa && sb && "Non-section chunks in CRT section!");2766 2767 StringRef sAObj = sa->file->mb.getBufferIdentifier();2768 StringRef sBObj = sb->file->mb.getBufferIdentifier();2769 2770 return sAObj == sBObj && sa->getSectionNumber() < sb->getSectionNumber();2771 };2772 llvm::stable_sort(chunks, sectionChunkOrder);2773 2774 if (ctx.config.verbose) {2775 for (auto &c : chunks) {2776 auto sc = dyn_cast<SectionChunk>(c);2777 Log(ctx) << " " << sc->file->mb.getBufferIdentifier().str()2778 << ", SectionID: " << sc->getSectionNumber();2779 }2780 }2781}2782 2783OutputSection *Writer::findSection(StringRef name) {2784 for (OutputSection *sec : ctx.outputSections)2785 if (sec->name == name)2786 return sec;2787 return nullptr;2788}2789 2790uint32_t Writer::getSizeOfInitializedData() {2791 uint32_t res = 0;2792 for (OutputSection *s : ctx.outputSections)2793 if (s->header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)2794 res += s->getRawSize();2795 return res;2796}2797 2798// Add base relocations to .reloc section.2799void Writer::addBaserels() {2800 if (!ctx.config.relocatable)2801 return;2802 std::vector<Baserel> v;2803 for (OutputSection *sec : ctx.outputSections) {2804 if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)2805 continue;2806 llvm::TimeTraceScope timeScope("Base relocations: ", sec->name);2807 // Collect all locations for base relocations.2808 for (Chunk *c : sec->chunks)2809 c->getBaserels(&v);2810 // Add the addresses to .reloc section.2811 if (!v.empty())2812 addBaserelBlocks(v);2813 v.clear();2814 }2815}2816 2817// Add addresses to .reloc section. Note that addresses are grouped by page.2818void Writer::addBaserelBlocks(std::vector<Baserel> &v) {2819 const uint32_t mask = ~uint32_t(pageSize - 1);2820 uint32_t page = v[0].rva & mask;2821 size_t i = 0, j = 1;2822 llvm::sort(v,2823 [](const Baserel &x, const Baserel &y) { return x.rva < y.rva; });2824 for (size_t e = v.size(); j < e; ++j) {2825 uint32_t p = v[j].rva & mask;2826 if (p == page)2827 continue;2828 relocSec->addChunk(make<BaserelChunk>(page, &v[i], &v[0] + j));2829 i = j;2830 page = p;2831 }2832 if (i == j)2833 return;2834 relocSec->addChunk(make<BaserelChunk>(page, &v[i], &v[0] + j));2835}2836 2837void Writer::createDynamicRelocs() {2838 if (!ctx.dynamicRelocs)2839 return;2840 2841 // Adjust the Machine field in the COFF header to AMD64.2842 ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint16_t),2843 coffHeaderOffset + offsetof(coff_file_header, Machine),2844 AMD64);2845 2846 if (ctx.symtab.entry != ctx.hybridSymtab->entry ||2847 pdata.first != hybridPdata.first) {2848 chpeSym = cast_or_null<DefinedRegular>(2849 ctx.symtab.findUnderscore("__chpe_metadata"));2850 if (!chpeSym)2851 Warn(ctx) << "'__chpe_metadata' is missing for ARM64X target";2852 }2853 2854 if (ctx.symtab.entry != ctx.hybridSymtab->entry) {2855 ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),2856 peHeaderOffset +2857 offsetof(pe32plus_header, AddressOfEntryPoint),2858 cast_or_null<Defined>(ctx.symtab.entry));2859 2860 // Swap the alternate entry point in the CHPE metadata.2861 if (chpeSym)2862 ctx.dynamicRelocs->add(2863 IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),2864 Arm64XRelocVal(chpeSym, offsetof(chpe_metadata, AlternateEntryPoint)),2865 cast_or_null<Defined>(ctx.hybridSymtab->entry));2866 }2867 2868 if (ctx.symtab.edataStart != ctx.hybridSymtab->edataStart) {2869 ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),2870 dataDirOffset64 +2871 EXPORT_TABLE * sizeof(data_directory) +2872 offsetof(data_directory, RelativeVirtualAddress),2873 ctx.symtab.edataStart);2874 // The Size value is assigned after addresses are finalized.2875 ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),2876 dataDirOffset64 +2877 EXPORT_TABLE * sizeof(data_directory) +2878 offsetof(data_directory, Size));2879 }2880 2881 if (pdata.first != hybridPdata.first) {2882 ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),2883 dataDirOffset64 +2884 EXCEPTION_TABLE * sizeof(data_directory) +2885 offsetof(data_directory, RelativeVirtualAddress),2886 hybridPdata.first);2887 // The Size value is assigned after addresses are finalized.2888 ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),2889 dataDirOffset64 +2890 EXCEPTION_TABLE * sizeof(data_directory) +2891 offsetof(data_directory, Size));2892 2893 // Swap ExtraRFETable in the CHPE metadata.2894 if (chpeSym) {2895 ctx.dynamicRelocs->add(2896 IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),2897 Arm64XRelocVal(chpeSym, offsetof(chpe_metadata, ExtraRFETable)),2898 pdata.first);2899 // The Size value is assigned after addresses are finalized.2900 ctx.dynamicRelocs->add(2901 IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),2902 Arm64XRelocVal(chpeSym, offsetof(chpe_metadata, ExtraRFETableSize)));2903 }2904 }2905 2906 // Set the hybrid load config to the EC load config.2907 ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),2908 dataDirOffset64 +2909 LOAD_CONFIG_TABLE * sizeof(data_directory) +2910 offsetof(data_directory, RelativeVirtualAddress),2911 ctx.symtab.loadConfigSym);2912 ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),2913 dataDirOffset64 +2914 LOAD_CONFIG_TABLE * sizeof(data_directory) +2915 offsetof(data_directory, Size),2916 ctx.symtab.loadConfigSize);2917}2918 2919PartialSection *Writer::createPartialSection(StringRef name,2920 uint32_t outChars) {2921 PartialSection *&pSec = partialSections[{name, outChars}];2922 if (pSec)2923 return pSec;2924 pSec = make<PartialSection>(name, outChars);2925 return pSec;2926}2927 2928PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {2929 auto it = partialSections.find({name, outChars});2930 if (it != partialSections.end())2931 return it->second;2932 return nullptr;2933}2934 2935void Writer::fixTlsAlignment() {2936 Defined *tlsSym =2937 dyn_cast_or_null<Defined>(ctx.symtab.findUnderscore("_tls_used"));2938 if (!tlsSym)2939 return;2940 2941 OutputSection *sec = ctx.getOutputSection(tlsSym->getChunk());2942 assert(sec && tlsSym->getRVA() >= sec->getRVA() &&2943 "no output section for _tls_used");2944 2945 uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff();2946 uint64_t tlsOffset = tlsSym->getRVA() - sec->getRVA();2947 uint64_t directorySize = ctx.config.is64()2948 ? sizeof(object::coff_tls_directory64)2949 : sizeof(object::coff_tls_directory32);2950 2951 if (tlsOffset + directorySize > sec->getRawSize())2952 Fatal(ctx) << "_tls_used sym is malformed";2953 2954 if (ctx.config.is64()) {2955 object::coff_tls_directory64 *tlsDir =2956 reinterpret_cast<object::coff_tls_directory64 *>(&secBuf[tlsOffset]);2957 tlsDir->setAlignment(tlsAlignment);2958 } else {2959 object::coff_tls_directory32 *tlsDir =2960 reinterpret_cast<object::coff_tls_directory32 *>(&secBuf[tlsOffset]);2961 tlsDir->setAlignment(tlsAlignment);2962 }2963}2964 2965void Writer::prepareLoadConfig() {2966 ctx.forEachActiveSymtab([&](SymbolTable &symtab) {2967 if (!symtab.loadConfigSym)2968 return;2969 2970 OutputSection *sec = ctx.getOutputSection(symtab.loadConfigSym->getChunk());2971 uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff();2972 uint8_t *symBuf = secBuf + (symtab.loadConfigSym->getRVA() - sec->getRVA());2973 2974 if (ctx.config.is64())2975 prepareLoadConfig(symtab,2976 reinterpret_cast<coff_load_configuration64 *>(symBuf));2977 else2978 prepareLoadConfig(symtab,2979 reinterpret_cast<coff_load_configuration32 *>(symBuf));2980 });2981}2982 2983template <typename T>2984void Writer::prepareLoadConfig(SymbolTable &symtab, T *loadConfig) {2985 size_t loadConfigSize = loadConfig->Size;2986 2987#define RETURN_IF_NOT_CONTAINS(field) \2988 if (loadConfigSize < offsetof(T, field) + sizeof(T::field)) { \2989 Warn(ctx) << "'_load_config_used' structure too small to include " #field; \2990 return; \2991 }2992 2993#define IF_CONTAINS(field) \2994 if (loadConfigSize >= offsetof(T, field) + sizeof(T::field))2995 2996#define CHECK_VA(field, sym) \2997 if (auto *s = dyn_cast<DefinedSynthetic>(symtab.findUnderscore(sym))) \2998 if (loadConfig->field != ctx.config.imageBase + s->getRVA()) \2999 Warn(ctx) << #field " not set correctly in '_load_config_used'";3000 3001#define CHECK_ABSOLUTE(field, sym) \3002 if (auto *s = dyn_cast<DefinedAbsolute>(symtab.findUnderscore(sym))) \3003 if (loadConfig->field != s->getVA()) \3004 Warn(ctx) << #field " not set correctly in '_load_config_used'";3005 3006 if (ctx.config.dependentLoadFlags) {3007 RETURN_IF_NOT_CONTAINS(DependentLoadFlags)3008 loadConfig->DependentLoadFlags = ctx.config.dependentLoadFlags;3009 }3010 3011 if (ctx.dynamicRelocs) {3012 IF_CONTAINS(DynamicValueRelocTableSection) {3013 loadConfig->DynamicValueRelocTableSection = relocSec->sectionIndex;3014 loadConfig->DynamicValueRelocTableOffset =3015 ctx.dynamicRelocs->getRVA() - relocSec->getRVA();3016 }3017 else {3018 Warn(ctx) << "'_load_config_used' structure too small to include dynamic "3019 "relocations";3020 }3021 }3022 3023 IF_CONTAINS(CHPEMetadataPointer) {3024 // On ARM64X, only the EC version of the load config contains3025 // CHPEMetadataPointer. Copy its value to the native load config.3026 if (ctx.config.machine == ARM64X && !symtab.isEC() &&3027 ctx.symtab.loadConfigSize >=3028 offsetof(T, CHPEMetadataPointer) + sizeof(T::CHPEMetadataPointer)) {3029 OutputSection *sec =3030 ctx.getOutputSection(ctx.symtab.loadConfigSym->getChunk());3031 uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff();3032 auto hybridLoadConfig =3033 reinterpret_cast<const coff_load_configuration64 *>(3034 secBuf + (ctx.symtab.loadConfigSym->getRVA() - sec->getRVA()));3035 loadConfig->CHPEMetadataPointer = hybridLoadConfig->CHPEMetadataPointer;3036 }3037 }3038 3039 if (ctx.config.guardCF == GuardCFLevel::Off)3040 return;3041 RETURN_IF_NOT_CONTAINS(GuardFlags)3042 CHECK_VA(GuardCFFunctionTable, "__guard_fids_table")3043 CHECK_ABSOLUTE(GuardCFFunctionCount, "__guard_fids_count")3044 CHECK_ABSOLUTE(GuardFlags, "__guard_flags")3045 IF_CONTAINS(GuardAddressTakenIatEntryCount) {3046 CHECK_VA(GuardAddressTakenIatEntryTable, "__guard_iat_table")3047 CHECK_ABSOLUTE(GuardAddressTakenIatEntryCount, "__guard_iat_count")3048 }3049 3050 if (!(ctx.config.guardCF & GuardCFLevel::LongJmp))3051 return;3052 RETURN_IF_NOT_CONTAINS(GuardLongJumpTargetCount)3053 CHECK_VA(GuardLongJumpTargetTable, "__guard_longjmp_table")3054 CHECK_ABSOLUTE(GuardLongJumpTargetCount, "__guard_longjmp_count")3055 3056 if (!(ctx.config.guardCF & GuardCFLevel::EHCont))3057 return;3058 RETURN_IF_NOT_CONTAINS(GuardEHContinuationCount)3059 CHECK_VA(GuardEHContinuationTable, "__guard_eh_cont_table")3060 CHECK_ABSOLUTE(GuardEHContinuationCount, "__guard_eh_cont_count")3061 3062#undef RETURN_IF_NOT_CONTAINS3063#undef IF_CONTAINS3064#undef CHECK_VA3065#undef CHECK_ABSOLUTE3066}3067 3068void Writer::printSummary() {3069 if (!ctx.config.showSummary)3070 return;3071 3072 SmallString<256> buffer;3073 raw_svector_ostream stream(buffer);3074 3075 stream << center_justify("Summary", 80) << '\n'3076 << std::string(80, '-') << '\n';3077 3078 auto print = [&](uint64_t v, StringRef s) {3079 stream << formatv("{0}",3080 fmt_align(formatv("{0:N}", v), AlignStyle::Right, 20))3081 << " " << s << '\n';3082 };3083 3084 bool hasStats = ctx.pdbStats.has_value();3085 3086 print(ctx.objFileInstances.size(),3087 "Input OBJ files (expanded from all cmd-line inputs)");3088 print(ctx.consumedInputsSize,3089 "Size of all consumed OBJ files (non-lazy), in bytes");3090 print(ctx.typeServerSourceMappings.size(), "PDB type server dependencies");3091 print(ctx.precompSourceMappings.size(), "Precomp OBJ dependencies");3092 print(hasStats ? ctx.pdbStats->nbTypeRecords : 0, "Input debug type records");3093 print(hasStats ? ctx.pdbStats->nbTypeRecordsBytes : 0,3094 "Size of all input debug type records, in bytes");3095 print(hasStats ? ctx.pdbStats->nbTPIrecords : 0, "Merged TPI records");3096 print(hasStats ? ctx.pdbStats->nbIPIrecords : 0, "Merged IPI records");3097 print(hasStats ? ctx.pdbStats->strTabSize : 0, "Output PDB strings");3098 print(hasStats ? ctx.pdbStats->globalSymbols : 0, "Global symbol records");3099 print(hasStats ? ctx.pdbStats->moduleSymbols : 0, "Module symbol records");3100 print(hasStats ? ctx.pdbStats->publicSymbols : 0, "Public symbol records");3101 3102 if (hasStats)3103 stream << ctx.pdbStats->largeInputTypeRecs;3104 3105 Msg(ctx) << buffer;3106}3107