diff options
Diffstat (limited to 'lld')
-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/Driver.cpp | 88 | ||||
-rw-r--r-- | lld/COFF/Driver.h | 2 | ||||
-rw-r--r-- | lld/COFF/DriverUtils.cpp | 16 | ||||
-rw-r--r-- | lld/COFF/MarkLive.cpp | 5 | ||||
-rw-r--r-- | lld/COFF/Options.td | 4 | ||||
-rw-r--r-- | lld/COFF/SymbolTable.cpp | 42 | ||||
-rw-r--r-- | lld/COFF/SymbolTable.h | 6 | ||||
-rw-r--r-- | lld/COFF/Symbols.cpp | 8 | ||||
-rw-r--r-- | lld/COFF/Symbols.h | 4 | ||||
-rw-r--r-- | lld/COFF/Writer.cpp | 29 | ||||
-rw-r--r-- | lld/ELF/Arch/LoongArch.cpp | 9 | ||||
-rw-r--r-- | lld/ELF/Arch/Mips.cpp | 4 | ||||
-rw-r--r-- | lld/ELF/Config.h | 2 | ||||
-rw-r--r-- | lld/ELF/Driver.cpp | 1 | ||||
-rw-r--r-- | lld/ELF/InputSection.cpp | 5 | ||||
-rw-r--r-- | lld/ELF/Relocations.cpp | 29 | ||||
-rw-r--r-- | lld/ELF/Relocations.h | 1 | ||||
-rw-r--r-- | lld/ELF/SyntheticSections.cpp | 56 | ||||
-rw-r--r-- | lld/ELF/SyntheticSections.h | 71 | ||||
-rw-r--r-- | lld/ELF/Target.cpp | 3 | ||||
-rw-r--r-- | lld/ELF/Target.h | 1 | ||||
-rw-r--r-- | lld/ELF/Writer.cpp | 5 | ||||
-rw-r--r-- | lld/test/COFF/arm64x-sameaddress.test | 107 | ||||
-rw-r--r-- | lld/test/ELF/loongarch-relax-pc-hi20-lo12.s | 59 |
27 files changed, 426 insertions, 188 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/Driver.cpp b/lld/COFF/Driver.cpp index 570b8f9..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( @@ -2676,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 @@ -2773,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/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 0d66b49..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">; @@ -373,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/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp index 189e75d..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); diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h index 7eb0676..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,7 @@ 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(); @@ -140,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, @@ -159,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); }); } } diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp index 15dcddb..a145530 100644 --- a/lld/ELF/Arch/LoongArch.cpp +++ b/lld/ELF/Arch/LoongArch.cpp @@ -809,10 +809,13 @@ static void relaxPCHi20Lo12(Ctx &ctx, const InputSection &sec, size_t i, // address. // Meanwhile skip undefined, preemptible and STT_GNU_IFUNC symbols, because // these symbols may be resolve in runtime. + // Moreover, relaxation can only occur if the addends of both relocations are + // zero for GOT references. if (rHi20.type == R_LARCH_GOT_PC_HI20 && - (!rHi20.sym->isDefined() || rHi20.sym->isPreemptible || - rHi20.sym->isGnuIFunc() || - (ctx.arg.isPic && !cast<Defined>(*rHi20.sym).section))) + (!rHi20.sym || rHi20.sym != rLo12.sym || !rHi20.sym->isDefined() || + rHi20.sym->isPreemptible || rHi20.sym->isGnuIFunc() || + (ctx.arg.isPic && !cast<Defined>(*rHi20.sym).section) || + rHi20.addend != 0 || rLo12.addend != 0)) return; uint64_t dest = 0; diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp index 91c7f15..f88b021c 100644 --- a/lld/ELF/Arch/Mips.cpp +++ b/lld/ELF/Arch/Mips.cpp @@ -40,6 +40,10 @@ public: }; } // namespace +uint64_t elf::getMipsPageAddr(uint64_t addr) { + return (addr + 0x8000) & ~0xffff; +} + template <class ELFT> MIPS<ELFT>::MIPS(Ctx &ctx) : TargetInfo(ctx) { gotPltHeaderEntriesNum = 2; defaultMaxPageSize = 65536; diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index e7622dd..a83a4c1 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -702,6 +702,8 @@ struct Ctx : CommonLinkerContext { std::unique_ptr<llvm::TarWriter> tar; // InputFile for linker created symbols with no source location. InputFile *internalFile = nullptr; + // Dummy Undefined for relocations without a symbol. + Undefined *dummySym = nullptr; // True if symbols can be exported (isExported) or preemptible. bool hasDynsym = false; // True if SHT_LLVM_SYMPART is used. diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 91e11bc..6c2f318 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -3140,6 +3140,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) { ctx.symtab->insert(arg->getValue())->traced = true; ctx.internalFile = createInternalFile(ctx, "<internal>"); + ctx.dummySym = make<Undefined>(ctx.internalFile, "", STB_LOCAL, 0, 0); // Handle -u/--undefined before input files. If both a.a and b.so define foo, // -u foo a.a b.so will extract a.a. diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 68e3feb..784ff7c 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -861,6 +861,11 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r, return ctx.in.mipsGot->getVA() + ctx.in.mipsGot->getPageEntryOffset(file, *r.sym, a) - ctx.in.mipsGot->getGp(file); + case RE_MIPS_OSEC_LOCAL_PAGE: + // This is used by the MIPS multi-GOT implementation. It relocates + // addresses of 64kb pages that lie inside the output section that sym is + // a representative for. + return getMipsPageAddr(r.sym->getOutputSection()->addr) + a; case RE_MIPS_GOT_OFF: case RE_MIPS_GOT_OFF32: // In case of MIPS if a GOT relocation has non-zero addend this addend diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index bd22fe2..32ac28d6 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -885,10 +885,12 @@ static void addPltEntry(Ctx &ctx, PltSection &plt, GotPltSection &gotPlt, RelocationBaseSection &rel, RelType type, Symbol &sym) { plt.addEntry(sym); gotPlt.addEntry(sym); - rel.addReloc({type, &gotPlt, sym.getGotPltOffset(ctx), - sym.isPreemptible ? DynamicReloc::AgainstSymbol - : DynamicReloc::AddendOnlyWithTargetVA, - sym, 0, R_ABS}); + if (sym.isPreemptible) + rel.addReloc( + {type, &gotPlt, sym.getGotPltOffset(ctx), true, sym, 0, R_ADDEND}); + else + rel.addReloc( + {type, &gotPlt, sym.getGotPltOffset(ctx), false, sym, 0, R_ABS}); } void elf::addGotEntry(Ctx &ctx, Symbol &sym) { @@ -897,9 +899,8 @@ void elf::addGotEntry(Ctx &ctx, Symbol &sym) { // If preemptible, emit a GLOB_DAT relocation. if (sym.isPreemptible) { - ctx.mainPart->relaDyn->addReloc({ctx.target->gotRel, ctx.in.got.get(), off, - DynamicReloc::AgainstSymbol, sym, 0, - R_ABS}); + ctx.mainPart->relaDyn->addReloc( + {ctx.target->gotRel, ctx.in.got.get(), off, true, sym, 0, R_ADDEND}); return; } @@ -920,15 +921,13 @@ static void addGotAuthEntry(Ctx &ctx, Symbol &sym) { // If preemptible, emit a GLOB_DAT relocation. if (sym.isPreemptible) { ctx.mainPart->relaDyn->addReloc({R_AARCH64_AUTH_GLOB_DAT, ctx.in.got.get(), - off, DynamicReloc::AgainstSymbol, sym, 0, - R_ABS}); + off, true, sym, 0, R_ADDEND}); return; } // Signed GOT requires dynamic relocation. ctx.in.got->getPartition(ctx).relaDyn->addReloc( - {R_AARCH64_AUTH_RELATIVE, ctx.in.got.get(), off, - DynamicReloc::AddendOnlyWithTargetVA, sym, 0, R_ABS}); + {R_AARCH64_AUTH_RELATIVE, ctx.in.got.get(), off, false, sym, 0, R_ABS}); } static void addTpOffsetGotEntry(Ctx &ctx, Symbol &sym) { @@ -1159,9 +1158,8 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, sec->addReloc({expr, type, offset, addend, &sym}); part.relrAuthDyn->relocs.push_back({sec, sec->relocs().size() - 1}); } else { - part.relaDyn->addReloc({R_AARCH64_AUTH_RELATIVE, sec, offset, - DynamicReloc::AddendOnlyWithTargetVA, sym, - addend, R_ABS}); + part.relaDyn->addReloc({R_AARCH64_AUTH_RELATIVE, sec, offset, false, + sym, addend, R_ABS}); } return; } @@ -1948,13 +1946,12 @@ void elf::postScanRelocations(Ctx &ctx) { GotSection *got = ctx.in.got.get(); if (ctx.needsTlsLd.load(std::memory_order_relaxed) && got->addTlsIndex()) { - static Undefined dummy(ctx.internalFile, "", STB_LOCAL, 0, 0); if (ctx.arg.shared) ctx.mainPart->relaDyn->addReloc( {ctx.target->tlsModuleIndexRel, got, got->getTlsIndexOff()}); else got->addConstant({R_ADDEND, ctx.target->symbolicRel, - got->getTlsIndexOff(), 1, &dummy}); + got->getTlsIndexOff(), 1, ctx.dummySym}); } assert(ctx.symAux.size() == 1); diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h index 02ddf70..c1c4860 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -110,6 +110,7 @@ enum RelExpr { RE_MIPS_GOT_LOCAL_PAGE, RE_MIPS_GOT_OFF, RE_MIPS_GOT_OFF32, + RE_MIPS_OSEC_LOCAL_PAGE, RE_MIPS_TLSGD, RE_MIPS_TLSLD, RE_PPC32_PLTREL, diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index efec41a..0d87f9a 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -769,10 +769,6 @@ void GotSection::writeTo(uint8_t *buf) { } } -static uint64_t getMipsPageAddr(uint64_t addr) { - return (addr + 0x8000) & ~0xffff; -} - static uint64_t getMipsPageCount(uint64_t size) { return (size + 0xfffe) / 0xffff + 1; } @@ -786,7 +782,7 @@ void MipsGotSection::addEntry(InputFile &file, Symbol &sym, int64_t addend, FileGot &g = getGot(file); if (expr == RE_MIPS_GOT_LOCAL_PAGE) { if (const OutputSection *os = sym.getOutputSection()) - g.pagesMap.insert({os, {}}); + g.pagesMap.insert({os, {&sym}}); else g.local16.insert({{nullptr, getMipsPageAddr(sym.getVA(ctx, addend))}, 0}); } else if (sym.isTls()) @@ -1066,8 +1062,7 @@ void MipsGotSection::build() { // be allocated before us in the static TLS block. if (s->isPreemptible || ctx.arg.shared) ctx.mainPart->relaDyn->addReloc( - {ctx.target->tlsGotRel, this, offset, - DynamicReloc::AgainstSymbolWithTargetVA, *s, 0, R_ABS}); + {ctx.target->tlsGotRel, this, offset, true, *s, 0, R_ABS}); } for (std::pair<Symbol *, size_t> &p : got.dynTlsSymbols) { Symbol *s = p.first; @@ -1115,15 +1110,16 @@ void MipsGotSection::build() { size_t pageCount = l.second.count; for (size_t pi = 0; pi < pageCount; ++pi) { uint64_t offset = (l.second.firstIndex + pi) * ctx.arg.wordsize; - ctx.mainPart->relaDyn->addReloc({ctx.target->relativeRel, this, offset, - l.first, int64_t(pi * 0x10000)}); + ctx.mainPart->relaDyn->addReloc( + {ctx.target->relativeRel, this, offset, false, *l.second.repSym, + int64_t(pi * 0x10000), RE_MIPS_OSEC_LOCAL_PAGE}); } } for (const std::pair<GotEntry, size_t> &p : got.local16) { uint64_t offset = p.second * ctx.arg.wordsize; ctx.mainPart->relaDyn->addReloc({ctx.target->relativeRel, this, offset, - DynamicReloc::AddendOnlyWithTargetVA, - *p.first.first, p.first.second, R_ABS}); + false, *p.first.first, p.first.second, + R_ABS}); } } } @@ -1646,24 +1642,10 @@ uint64_t DynamicReloc::getOffset() const { } int64_t DynamicReloc::computeAddend(Ctx &ctx) const { - switch (kind) { - case AddendOnly: - assert(sym == nullptr); - return addend; - case AgainstSymbol: - assert(sym != nullptr); - return addend; - case AddendOnlyWithTargetVA: - case AgainstSymbolWithTargetVA: { - uint64_t ca = inputSec->getRelocTargetVA( - ctx, Relocation{expr, type, 0, addend, sym}, getOffset()); - return ctx.arg.is64 ? ca : SignExtend64<32>(ca); - } - case MipsMultiGotPage: - assert(sym == nullptr); - return getMipsPageAddr(outputSec->addr) + addend; - } - llvm_unreachable("Unknown DynamicReloc::Kind enum"); + assert(!isFinal && "addend already computed"); + uint64_t ca = inputSec->getRelocTargetVA( + ctx, Relocation{expr, type, 0, addend, sym}, getOffset()); + return ctx.arg.is64 ? ca : SignExtend64<32>(ca); } uint32_t DynamicReloc::getSymIndex(SymbolTableBaseSection *symTab) const { @@ -1691,8 +1673,8 @@ RelocationBaseSection::RelocationBaseSection(Ctx &ctx, StringRef name, void RelocationBaseSection::addSymbolReloc( RelType dynType, InputSectionBase &isec, uint64_t offsetInSec, Symbol &sym, int64_t addend, std::optional<RelType> addendRelType) { - addReloc(DynamicReloc::AgainstSymbol, dynType, isec, offsetInSec, sym, addend, - R_ADDEND, addendRelType ? *addendRelType : ctx.target->noneRel); + addReloc(true, dynType, isec, offsetInSec, sym, addend, R_ADDEND, + addendRelType ? *addendRelType : ctx.target->noneRel); } void RelocationBaseSection::addAddendOnlyRelocIfNonPreemptible( @@ -1700,11 +1682,9 @@ void RelocationBaseSection::addAddendOnlyRelocIfNonPreemptible( RelType addendRelType) { // No need to write an addend to the section for preemptible symbols. if (sym.isPreemptible) - addReloc({dynType, &isec, offsetInSec, DynamicReloc::AgainstSymbol, sym, 0, - R_ABS}); + addReloc({dynType, &isec, offsetInSec, true, sym, 0, R_ADDEND}); else - addReloc(DynamicReloc::AddendOnlyWithTargetVA, dynType, isec, offsetInSec, - sym, 0, R_ABS, addendRelType); + addReloc(false, dynType, isec, offsetInSec, sym, 0, R_ABS, addendRelType); } void RelocationBaseSection::mergeRels() { @@ -1744,17 +1724,17 @@ void RelocationBaseSection::finalizeContents() { } } -void DynamicReloc::computeRaw(Ctx &ctx, SymbolTableBaseSection *symt) { +void DynamicReloc::finalize(Ctx &ctx, SymbolTableBaseSection *symt) { r_offset = getOffset(); r_sym = getSymIndex(symt); addend = computeAddend(ctx); - kind = AddendOnly; // Catch errors + isFinal = true; // Catch errors } void RelocationBaseSection::computeRels() { SymbolTableBaseSection *symTab = getPartition(ctx).dynSymTab.get(); parallelForEach(relocs, [&ctx = ctx, symTab](DynamicReloc &rel) { - rel.computeRaw(ctx, symTab); + rel.finalize(ctx, symTab); }); auto irelative = std::stable_partition( diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h index 5f01513..223dfe3 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -327,9 +327,11 @@ private: size_t startIndex = 0; struct PageBlock { + Symbol *repSym; // Representative symbol for the OutputSection size_t firstIndex; size_t count; - PageBlock() : firstIndex(0), count(0) {} + PageBlock(Symbol *repSym = nullptr) + : repSym(repSym), firstIndex(0), count(0) {} }; // Map output sections referenced by MIPS GOT relocations @@ -418,61 +420,31 @@ private: class DynamicReloc { public: - enum Kind { - /// The resulting dynamic relocation does not reference a symbol (#sym must - /// be nullptr) and uses #addend as the result of computeAddend(ctx). - AddendOnly, - /// The resulting dynamic relocation will not reference a symbol: #sym is - /// only used to compute the addend with InputSection::getRelocTargetVA(). - /// Useful for various relative and TLS relocations (e.g. R_X86_64_TPOFF64). - AddendOnlyWithTargetVA, - /// The resulting dynamic relocation references symbol #sym from the dynamic - /// symbol table and uses #addend as the value of computeAddend(ctx). - AgainstSymbol, - /// The resulting dynamic relocation references symbol #sym from the dynamic - /// symbol table and uses InputSection::getRelocTargetVA() + #addend for the - /// final addend. It can be used for relocations that write the symbol VA as - // the addend (e.g. R_MIPS_TLS_TPREL64) but still reference the symbol. - AgainstSymbolWithTargetVA, - /// This is used by the MIPS multi-GOT implementation. It relocates - /// addresses of 64kb pages that lie inside the output section. - MipsMultiGotPage, - }; - /// This constructor records a relocation against a symbol. + /// This constructor records a normal relocation. DynamicReloc(RelType type, const InputSectionBase *inputSec, - uint64_t offsetInSec, Kind kind, Symbol &sym, int64_t addend, - RelExpr expr) + uint64_t offsetInSec, bool isAgainstSymbol, Symbol &sym, + int64_t addend, RelExpr expr) : sym(&sym), inputSec(inputSec), offsetInSec(offsetInSec), type(type), - addend(addend), kind(kind), expr(expr) {} + addend(addend), isAgainstSymbol(isAgainstSymbol), isFinal(false), + expr(expr) {} /// This constructor records a relative relocation with no symbol. DynamicReloc(RelType type, const InputSectionBase *inputSec, uint64_t offsetInSec, int64_t addend = 0) - : sym(nullptr), inputSec(inputSec), offsetInSec(offsetInSec), type(type), - addend(addend), kind(AddendOnly), expr(R_ADDEND) {} - /// This constructor records dynamic relocation settings used by the MIPS - /// multi-GOT implementation. - DynamicReloc(RelType type, const InputSectionBase *inputSec, - uint64_t offsetInSec, const OutputSection *outputSec, - int64_t addend) - : sym(nullptr), outputSec(outputSec), inputSec(inputSec), - offsetInSec(offsetInSec), type(type), addend(addend), - kind(MipsMultiGotPage), expr(R_ADDEND) {} + : DynamicReloc(type, inputSec, offsetInSec, false, + *inputSec->getCtx().dummySym, addend, R_ADDEND) {} uint64_t getOffset() const; uint32_t getSymIndex(SymbolTableBaseSection *symTab) const; - bool needsDynSymIndex() const { - return kind == AgainstSymbol || kind == AgainstSymbolWithTargetVA; - } + bool needsDynSymIndex() const { return isAgainstSymbol; } /// Computes the addend of the dynamic relocation. Note that this is not the /// same as the #addend member variable as it may also include the symbol /// address/the address of the corresponding GOT entry/etc. int64_t computeAddend(Ctx &) const; - void computeRaw(Ctx &, SymbolTableBaseSection *symt); + void finalize(Ctx &, SymbolTableBaseSection *symt); Symbol *sym; - const OutputSection *outputSec = nullptr; const InputSectionBase *inputSec; uint64_t offsetInSec; uint64_t r_offset; @@ -483,7 +455,15 @@ public: int64_t addend; private: - Kind kind; + /// Whether this was constructed with a Kind of AgainstSymbol. + LLVM_PREFERRED_TYPE(bool) + uint8_t isAgainstSymbol : 1; + + /// The resulting dynamic relocation has already had its addend computed. + /// Calling computeAddend() is an error. + LLVM_PREFERRED_TYPE(bool) + uint8_t isFinal : 1; + // The kind of expression used to calculate the added (required e.g. for // relative GOT relocations). RelExpr expr; @@ -528,8 +508,8 @@ public: uint64_t offsetInSec, Symbol &sym, int64_t addend, RelType addendRelType, RelExpr expr) { assert(expr != R_ADDEND && "expected non-addend relocation expression"); - addReloc<shard>(DynamicReloc::AddendOnlyWithTargetVA, dynType, isec, - offsetInSec, sym, addend, expr, addendRelType); + addReloc<shard>(false, dynType, isec, offsetInSec, sym, addend, expr, + addendRelType); } /// Add a dynamic relocation using the target address of \p sym as the addend /// if \p sym is non-preemptible. Otherwise add a relocation against \p sym. @@ -538,14 +518,15 @@ public: uint64_t offsetInSec, Symbol &sym, RelType addendRelType); template <bool shard = false> - void addReloc(DynamicReloc::Kind kind, RelType dynType, InputSectionBase &sec, + void addReloc(bool isAgainstSymbol, RelType dynType, InputSectionBase &sec, uint64_t offsetInSec, Symbol &sym, int64_t addend, RelExpr expr, RelType addendRelType) { // Write the addends to the relocated address if required. We skip // it if the written value would be zero. if (ctx.arg.writeAddends && (expr != R_ADDEND || addend != 0)) sec.addReloc({expr, addendRelType, offsetInSec, addend, &sym}); - addReloc<shard>({dynType, &sec, offsetInSec, kind, sym, addend, expr}); + addReloc<shard>( + {dynType, &sec, offsetInSec, isAgainstSymbol, sym, addend, expr}); } bool isNeeded() const override { return !relocs.empty() || diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index ad7d57d..4946484 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -105,10 +105,9 @@ ErrorPlace elf::getErrorPlace(Ctx &ctx, const uint8_t *loc) { if (isecLoc <= loc && loc < isecLoc + isec->getSize()) { std::string objLoc = isec->getLocation(loc - isecLoc); // Return object file location and source file location. - Undefined dummy(ctx.internalFile, "", STB_LOCAL, 0, 0); ELFSyncStream msg(ctx, DiagLevel::None); if (isec->file) - msg << isec->getSrcMsg(dummy, loc - isecLoc); + msg << isec->getSrcMsg(*ctx.dummySym, loc - isecLoc); return {isec, objLoc + ": ", std::string(msg.str())}; } } diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index f4a6d83..fdc0c20 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -214,6 +214,7 @@ void processArmCmseSymbols(Ctx &); template <class ELFT> uint32_t calcMipsEFlags(Ctx &); uint8_t getMipsFpAbiFlag(Ctx &, InputFile *file, uint8_t oldFlag, uint8_t newFlag); +uint64_t getMipsPageAddr(uint64_t addr); bool isMipsN32Abi(Ctx &, const InputFile &f); bool isMicroMips(Ctx &); bool isMipsR6(Ctx &); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index c3c0578..2b0e097 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1586,9 +1586,8 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() { if (isInt<32>(reloc.sym->getVA(ctx, reloc.addend))) return false; part.relaDyn->addReloc({R_AARCH64_AUTH_RELATIVE, elem.inputSec, - reloc.offset, - DynamicReloc::AddendOnlyWithTargetVA, - *reloc.sym, reloc.addend, R_ABS}); + reloc.offset, false, *reloc.sym, + reloc.addend, R_ABS}); return true; }); changed |= (it != part.relrAuthDyn->relocs.end()); diff --git a/lld/test/COFF/arm64x-sameaddress.test b/lld/test/COFF/arm64x-sameaddress.test index c69be9d..819d19b 100644 --- a/lld/test/COFF/arm64x-sameaddress.test +++ b/lld/test/COFF/arm64x-sameaddress.test @@ -3,16 +3,103 @@ RUN: split-file %s %t.dir && cd %t.dir RUN: llvm-mc -filetype=obj -triple=arm64ec-windows func-arm64ec.s -o func-arm64ec.obj RUN: llvm-mc -filetype=obj -triple=aarch64-windows func-arm64.s -o func-arm64.obj +RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ref-arm64ec.s -o ref-arm64ec.obj +RUN: llvm-mc -filetype=obj -triple=aarch64-windows ref-arm64.s -o ref-arm64.obj RUN: llvm-mc -filetype=obj -triple=arm64ec-windows drectve.s -o drectve.obj RUN: llvm-mc -filetype=obj -triple=aarch64-windows drectve.s -o drectve-arm64.obj RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64.s -o loadconfig-arm64.obj RUN: lld-link -machine:arm64x -dll -noentry -out:out.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \ -RUN: func-arm64.obj func-arm64ec.obj drectve.obj +RUN: func-arm64.obj func-arm64ec.obj ref-arm64.obj ref-arm64ec.obj drectve.obj + +RUN: llvm-objdump -d out.dll | FileCheck --check-prefix=DISASM %s +DISASM: 000000180001000 <.text>: +DISASM-NEXT: 180001000: d2800020 mov x0, #0x1 // =1 +DISASM-NEXT: 180001004: d65f03c0 ret +DISASM-NEXT: ... +DISASM-NEXT: 180002000: 00000019 udf #0x19 +DISASM-NEXT: 180002004: d2800040 mov x0, #0x2 // =2 +DISASM-NEXT: 180002008: d65f03c0 ret +DISASM-NEXT: 18000200c: 0000000d udf #0xd +DISASM-NEXT: 180002010: f0fffff0 adrp x16, 0x180001000 <.text> +DISASM-NEXT: 180002014: 91000210 add x16, x16, #0x0 +DISASM-NEXT: 180002018: d61f0200 br x16 +DISASM-NEXT: 18000201c: d2800060 mov x0, #0x3 // =3 +DISASM-NEXT: 180002020: d65f03c0 ret + +RUN: llvm-readobj --hex-dump=.test out.dll | FileCheck --check-prefix=TESTSEC %s +TESTSEC: 10200000 10200000 10200000 + +RUN: llvm-readobj --coff-load-config out.dll | FileCheck --check-prefix=DYNRELOCS %s +DYNRELOCS: DynamicRelocations [ +DYNRELOCS-NEXT: Version: 0x1 +DYNRELOCS-NEXT: Arm64X [ +DYNRELOCS-NEXT: Entry [ +DYNRELOCS-NEXT: RVA: 0x7C +DYNRELOCS-NEXT: Type: VALUE +DYNRELOCS-NEXT: Size: 0x2 +DYNRELOCS-NEXT: Value: 0x8664 +DYNRELOCS-NEXT: ] +DYNRELOCS-NEXT: Entry [ +DYNRELOCS-NEXT: RVA: 0x150 +DYNRELOCS-NEXT: Type: VALUE +DYNRELOCS-NEXT: Size: 0x4 +DYNRELOCS-NEXT: Value: 0x3150 +DYNRELOCS-NEXT: ] +DYNRELOCS-NEXT: Entry [ +DYNRELOCS-NEXT: RVA: 0x154 +DYNRELOCS-NEXT: Type: VALUE +DYNRELOCS-NEXT: Size: 0x4 +DYNRELOCS-NEXT: Value: 0x140 +DYNRELOCS-NEXT: ] +DYNRELOCS-NEXT: Entry [ +DYNRELOCS-NEXT: RVA: 0x2010 +DYNRELOCS-NEXT: Type: VALUE +DYNRELOCS-NEXT: Size: 0x4 +DYNRELOCS-NEXT: Value: 0x90000010 +DYNRELOCS-NEXT: ] +DYNRELOCS-NEXT: Entry [ +DYNRELOCS-NEXT: RVA: 0x2014 +DYNRELOCS-NEXT: Type: VALUE +DYNRELOCS-NEXT: Size: 0x4 +DYNRELOCS-NEXT: Value: 0x91001210 +DYNRELOCS-NEXT: ] +DYNRELOCS-NEXT: ] +DYNRELOCS-NEXT: ] RUN: lld-link -machine:arm64x -dll -noentry -out:out-cmd.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \ -RUN: func-arm64.obj func-arm64ec.obj -arm64xsameaddress:func +RUN: func-arm64.obj func-arm64ec.obj ref-arm64.obj ref-arm64ec.obj -arm64xsameaddress:func +RUN: llvm-objdump -d out-cmd.dll | FileCheck --check-prefix=DISASM %s +RUN: llvm-readobj --hex-dump=.test out-cmd.dll | FileCheck --check-prefix=TESTSEC %s +RUN: llvm-readobj --coff-load-config out-cmd.dll | FileCheck --check-prefix=DYNRELOCS %s + +RUN: lld-link -machine:arm64x -dll -noentry -out:out-both.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \ +RUN: func-arm64.obj func-arm64ec.obj ref-arm64.obj ref-arm64ec.obj drectve.obj -arm64xsameaddress:func +RUN: llvm-objdump -d out-both.dll | FileCheck --check-prefix=DISASM %s +RUN: llvm-readobj --hex-dump=.test out-both.dll | FileCheck --check-prefix=TESTSEC %s +RUN: llvm-readobj --coff-load-config out-both.dll | FileCheck --check-prefix=DYNRELOCS %s + +Check that if any of the sameaddress symbols is not alive, the thunk is not generated. + +RUN: lld-link -machine:arm64x -dll -noentry -out:out-live1.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \ +RUN: func-arm64.obj func-arm64ec.obj ref-arm64ec.obj drectve.obj +RUN: llvm-objdump -d out-live1.dll | FileCheck --check-prefix=DISASM-LIVE1 %s +DISASM-LIVE1: 0000000180001000 <.text>: +DISASM-LIVE1-NEXT: 180001000: 00000009 udf #0x9 +DISASM-LIVE1-NEXT: 180001004: d2800040 mov x0, #0x2 // =2 +DISASM-LIVE1-NEXT: 180001008: d65f03c0 ret +DISASM-LIVE1-NEXT: 18000100c: d2800060 mov x0, #0x3 // =3 +DISASM-LIVE1-NEXT: 180001010: d65f03c0 ret +DISASM-LIVE1-NOT: br + +RUN: lld-link -machine:arm64x -dll -noentry -out:out-live2.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \ +RUN: func-arm64.obj func-arm64ec.obj ref-arm64.obj drectve.obj +RUN: llvm-objdump -d out-live2.dll | FileCheck --check-prefix=DISASM-LIVE2 %s +DISASM-LIVE2: 0000000180001000 <.text>: +DISASM-LIVE2-NEXT: 180001000: d2800020 mov x0, #0x1 // =1 +DISASM-LIVE2-NEXT: 180001004: d65f03c0 ret +DISASM-LIVE2-NOT: br RUN: lld-link -machine:arm64ec -dll -noentry -out:out-ec.dll loadconfig-arm64ec.obj func-arm64ec.obj drectve.obj @@ -20,6 +107,10 @@ RUN: lld-link -machine:arm64x -dll -noentry -out:out-warn.dll loadconfig-arm64.o RUN: func-arm64.obj func-arm64ec.obj drectve-arm64.obj 2>&1 | FileCheck --check-prefix=WARN %s WARN: lld-link: warning: -arm64xsameaddress: is not allowed in non-ARM64EC files (drectve-arm64.obj) +RUN: lld-link -machine:arm64 -dll -noentry -out:out-warn2.dll loadconfig-arm64.obj \ +RUN: func-arm64.obj -arm64xsameaddress:func 2>&1 | FileCheck --check-prefix=WARN2 %s +WARN2: lld-link: warning: -arm64xsameaddress: is allowed only on EC targets + #--- func-arm64.s .section .text,"xr",discard,func .globl func @@ -27,6 +118,10 @@ func: mov x0, #1 ret +#--- ref-arm64.s + .section .test,"dr" + .rva func + #--- func-arm64ec.s .section .text,"xr",discard,"#func" .globl "#func" @@ -43,14 +138,16 @@ entry_thunk: mov x0, #3 ret - .section .test,"dr" - .rva func - .section .hybmp$x,"yi" .symidx "#func" .symidx entry_thunk .word 1 +#--- ref-arm64ec.s + .section .test,"dr" + .rva func + .rva "#func" + #--- drectve.s .section .drectve, "yn" .ascii " -arm64xsameaddress:func" diff --git a/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s b/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s index a417d89..a33f866 100644 --- a/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s +++ b/lld/test/ELF/loongarch-relax-pc-hi20-lo12.s @@ -1,22 +1,23 @@ # REQUIRES: loongarch +# RUN: rm -rf %t && split-file %s %t && cd %t -# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax %s -o %t.32.o -# RUN: llvm-mc --filetype=obj --triple=loongarch64 -mattr=+relax %s -o %t.64.o +# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax a.s -o a.32.o +# RUN: llvm-mc --filetype=obj --triple=loongarch64 -mattr=+relax a.s -o a.64.o -# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 %t.32.o -o %t.32 -# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 %t.64.o -o %t.64 -# RUN: llvm-objdump -td --no-show-raw-insn %t.32 | FileCheck --check-prefixes=RELAX %s -# RUN: llvm-objdump -td --no-show-raw-insn %t.64 | FileCheck --check-prefixes=RELAX %s +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 a.32.o -o a.32 +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 a.64.o -o a.64 +# RUN: llvm-objdump -td --no-show-raw-insn a.32 | FileCheck --check-prefixes=RELAX %s +# RUN: llvm-objdump -td --no-show-raw-insn a.64 | FileCheck --check-prefixes=RELAX %s -# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 %t.32.o -shared -o %t.32s -# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 %t.64.o -shared -o %t.64s -# RUN: llvm-objdump -td --no-show-raw-insn %t.32s | FileCheck --check-prefixes=RELAX %s -# RUN: llvm-objdump -td --no-show-raw-insn %t.64s | FileCheck --check-prefixes=RELAX %s +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 a.32.o -shared -o a.32s +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 a.64.o -shared -o a.64s +# RUN: llvm-objdump -td --no-show-raw-insn a.32s | FileCheck --check-prefixes=RELAX %s +# RUN: llvm-objdump -td --no-show-raw-insn a.64s | FileCheck --check-prefixes=RELAX %s -# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x410000 %t.32.o -o %t.32o -# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x410000 %t.64.o -o %t.64o -# RUN: llvm-objdump -td --no-show-raw-insn %t.32o | FileCheck --check-prefixes=NORELAX32 %s -# RUN: llvm-objdump -td --no-show-raw-insn %t.64o | FileCheck --check-prefixes=NORELAX64 %s +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x410000 a.32.o -o a.32o +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x410000 a.64.o -o a.64o +# RUN: llvm-objdump -td --no-show-raw-insn a.32o | FileCheck --check-prefixes=NORELAX32 %s +# RUN: llvm-objdump -td --no-show-raw-insn a.64o | FileCheck --check-prefixes=NORELAX64 %s # RELAX-LABEL: <_start>: ## offset = 0x14000 - 0x10000 = 4096<<2 @@ -49,6 +50,25 @@ # NORELAX64-NEXT: pcalau12i $a0, 1024 # NORELAX64-NEXT: ld.d $a0, $a0, 8 + +## GOT references with non-zero addends. No relaxation. +# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax nonzero.s -o nonzero.32.o +# RUN: llvm-mc --filetype=obj --triple=loongarch64 -mattr=+relax nonzero.s -o nonzero.64.o +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 nonzero.32.o -o nonzero.32 +# RUN: ld.lld --section-start=.text=0x10000 --section-start=.data=0x14000 nonzero.64.o -o nonzero.64 +# RUN: llvm-objdump -td --no-show-raw-insn nonzero.32 | FileCheck --check-prefixes=NONZERO32 %s +# RUN: llvm-objdump -td --no-show-raw-insn nonzero.64 | FileCheck --check-prefixes=NONZERO64 %s + +# NONZERO32-LABEL: <_start>: +# NONZERO32-NEXT: 10000: pcalau12i $a0, 4 +# NONZERO32-NEXT: ld.w $a0, $a0, 8 + +# NONZERO64-LABEL: <_start>: +# NONZERO64-NEXT: 10000: pcalau12i $a0, 4 +# NONZERO64-NEXT: ld.d $a0, $a0, 12 + + +#--- a.s .section .text .global _start _start: @@ -60,3 +80,14 @@ _start: .section .data sym: .zero 4 + + +#--- nonzero.s +.section .text +.global _start +_start: + la.got $a0, sym+4 + +.section .data +sym: + .zero 4 |