diff options
Diffstat (limited to 'lld/COFF')
-rw-r--r-- | lld/COFF/Chunks.cpp | 22 | ||||
-rw-r--r-- | lld/COFF/Chunks.h | 32 | ||||
-rw-r--r-- | lld/COFF/Config.h | 3 | ||||
-rw-r--r-- | lld/COFF/DLL.cpp | 58 | ||||
-rw-r--r-- | lld/COFF/Driver.cpp | 111 | ||||
-rw-r--r-- | lld/COFF/Driver.h | 2 | ||||
-rw-r--r-- | lld/COFF/DriverUtils.cpp | 16 | ||||
-rw-r--r-- | lld/COFF/InputFiles.cpp | 5 | ||||
-rw-r--r-- | lld/COFF/MarkLive.cpp | 5 | ||||
-rw-r--r-- | lld/COFF/Options.td | 10 | ||||
-rw-r--r-- | lld/COFF/PDB.cpp | 9 | ||||
-rw-r--r-- | lld/COFF/SymbolTable.cpp | 79 | ||||
-rw-r--r-- | lld/COFF/SymbolTable.h | 9 | ||||
-rw-r--r-- | lld/COFF/Symbols.cpp | 8 | ||||
-rw-r--r-- | lld/COFF/Symbols.h | 4 | ||||
-rw-r--r-- | lld/COFF/Writer.cpp | 29 |
16 files changed, 284 insertions, 118 deletions
diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp index 01752cd..39fc250 100644 --- a/lld/COFF/Chunks.cpp +++ b/lld/COFF/Chunks.cpp @@ -422,12 +422,6 @@ void SectionChunk::writeTo(uint8_t *buf) const { applyRelocation(buf + rel.VirtualAddress, rel); } - - // Write the offset to EC entry thunk preceding section contents. The low bit - // is always set, so it's effectively an offset from the last byte of the - // offset. - if (Defined *entryThunk = getEntryThunk()) - write32le(buf - sizeof(uint32_t), entryThunk->getRVA() - rva + 1); } void SectionChunk::applyRelocation(uint8_t *off, @@ -881,6 +875,19 @@ void RangeExtensionThunkARM64::writeTo(uint8_t *buf) const { applyArm64Imm(buf + 4, target->getRVA() & 0xfff, 0); } +void SameAddressThunkARM64EC::setDynamicRelocs(COFFLinkerContext &ctx) const { + // Add ARM64X relocations replacing adrp/add instructions with a version using + // the hybrid target. + RangeExtensionThunkARM64 hybridView(ARM64EC, hybridTarget); + uint8_t buf[sizeof(arm64Thunk)]; + hybridView.setRVA(rva); + hybridView.writeTo(buf); + uint32_t addrp = *reinterpret_cast<ulittle32_t *>(buf); + uint32_t add = *reinterpret_cast<ulittle32_t *>(buf + sizeof(uint32_t)); + ctx.dynamicRelocs->set(this, addrp); + ctx.dynamicRelocs->set(Arm64XRelocVal(this, sizeof(uint32_t)), add); +} + LocalImportChunk::LocalImportChunk(COFFLinkerContext &c, Defined *s) : sym(s), ctx(c) { setAlignment(ctx.config.wordsize); @@ -1264,7 +1271,8 @@ void DynamicRelocsChunk::finalize() { } // Set the reloc value. The reloc entry must be allocated beforehand. -void DynamicRelocsChunk::set(uint32_t rva, Arm64XRelocVal value) { +void DynamicRelocsChunk::set(Arm64XRelocVal offset, Arm64XRelocVal value) { + uint32_t rva = offset.get(); auto entry = llvm::find_if(arm64xRelocs, [rva](const Arm64XDynamicRelocEntry &e) { return e.offset.get() == rva; diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h index d03a64c..6d88f5e 100644 --- a/lld/COFF/Chunks.h +++ b/lld/COFF/Chunks.h @@ -193,6 +193,8 @@ public: // allowed ranges. Return the additional space required for the extension. virtual uint32_t extendRanges() { return 0; }; + virtual Defined *getEntryThunk() const { return nullptr; }; + static bool classof(const Chunk *c) { return c->kind() >= OtherKind; } protected: @@ -633,7 +635,7 @@ public: bool verifyRanges() override; uint32_t extendRanges() override; - Defined *exitThunk; + Defined *exitThunk = nullptr; Defined *sym = nullptr; bool extended = false; @@ -675,6 +677,26 @@ private: MachineTypes machine; }; +// A chunk used to guarantee the same address for a function in both views of +// a hybrid image. Similar to RangeExtensionThunkARM64 chunks, it calls the +// target symbol using a BR instruction. It also contains an entry thunk for EC +// compatibility and additional ARM64X relocations that swap targets between +// views. +class SameAddressThunkARM64EC : public RangeExtensionThunkARM64 { +public: + explicit SameAddressThunkARM64EC(Defined *t, Defined *hybridTarget, + Defined *entryThunk) + : RangeExtensionThunkARM64(ARM64EC, t), hybridTarget(hybridTarget), + entryThunk(entryThunk) {} + + Defined *getEntryThunk() const override { return entryThunk; } + void setDynamicRelocs(COFFLinkerContext &ctx) const; + +private: + Defined *hybridTarget; + Defined *entryThunk; +}; + // Windows-specific. // See comments for DefinedLocalImport class. class LocalImportChunk : public NonSectionChunk { @@ -843,13 +865,13 @@ class Arm64XRelocVal { public: Arm64XRelocVal(uint64_t value = 0) : value(value) {} Arm64XRelocVal(Defined *sym, int32_t offset = 0) : sym(sym), value(offset) {} - Arm64XRelocVal(Chunk *chunk, int32_t offset = 0) + Arm64XRelocVal(const Chunk *chunk, int32_t offset = 0) : chunk(chunk), value(offset) {} uint64_t get() const; private: Defined *sym = nullptr; - Chunk *chunk = nullptr; + const Chunk *chunk = nullptr; uint64_t value; }; @@ -884,7 +906,7 @@ public: arm64xRelocs.emplace_back(type, size, offset, value); } - void set(uint32_t rva, Arm64XRelocVal value); + void set(Arm64XRelocVal offset, Arm64XRelocVal value); private: std::vector<Arm64XDynamicRelocEntry> arm64xRelocs; @@ -940,6 +962,8 @@ inline bool Chunk::isHotPatchable() const { inline Defined *Chunk::getEntryThunk() const { if (auto *c = dyn_cast<const SectionChunkEC>(this)) return c->entryThunk; + if (auto *c = dyn_cast<const NonSectionChunk>(this)) + return c->getEntryThunk(); return nullptr; } diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h index 91b6e63..a03bb57 100644 --- a/lld/COFF/Config.h +++ b/lld/COFF/Config.h @@ -223,6 +223,9 @@ struct Configuration { StringRef manifestUIAccess = "'false'"; StringRef manifestFile; + // used for /arm64xsameaddress + std::vector<std::pair<Symbol *, Symbol *>> sameAddresses; + // used for /dwodir StringRef dwoDir; diff --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp index c327da2..3ce8853 100644 --- a/lld/COFF/DLL.cpp +++ b/lld/COFF/DLL.cpp @@ -244,40 +244,36 @@ static const uint8_t thunkX64[] = { }; static const uint8_t tailMergeX64[] = { - 0x51, // push rcx - 0x52, // push rdx - 0x41, 0x50, // push r8 - 0x41, 0x51, // push r9 - 0x48, 0x83, 0xEC, 0x48, // sub rsp, 48h - 0x66, 0x0F, 0x7F, 0x04, 0x24, // movdqa xmmword ptr [rsp], xmm0 - 0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x10, // movdqa xmmword ptr [rsp+10h], xmm1 - 0x66, 0x0F, 0x7F, 0x54, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h], xmm2 - 0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h], xmm3 - 0x48, 0x8B, 0xD0, // mov rdx, rax - 0x48, 0x8D, 0x0D, 0, 0, 0, 0, // lea rcx, [___DELAY_IMPORT_...] - 0xE8, 0, 0, 0, 0, // call __delayLoadHelper2 - 0x66, 0x0F, 0x6F, 0x04, 0x24, // movdqa xmm0, xmmword ptr [rsp] - 0x66, 0x0F, 0x6F, 0x4C, 0x24, 0x10, // movdqa xmm1, xmmword ptr [rsp+10h] - 0x66, 0x0F, 0x6F, 0x54, 0x24, 0x20, // movdqa xmm2, xmmword ptr [rsp+20h] - 0x66, 0x0F, 0x6F, 0x5C, 0x24, 0x30, // movdqa xmm3, xmmword ptr [rsp+30h] - 0x48, 0x83, 0xC4, 0x48, // add rsp, 48h - 0x41, 0x59, // pop r9 - 0x41, 0x58, // pop r8 - 0x5A, // pop rdx - 0x59, // pop rcx - 0xFF, 0xE0, // jmp rax + 0x48, 0x89, 0x4C, 0x24, 0x08, // mov qword ptr [rsp+8], rcx + 0x48, 0x89, 0x54, 0x24, 0x10, // mov qword ptr [rsp+10h], rdx + 0x4C, 0x89, 0x44, 0x24, 0x18, // mov qword ptr [rsp+18h], r8 + 0x4C, 0x89, 0x4C, 0x24, 0x20, // mov qword ptr [rsp+20h], r9 + 0x48, 0x83, 0xEC, 0x68, // sub rsp, 68h + 0x66, 0x0F, 0x7F, 0x44, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h], xmm0 + 0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h], xmm1 + 0x66, 0x0F, 0x7F, 0x54, 0x24, 0x40, // movdqa xmmword ptr [rsp+40h], xmm2 + 0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x50, // movdqa xmmword ptr [rsp+50h], xmm3 + 0x48, 0x8B, 0xD0, // mov rdx, rax + 0x48, 0x8D, 0x0D, 0, 0, 0, 0, // lea rcx, [___DELAY_IMPORT_...] + 0xE8, 0, 0, 0, 0, // call __delayLoadHelper2 + 0x66, 0x0F, 0x6F, 0x44, 0x24, 0x20, // movdqa xmm0, xmmword ptr [rsp+20h] + 0x66, 0x0F, 0x6F, 0x4C, 0x24, 0x30, // movdqa xmm1, xmmword ptr [rsp+30h] + 0x66, 0x0F, 0x6F, 0x54, 0x24, 0x40, // movdqa xmm2, xmmword ptr [rsp+40h] + 0x66, 0x0F, 0x6F, 0x5C, 0x24, 0x50, // movdqa xmm3, xmmword ptr [rsp+50h] + 0x48, 0x8B, 0x4C, 0x24, 0x70, // mov rcx, qword ptr [rsp+70h] + 0x48, 0x8B, 0x54, 0x24, 0x78, // mov rdx, qword ptr [rsp+78h] + 0x4C, 0x8B, 0x84, 0x24, 0x80, 0, 0, 0, // mov r8, qword ptr [rsp+80h] + 0x4C, 0x8B, 0x8C, 0x24, 0x88, 0, 0, 0, // mov r9, qword ptr [rsp+88h] + 0x48, 0x83, 0xC4, 0x68, // add rsp, 68h + 0xFF, 0xE0, // jmp rax }; static const uint8_t tailMergeUnwindInfoX64[] = { 0x01, // Version=1, Flags=UNW_FLAG_NHANDLER - 0x0a, // Size of prolog - 0x05, // Count of unwind codes + 0x18, // Size of prolog + 0x01, // Count of unwind codes 0x00, // No frame register - 0x0a, 0x82, // Offset 0xa: UWOP_ALLOC_SMALL(0x48) - 0x06, 0x02, // Offset 6: UWOP_ALLOC_SMALL(8) - 0x04, 0x02, // Offset 4: UWOP_ALLOC_SMALL(8) - 0x02, 0x02, // Offset 2: UWOP_ALLOC_SMALL(8) - 0x01, 0x02, // Offset 1: UWOP_ALLOC_SMALL(8) + 0x18, 0xC2, // Offset 0x18: UWOP_ALLOC_SMALL(0x68) 0x00, 0x00 // Padding to align on 32-bits }; @@ -378,8 +374,8 @@ public: void writeTo(uint8_t *buf) const override { memcpy(buf, tailMergeX64, sizeof(tailMergeX64)); - write32le(buf + 39, desc->getRVA() - rva - 43); - write32le(buf + 44, helper->getRVA() - rva - 48); + write32le(buf + 54, desc->getRVA() - rva - 58); + write32le(buf + 59, helper->getRVA() - rva - 63); } Chunk *desc = nullptr; diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index 83040b5..7580b469 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -500,7 +500,9 @@ void LinkerDriver::parseDirectives(InputFile *file) { file->symtab.parseAlternateName(arg->getValue()); break; case OPT_arm64xsameaddress: - if (!file->symtab.isEC()) + if (file->symtab.isEC()) + parseSameAddress(arg->getValue()); + else Warn(ctx) << arg->getSpelling() << " is not allowed in non-ARM64EC files (" << toString(file) << ")"; @@ -1318,13 +1320,9 @@ void LinkerDriver::convertResources() { } void LinkerDriver::maybeCreateECExportThunk(StringRef name, Symbol *&sym) { - Defined *def; if (!sym) return; - if (auto undef = dyn_cast<Undefined>(sym)) - def = undef->getDefinedWeakAlias(); - else - def = dyn_cast<Defined>(sym); + Defined *def = sym->getDefined(); if (!def) return; @@ -1356,11 +1354,7 @@ void LinkerDriver::createECExportThunks() { Symbol *sym = ctx.symtab.find(targetName); if (!sym) continue; - Defined *targetSym; - if (auto undef = dyn_cast<Undefined>(sym)) - targetSym = undef->getDefinedWeakAlias(); - else - targetSym = dyn_cast<Defined>(sym); + Defined *targetSym = sym->getDefined(); if (!targetSym) continue; @@ -2303,6 +2297,13 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) { args.filtered(OPT_dependentloadflag, OPT_dependentloadflag_opt)) parseDependentLoadFlags(arg); + for (auto *arg : args.filtered(OPT_arm64xsameaddress)) { + if (ctx.hybridSymtab) + parseSameAddress(arg->getValue()); + else + Warn(ctx) << arg->getSpelling() << " is allowed only on EC targets"; + } + if (tar) { llvm::TimeTraceScope timeScope("Reproducer: response file"); tar->append( @@ -2554,28 +2555,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) { e.symbolName = symtab.mangleMaybe(e.sym); } - // Add weak aliases. Weak aliases is a mechanism to give remaining - // undefined symbols final chance to be resolved successfully. - for (auto pair : symtab.alternateNames) { - StringRef from = pair.first; - StringRef to = pair.second; - Symbol *sym = symtab.find(from); - if (!sym) - continue; - if (auto *u = dyn_cast<Undefined>(sym)) { - if (u->weakAlias) { - // On ARM64EC, anti-dependency aliases are treated as undefined - // symbols unless a demangled symbol aliases a defined one, which - // is part of the implementation. - if (!symtab.isEC() || !u->isAntiDep) - continue; - if (!isa<Undefined>(u->weakAlias) && - !isArm64ECMangledFunctionName(u->getName())) - continue; - } - u->setWeakAlias(symtab.addUndefined(to)); - } - } + symtab.resolveAlternateNames(); }); ctx.forEachActiveSymtab([&](SymbolTable &symtab) { @@ -2697,12 +2677,46 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) { createECExportThunks(); // Resolve remaining undefined symbols and warn about imported locals. + std::vector<Undefined *> aliases; ctx.forEachSymtab( - [&](SymbolTable &symtab) { symtab.resolveRemainingUndefines(); }); + [&](SymbolTable &symtab) { symtab.resolveRemainingUndefines(aliases); }); if (errorCount()) return; + ctx.forEachActiveSymtab([](SymbolTable &symtab) { + symtab.initializeECThunks(); + symtab.initializeLoadConfig(); + }); + + // Identify unreferenced COMDAT sections. + if (config->doGC) { + if (config->mingw) { + // markLive doesn't traverse .eh_frame, but the personality function is + // only reached that way. The proper solution would be to parse and + // traverse the .eh_frame section, like the ELF linker does. + // For now, just manually try to retain the known possible personality + // functions. This doesn't bring in more object files, but only marks + // functions that already have been included to be retained. + ctx.forEachSymtab([&](SymbolTable &symtab) { + for (const char *n : {"__gxx_personality_v0", "__gcc_personality_v0", + "rust_eh_personality"}) { + Defined *d = dyn_cast_or_null<Defined>(symtab.findUnderscore(n)); + if (d && !d->isGCRoot) { + d->isGCRoot = true; + config->gcroot.push_back(d); + } + } + }); + } + + markLive(ctx); + } + + ctx.symtab.initializeSameAddressThunks(); + for (auto alias : aliases) + alias->resolveWeakAlias(); + if (config->mingw) { // Make sure the crtend.o object is the last object file. This object // file can contain terminating section chunks that need to be placed @@ -2794,35 +2808,6 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) { if (auto *arg = args.getLastArg(OPT_print_symbol_order)) config->printSymbolOrder = arg->getValue(); - if (ctx.symtab.isEC()) - ctx.symtab.initializeECThunks(); - ctx.forEachActiveSymtab( - [](SymbolTable &symtab) { symtab.initializeLoadConfig(); }); - - // Identify unreferenced COMDAT sections. - if (config->doGC) { - if (config->mingw) { - // markLive doesn't traverse .eh_frame, but the personality function is - // only reached that way. The proper solution would be to parse and - // traverse the .eh_frame section, like the ELF linker does. - // For now, just manually try to retain the known possible personality - // functions. This doesn't bring in more object files, but only marks - // functions that already have been included to be retained. - ctx.forEachSymtab([&](SymbolTable &symtab) { - for (const char *n : {"__gxx_personality_v0", "__gcc_personality_v0", - "rust_eh_personality"}) { - Defined *d = dyn_cast_or_null<Defined>(symtab.findUnderscore(n)); - if (d && !d->isGCRoot) { - d->isGCRoot = true; - config->gcroot.push_back(d); - } - } - }); - } - - markLive(ctx); - } - // Needs to happen after the last call to addFile(). convertResources(); diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h index 5a9bd5c..b500ac8 100644 --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -214,6 +214,8 @@ private: void parsePDBPageSize(StringRef); void parseSection(StringRef); + void parseSameAddress(StringRef); + // Parses a MS-DOS stub file void parseDosStub(StringRef path); diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index d8b41c7..dc4039f 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -328,6 +328,22 @@ void LinkerDriver::parseSwaprun(StringRef arg) { } while (!arg.empty()); } +void LinkerDriver::parseSameAddress(StringRef arg) { + auto mangledName = getArm64ECMangledFunctionName(arg); + Symbol *sym = ctx.symtab.addUndefined(mangledName ? *mangledName : arg); + + // MSVC appears to generate thunks even for non-hybrid ARM64EC images. + // As a side effect, the native symbol is pulled in. Since this is used + // in the CRT for thread-local constructors, it results in the image + // containing unnecessary native code. As these thunks don't appear to + // be useful, we limit this behavior to actual hybrid targets. This may + // change if compatibility becomes necessary. + if (ctx.config.machine != ARM64X) + return; + Symbol *nativeSym = ctx.hybridSymtab->addUndefined(arg); + ctx.config.sameAddresses.emplace_back(sym, nativeSym); +} + // An RAII temporary file class that automatically removes a temporary file. namespace { class TemporaryFile { diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp index 2a6b63c..c08099b 100644 --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -403,6 +403,11 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber, return nullptr; } + // Those sections are generated by -fembed-bitcode and do not need to be kept + // in executable files. + if (name == ".llvmbc" || name == ".llvmcmd") + return nullptr; + // Object files may have DWARF debug info or MS CodeView debug info // (or both). // diff --git a/lld/COFF/MarkLive.cpp b/lld/COFF/MarkLive.cpp index f40810c..78f5030 100644 --- a/lld/COFF/MarkLive.cpp +++ b/lld/COFF/MarkLive.cpp @@ -49,7 +49,10 @@ void markLive(COFFLinkerContext &ctx) { addSym(file->impchkThunk->exitThunk); }; - addSym = [&](Symbol *b) { + addSym = [&](Symbol *s) { + Defined *b = s->getDefined(); + if (!b) + return; if (auto *sym = dyn_cast<DefinedRegular>(b)) { enqueue(sym->getChunk()); } else if (auto *sym = dyn_cast<DefinedImportData>(b)) { diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td index 2a82fb5..2c393cc 100644 --- a/lld/COFF/Options.td +++ b/lld/COFF/Options.td @@ -31,6 +31,9 @@ multiclass B_priv<string name> { def align : P<"align", "Section alignment">; def aligncomm : P<"aligncomm", "Set common symbol alignment">; def alternatename : P<"alternatename", "Define weak alias">; +def arm64xsameaddress + : P<"arm64xsameaddress", "Generate a thunk for the symbol with the same " + "address in both native and EC views on ARM64X.">; def base : P<"base", "Base address of the program">; def color_diagnostics: Flag<["--"], "color-diagnostics">, HelpText<"Alias for --color-diagnostics=always">; @@ -353,7 +356,13 @@ def fastfail : F<"fastfail">; def kernel : F<"kernel">; def pdbcompress : F<"pdbcompress">; def emitpogophaseinfo : F<"emitpogophaseinfo">; +defm emittoolversioninfo: B< + "emittoolversioninfo", + "Emit a tool version info after DOS header (so-called Rich header, default)", + "Do not emit a tool version info after DOS header (so-called Rich header)">; +def nocoffgrpinfo: F<"nocoffgrpinfo">; def noexp : F<"noexp">; +def novcfeature: F<"novcfeature">; def delay : P_priv<"delay">; def errorreport : P_priv<"errorreport">; @@ -367,4 +376,3 @@ def tlbid : P_priv<"tlbid">; def tlbout : P_priv<"tlbout">; def verbose_all : P_priv<"verbose">; def guardsym : P_priv<"guardsym">; -def arm64xsameaddress : P_priv<"arm64xsameaddress">; diff --git a/lld/COFF/PDB.cpp b/lld/COFF/PDB.cpp index a54ea40..94eeae2 100644 --- a/lld/COFF/PDB.cpp +++ b/lld/COFF/PDB.cpp @@ -1135,9 +1135,12 @@ static pdb::BulkPublic createPublic(COFFLinkerContext &ctx, Defined *def) { pub.setFlags(flags); OutputSection *os = ctx.getOutputSection(def->getChunk()); - assert(os && "all publics should be in final image"); - pub.Offset = def->getRVA() - os->getRVA(); - pub.Segment = os->sectionIndex; + assert((os || !def->getChunk()->getSize()) && + "all publics should be in final image"); + if (os) { + pub.Offset = def->getRVA() - os->getRVA(); + pub.Segment = os->sectionIndex; + } return pub; } diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp index 0062df5..de04cdf 100644 --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -452,7 +452,7 @@ void SymbolTable::reportUnresolvable() { reportProblemSymbols(undefs, /*localImports=*/nullptr, true); } -void SymbolTable::resolveRemainingUndefines() { +void SymbolTable::resolveRemainingUndefines(std::vector<Undefined *> &aliases) { llvm::TimeTraceScope timeScope("Resolve remaining undefined symbols"); SmallPtrSet<Symbol *, 8> undefs; DenseMap<Symbol *, Symbol *> localImports; @@ -468,8 +468,10 @@ void SymbolTable::resolveRemainingUndefines() { StringRef name = undef->getName(); // A weak alias may have been resolved, so check for that. - if (undef->resolveWeakAlias()) + if (undef->getWeakAlias()) { + aliases.push_back(undef); continue; + } // If we can resolve a symbol by removing __imp_ prefix, do that. // This odd rule is for compatibility with MSVC linker. @@ -620,10 +622,10 @@ void SymbolTable::initializeECThunks() { return; for (auto it : entryThunks) { - auto *to = dyn_cast<Defined>(it.second); + Defined *to = it.second->getDefined(); if (!to) continue; - auto *from = dyn_cast<DefinedRegular>(it.first); + auto *from = dyn_cast_or_null<DefinedRegular>(it.first->getDefined()); // We need to be able to add padding to the function and fill it with an // offset to its entry thunks. To ensure that padding the function is // feasible, functions are required to be COMDAT symbols with no offset. @@ -642,7 +644,8 @@ void SymbolTable::initializeECThunks() { Symbol *sym = exitThunks.lookup(file->thunkSym); if (!sym) sym = exitThunks.lookup(file->impECSym); - file->impchkThunk->exitThunk = dyn_cast_or_null<Defined>(sym); + if (sym) + file->impchkThunk->exitThunk = sym->getDefined(); } // On ARM64EC, the __imp_ symbol references the auxiliary IAT, while the @@ -659,6 +662,35 @@ void SymbolTable::initializeECThunks() { }); } +void SymbolTable::initializeSameAddressThunks() { + for (auto iter : ctx.config.sameAddresses) { + auto sym = dyn_cast_or_null<DefinedRegular>(iter.first->getDefined()); + if (!sym || !sym->isLive()) + continue; + auto nativeSym = + dyn_cast_or_null<DefinedRegular>(iter.second->getDefined()); + if (!nativeSym || !nativeSym->isLive()) + continue; + Defined *entryThunk = sym->getChunk()->getEntryThunk(); + if (!entryThunk) + continue; + + // Replace symbols with symbols referencing the thunk. Store the original + // symbol as equivalent DefinedSynthetic instances for use in the thunk + // itself. + auto symClone = make<DefinedSynthetic>(sym->getName(), sym->getChunk(), + sym->getValue()); + auto nativeSymClone = make<DefinedSynthetic>( + nativeSym->getName(), nativeSym->getChunk(), nativeSym->getValue()); + SameAddressThunkARM64EC *thunk = + make<SameAddressThunkARM64EC>(nativeSymClone, symClone, entryThunk); + sameAddressThunks.push_back(thunk); + + replaceSymbol<DefinedSynthetic>(sym, sym->getName(), thunk); + replaceSymbol<DefinedSynthetic>(nativeSym, nativeSym->getName(), thunk); + } +} + Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f, bool overrideLazy) { auto [s, wasInserted] = insert(name, f); @@ -1344,6 +1376,43 @@ void SymbolTable::parseAlternateName(StringRef s) { alternateNames.insert(it, std::make_pair(from, to)); } +void SymbolTable::resolveAlternateNames() { + // Add weak aliases. Weak aliases is a mechanism to give remaining + // undefined symbols final chance to be resolved successfully. + for (auto pair : alternateNames) { + StringRef from = pair.first; + StringRef to = pair.second; + Symbol *sym = find(from); + if (!sym) + continue; + if (auto *u = dyn_cast<Undefined>(sym)) { + if (u->weakAlias) { + // On ARM64EC, anti-dependency aliases are treated as undefined + // symbols unless a demangled symbol aliases a defined one, which + // is part of the implementation. + if (!isEC() || !u->isAntiDep) + continue; + if (!isa<Undefined>(u->weakAlias) && + !isArm64ECMangledFunctionName(u->getName())) + continue; + } + + // Check if the destination symbol is defined. If not, skip it. + // It may still be resolved later if more input files are added. + // Also skip anti-dependency targets, as they can't be chained anyway. + Symbol *toSym = find(to); + if (!toSym) + continue; + auto toUndef = dyn_cast<Undefined>(toSym); + if (toUndef && (!toUndef->weakAlias || toUndef->isAntiDep)) + continue; + if (toSym->isLazy()) + forceLazy(toSym); + u->setWeakAlias(toSym); + } + } +} + // Parses /aligncomm option argument. void SymbolTable::parseAligncomm(StringRef s) { auto [name, align] = s.split(','); diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h index 15e2644..aadd366 100644 --- a/lld/COFF/SymbolTable.h +++ b/lld/COFF/SymbolTable.h @@ -31,6 +31,7 @@ class DefinedAbsolute; class DefinedRegular; class ImportThunkChunk; class LazyArchive; +class SameAddressThunkARM64EC; class SectionChunk; class Symbol; @@ -67,7 +68,10 @@ public: // Try to resolve any undefined symbols and update the symbol table // accordingly, then print an error message for any remaining undefined // symbols and warn about imported local symbols. - void resolveRemainingUndefines(); + void resolveRemainingUndefines(std::vector<Undefined *> &aliases); + + // Try to resolve undefined symbols with alternate names. + void resolveAlternateNames(); // Load lazy objects that are needed for MinGW automatic import and for // doing stdcall fixups. @@ -137,6 +141,7 @@ public: void addEntryThunk(Symbol *from, Symbol *to); void addExitThunk(Symbol *from, Symbol *to); void initializeECThunks(); + void initializeSameAddressThunks(); void reportDuplicate(Symbol *existing, InputFile *newFile, SectionChunk *newSc = nullptr, @@ -156,6 +161,8 @@ public: // A list of EC EXP+ symbols. std::vector<Symbol *> expSymbols; + std::vector<SameAddressThunkARM64EC *> sameAddressThunks; + // A list of DLL exports. std::vector<Export> exports; llvm::DenseSet<StringRef> directivesExports; diff --git a/lld/COFF/Symbols.cpp b/lld/COFF/Symbols.cpp index b571ce9..ba4f95d1 100644 --- a/lld/COFF/Symbols.cpp +++ b/lld/COFF/Symbols.cpp @@ -91,6 +91,14 @@ bool Symbol::isLive() const { return true; } +Defined *Symbol::getDefined() { + if (auto d = dyn_cast<Defined>(this)) + return d; + if (auto u = dyn_cast<Undefined>(this)) + return u->getDefinedWeakAlias(); + return nullptr; +} + void Symbol::replaceKeepingName(Symbol *other, size_t size) { StringRef origName = getName(); memcpy(this, other, size); diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h index fd3d8ce..c86ded8 100644 --- a/lld/COFF/Symbols.h +++ b/lld/COFF/Symbols.h @@ -95,6 +95,10 @@ public: symbolKind == LazyDLLSymbolKind; } + // Get the Defined symbol associated with this symbol, either itself or its + // weak alias. + Defined *getDefined(); + private: void computeName(); diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 0765618..21ab9d1 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -314,6 +314,7 @@ private: uint32_t dataDirOffset64; OutputSection *textSec; + OutputSection *wowthkSec; OutputSection *hexpthkSec; OutputSection *bssSec; OutputSection *rdataSec; @@ -1076,8 +1077,10 @@ void Writer::createSections() { // Try to match the section order used by link.exe. textSec = createSection(".text", code | r | x); - if (isArm64EC(ctx.config.machine)) + if (isArm64EC(ctx.config.machine)) { + wowthkSec = createSection(".wowthk", code | r | x); hexpthkSec = createSection(".hexpthk", code | r | x); + } bssSec = createSection(".bss", bss | r | w); rdataSec = createSection(".rdata", data | r); buildidSec = createSection(".buildid", data | r); @@ -1129,6 +1132,9 @@ void Writer::createSections() { if (hasIdata) locateImportTables(); + for (auto thunk : ctx.symtab.sameAddressThunks) + wowthkSec->addChunk(thunk); + // Then create an OutputSection for each section. // '$' and all following characters in input section names are // discarded when determining output section. So, .text$foo @@ -2310,6 +2316,14 @@ void Writer::createECChunks() { ctx.symtab.findUnderscore("__arm64x_redirection_metadata"); replaceSymbol<DefinedSynthetic>(entryPointsSym, entryPointsSym->getName(), entryPoints); + + for (auto thunk : ctx.symtab.sameAddressThunks) { + // Relocation values are set later in setECSymbols. + ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t), + thunk); + ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t), + Arm64XRelocVal(thunk, sizeof(uint32_t))); + } } // MinGW specific. Gather all relocations that are imported from a DLL even @@ -2519,6 +2533,9 @@ void Writer::setECSymbols() { chpeSym->getRVA() + offsetof(chpe_metadata, ExtraRFETableSize), pdata.last->getRVA() + pdata.last->getSize() - pdata.first->getRVA()); } + + for (SameAddressThunkARM64EC *thunk : ctx.symtab.sameAddressThunks) + thunk->setDynamicRelocs(ctx); } // Write section contents to a mmap'ed file. @@ -2544,7 +2561,15 @@ void Writer::writeSections() { } parallelForEach(sec->chunks, [&](Chunk *c) { - c->writeTo(secBuf + c->getRVA() - sec->getRVA()); + uint8_t *buf = secBuf + c->getRVA() - sec->getRVA(); + c->writeTo(buf); + + // Write the offset to EC entry thunk preceding section contents. The low + // bit is always set, so it's effectively an offset from the last byte of + // the offset. + if (Defined *entryThunk = c->getEntryThunk()) + write32le(buf - sizeof(uint32_t), + entryThunk->getRVA() - c->getRVA() + 1); }); } } |