Index: gnu/llvm/lld/ELF/Driver.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Driver.cpp,v diff -u -p -r1.24 Driver.cpp --- gnu/llvm/lld/ELF/Driver.cpp 29 May 2026 11:06:20 -0000 1.24 +++ gnu/llvm/lld/ELF/Driver.cpp 30 Jul 2026 10:58:21 -0000 @@ -1316,9 +1316,10 @@ static SmallVector getSymb static bool getIsRela(Ctx &ctx, opt::InputArgList &args) { // The psABI specifies the default relocation entry format. - bool rela = is_contained({EM_AARCH64, EM_AMDGPU, EM_HEXAGON, EM_LOONGARCH, - EM_PPC, EM_PPC64, EM_RISCV, EM_S390, EM_X86_64}, - ctx.arg.emachine); + bool rela = + is_contained({EM_AARCH64, EM_AMDGPU, EM_HEXAGON, EM_LOONGARCH, EM_PPC, + EM_PPC64, EM_RISCV, EM_S390, EM_SPARCV9, EM_X86_64}, + ctx.arg.emachine); // If -z rel or -z rela is specified, use the last option. for (auto *arg : args.filtered(OPT_z)) { StringRef s(arg->getValue()); Index: gnu/llvm/lld/ELF/InputFiles.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/InputFiles.cpp,v diff -u -p -r1.7 InputFiles.cpp --- gnu/llvm/lld/ELF/InputFiles.cpp 29 May 2026 11:06:20 -0000 1.7 +++ gnu/llvm/lld/ELF/InputFiles.cpp 30 Jul 2026 10:58:21 -0000 @@ -1213,6 +1213,19 @@ InputSectionBase *ObjFile::createI // Initialize symbols. symbols is a parallel array to the corresponding ELF // symbol table. +// +// LLVM's SPARC assembler and OpenBSD ld.so do not implement application +// register metadata. Accept GCC scratch declarations, but keep them out of +// ordinary symbol resolution. +template +static bool isSparcRegister(const Ctx &ctx, const typename ELFT::Sym &sym) { + return ctx.arg.emachine == EM_SPARCV9 && sym.getType() == STT_SPARC_REGISTER; +} + +static bool isSparcScratchRegister(uint64_t value) { + return value == 2 || value == 3 || value == 6 || value == 7; +} + template void ObjFile::initializeSymbols(const object::ELFFile &obj) { ArrayRef eSyms = this->getELFSyms(); @@ -1221,14 +1234,25 @@ void ObjFile::initializeSymbols(co // Some entries have been filled by LazyObjFile. auto *symtab = ctx.symtab.get(); - for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) - if (!symbols[i]) - symbols[i] = symtab->insert(CHECK2(eSyms[i].getName(stringTable), this)); + for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) { + const Elf_Sym &eSym = eSyms[i]; + if (isSparcRegister(ctx, eSym)) { + StringRef name = CHECK2(eSym.getName(stringTable), this); + if (eSym.getBinding() != STB_GLOBAL || eSym.st_shndx != SHN_UNDEF || + !name.empty() || !isSparcScratchRegister(eSym.st_value)) + Err(ctx) << this << ": unsupported SPARC register declaration"; + symbols[i] = ctx.dummySym; + } else if (!symbols[i]) { + symbols[i] = symtab->insert(CHECK2(eSym.getName(stringTable), this)); + } + } // Perform symbol resolution on non-local symbols. SmallVector undefineds; for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) { const Elf_Sym &eSym = eSyms[i]; + if (isSparcRegister(ctx, eSym)) + continue; uint32_t secIdx = eSym.st_shndx; if (secIdx == SHN_UNDEF) { undefineds.push_back(i); @@ -1330,6 +1354,8 @@ template void ObjFile ArrayRef eSyms = this->getELFSyms(); for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) { const Elf_Sym &eSym = eSyms[i]; + if (isSparcRegister(ctx, eSym)) + continue; Symbol &sym = *symbols[i]; uint32_t secIdx = eSym.st_shndx; uint8_t binding = eSym.getBinding(); @@ -1692,6 +1718,9 @@ template void SharedFile::p ArrayRef syms = this->getGlobalELFSyms(); for (size_t i = 0, e = syms.size(); i != e; ++i) { const Elf_Sym &sym = syms[i]; + // A SPARC register declaration is DSO metadata, not a symbol dependency. + if (isSparcRegister(ctx, sym)) + continue; // ELF spec requires that all local symbols precede weak or global // symbols in each symbol table, and the index of first non-local symbol Index: gnu/llvm/lld/ELF/InputSection.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/InputSection.cpp,v diff -u -p -r1.8 InputSection.cpp --- gnu/llvm/lld/ELF/InputSection.cpp 29 May 2026 11:06:20 -0000 1.8 +++ gnu/llvm/lld/ELF/InputSection.cpp 30 Jul 2026 10:58:21 -0000 @@ -838,6 +838,7 @@ uint64_t InputSectionBase::getRelocTarge return ctx.in.gotPlt->getVA() + a - p; case R_GOTREL: case RE_PPC64_RELAX_TOC: + case R_RELAX_GOT_OFF: return r.sym->getVA(ctx, a) - ctx.in.got->getVA(); case R_GOTPLTREL: return r.sym->getVA(ctx, a) - ctx.in.gotPlt->getVA(); Index: gnu/llvm/lld/ELF/Relocations.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Relocations.cpp,v diff -u -p -r1.11 Relocations.cpp --- gnu/llvm/lld/ELF/Relocations.cpp 29 May 2026 11:06:20 -0000 1.11 +++ gnu/llvm/lld/ELF/Relocations.cpp 30 Jul 2026 10:58:21 -0000 @@ -170,8 +170,9 @@ bool lld::elf::needsGot(RelExpr expr) { static bool isRelExpr(RelExpr expr) { return oneof(expr); + R_RELAX_GOT_OFF, R_RELAX_GOT_PC, RE_RISCV_PC_INDIRECT, + RE_PPC64_RELAX_GOT_PC, RE_LOONGARCH_PAGE_PC, + RE_LOONGARCH_PC_INDIRECT>(expr); } static RelExpr toPlt(RelExpr expr) { @@ -770,14 +771,18 @@ static void addRelativeReloc(Ctx &ctx, I template static void addPltEntry(Ctx &ctx, PltSection &plt, GotPltSection &gotPlt, RelocationBaseSection &rel, RelType type, Symbol &sym) { + RelExpr expr = sym.isPreemptible ? R_ADDEND : R_ABS; plt.addEntry(sym); - gotPlt.addEntry(sym); - if (sym.isPreemptible) - rel.addReloc( - {type, &gotPlt, sym.getGotPltOffset(ctx), true, sym, 0, R_ADDEND}); - else + if (ctx.target->usesGotPlt) { + gotPlt.addEntry(sym); + // The relocation is applied to the .got.plt entry. + rel.addReloc({type, &gotPlt, sym.getGotPltOffset(ctx), !!sym.isPreemptible, + sym, 0, expr}); + } else { + // The relocation is applied to the .plt entry. rel.addReloc( - {type, &gotPlt, sym.getGotPltOffset(ctx), false, sym, 0, R_ABS}); + {type, &plt, sym.getPltOffset(ctx), !!sym.isPreemptible, sym, 0, expr}); + } } void elf::addGotEntry(Ctx &ctx, Symbol &sym) { @@ -945,25 +950,32 @@ void RelocScan::process(RelExpr expr, Re // indirection. const bool isIfunc = sym.isGnuIFunc(); if (!sym.isPreemptible && (!isIfunc || ctx.arg.zIfuncNoplt)) { - if (expr != R_GOT_PC) { + if (expr != R_GOT_PC && expr != R_GOT_OFF) { // The 0x8000 bit of r_addend of R_PPC_PLTREL24 is used to choose call // stub type. It should be ignored if optimized to R_PC. if (ctx.arg.emachine == EM_PPC && expr == RE_PPC32_PLTREL) addend &= ~0x8000; // R_HEX_GD_PLT_B22_PCREL (call a@GDPLT) is transformed into // call __tls_get_addr even if the symbol is non-preemptible. + // Same deal for R_SPARC_TLS_LDM_CALL (call x@TLSPLT). if (!(ctx.arg.emachine == EM_HEXAGON && (type == R_HEX_GD_PLT_B22_PCREL || type == R_HEX_GD_PLT_B22_PCREL_X || - type == R_HEX_GD_PLT_B32_PCREL_X))) + type == R_HEX_GD_PLT_B32_PCREL_X)) && + !(ctx.arg.emachine == EM_SPARCV9 && type == R_SPARC_TLS_LDM_CALL)) expr = fromPlt(expr); } else if (!isAbsoluteValue(sym) || (type == R_PPC64_PCREL_OPT && ctx.arg.emachine == EM_PPC64)) { - expr = ctx.target->adjustGotPcExpr(type, addend, - sec->content().data() + offset); - // If the target adjusted the expression to R_RELAX_GOT_PC, we may end up + if (expr == R_GOT_PC) + expr = ctx.target->adjustGotPcExpr(type, addend, + sec->content().data() + offset); + else if (expr == R_GOT_OFF) + expr = ctx.target->adjustGotOffExpr(type, sym, addend, + sec->content().data() + offset); + + // If the target adjusted the expression to R_RELAX_GOT_*, we may end up // needing the GOT if we can't relax everything. - if (expr == R_RELAX_GOT_PC) + if (expr == R_RELAX_GOT_PC || expr == R_RELAX_GOT_OFF) ctx.in.got->hasGotOffRel.store(true, std::memory_order_relaxed); } } @@ -1022,8 +1034,14 @@ void RelocScan::process(RelExpr expr, Re (isa(sec) && ctx.arg.emachine != EM_MIPS)); if (canWrite) { RelType rel = ctx.target->getDynRel(type); - if (oneof(expr) || - (rel == ctx.target->symbolicRel && !sym.isPreemptible)) { + bool useRelative = + rel == ctx.target->symbolicRel && !sym.isPreemptible; + if (ctx.arg.emachine == EM_SPARCV9 && + (type == R_SPARC_16 || type == R_SPARC_32 || type == R_SPARC_64 || + type == R_SPARC_UA16 || type == R_SPARC_UA32 || + type == R_SPARC_UA64)) + useRelative = false; + if (oneof(expr) || useRelative) { addRelativeReloc(ctx, *sec, offset, sym, addend, expr, type); return; } @@ -1066,6 +1084,7 @@ void RelocScan::process(RelExpr expr, Re return; } } + ctx.target->prepareDynamicReloc(rel, sym, *sec); part.relaDyn->addSymbolReloc(rel, *sec, offset, sym, addend, type); // MIPS ABI turns using of GOT and dynamic relocations inside out. @@ -1236,12 +1255,13 @@ unsigned RelocScan::handleTlsRelocation( // ARM, Hexagon, LoongArch and RISC-V do not support GD/LD to IE/LE // optimizations. + // SPARC support for GD/LD to IE/LE optimizations is not yet implemented. // RISC-V supports TLSDESC to IE/LE optimizations. // For PPC64, if the file has missing R_PPC64_TLSGD/R_PPC64_TLSLD, disable // optimization as well. bool execOptimize = !ctx.arg.shared && ctx.arg.emachine != EM_ARM && - ctx.arg.emachine != EM_HEXAGON && + ctx.arg.emachine != EM_HEXAGON && ctx.arg.emachine != EM_SPARCV9 && (ctx.arg.emachine != EM_LOONGARCH || execOptimizeInLoongArch) && !(isRISCV && expr != R_TLSDESC_PC && expr != R_TLSDESC_CALL) && !sec->file->ppc64DisableTLSRelax; @@ -1585,10 +1605,7 @@ void elf::postScanRelocations(Ctx &ctx) } else { assert(sym.isFunc() && sym.hasFlag(NEEDS_PLT)); if (!sym.isDefined()) { - replaceWithDefined(ctx, sym, *ctx.in.plt, - ctx.target->pltHeaderSize + - ctx.target->pltEntrySize * sym.getPltIdx(ctx), - 0); + replaceWithDefined(ctx, sym, *ctx.in.plt, sym.getPltOffset(ctx), 0); sym.setFlags(NEEDS_COPY); if (ctx.arg.emachine == EM_PPC) { // PPC32 canonical PLT entries are at the beginning of .glink @@ -2233,11 +2250,11 @@ bool ThunkCreator::createThunks(uint32_t return addressesChanged; } -// The following aid in the conversion of call x@GDPLT to call __tls_get_addr -// hexagonNeedsTLSSymbol scans for relocations would require a call to -// __tls_get_addr. -// hexagonTLSSymbolUpdate rebinds the relocation to __tls_get_addr. -bool elf::hexagonNeedsTLSSymbol(ArrayRef outputSections) { +// The following aid in the conversion of call x@GDPLT (Hexagon) and +// call x@TLSPLT (SPARC) to call __tls_get_addr. needsTLSSymbol scans +// for relocations that would require a call to __tls_get_addr. +// tlsSymbolUpdate rebinds the relocation to __tls_get_addr. +bool elf::needsTLSSymbol(ArrayRef outputSections) { bool needTlsSymbol = false; forEachInputSectionDescription( outputSections, [&](OutputSection *os, InputSectionDescription *isd) { @@ -2251,7 +2268,7 @@ bool elf::hexagonNeedsTLSSymbol(ArrayRef return needTlsSymbol; } -void elf::hexagonTLSSymbolUpdate(Ctx &ctx) { +void elf::tlsSymbolUpdate(Ctx &ctx) { Symbol *sym = ctx.symtab->find("__tls_get_addr"); if (!sym) return; Index: gnu/llvm/lld/ELF/Relocations.h =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Relocations.h,v diff -u -p -r1.1.1.6 Relocations.h --- gnu/llvm/lld/ELF/Relocations.h 29 May 2026 11:05:55 -0000 1.1.1.6 +++ gnu/llvm/lld/ELF/Relocations.h 30 Jul 2026 10:58:21 -0000 @@ -61,6 +61,7 @@ enum RelExpr { R_PLT_GOTPLT, R_PLT_GOTREL, R_RELAX_HINT, + R_RELAX_GOT_OFF, R_RELAX_GOT_PC, R_RELAX_GOT_PC_NOPIC, R_RELAX_TLS_GD_TO_IE, @@ -171,8 +172,8 @@ bool maybeReportUndefined(Ctx &, Undefin void postScanRelocations(Ctx &ctx); void addGotEntry(Ctx &ctx, Symbol &sym); -void hexagonTLSSymbolUpdate(Ctx &ctx); -bool hexagonNeedsTLSSymbol(ArrayRef outputSections); +void tlsSymbolUpdate(Ctx &ctx); +bool needsTLSSymbol(ArrayRef outputSections); bool isAbsolute(const Symbol &sym); Index: gnu/llvm/lld/ELF/Symbols.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Symbols.cpp,v diff -u -p -r1.7 Symbols.cpp --- gnu/llvm/lld/ELF/Symbols.cpp 29 May 2026 11:06:20 -0000 1.7 +++ gnu/llvm/lld/ELF/Symbols.cpp 30 Jul 2026 10:58:21 -0000 @@ -173,11 +173,20 @@ uint64_t Symbol::getGotPltOffset(Ctx &ct ctx.target->gotEntrySize; } +uint64_t Symbol::getPltOffset(Ctx &ctx) const { + if (isInIplt) + return getPltIdx(ctx) * ctx.target->ipltEntrySize; + return ctx.target->getPltEntryOffset(getPltIdx(ctx), + ctx.in.plt->headerSize); +} + uint64_t Symbol::getPltVA(Ctx &ctx) const { uint64_t outVA = isInIplt ? ctx.in.iplt->getVA() + getPltIdx(ctx) * ctx.target->ipltEntrySize - : ctx.in.plt->getVA() + ctx.in.plt->headerSize + - getPltIdx(ctx) * ctx.target->pltEntrySize; + : ctx.in.plt->getVA() + + ctx.target->getPltEntryOffset( + getPltIdx(ctx), + ctx.in.plt->headerSize); // While linking microMIPS code PLT code are always microMIPS // code. Set the less-significant bit to track that fact. Index: gnu/llvm/lld/ELF/Symbols.h =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Symbols.h,v diff -u -p -r1.8 Symbols.h --- gnu/llvm/lld/ELF/Symbols.h 29 May 2026 11:06:20 -0000 1.8 +++ gnu/llvm/lld/ELF/Symbols.h 30 Jul 2026 10:58:21 -0000 @@ -207,6 +207,7 @@ public: uint64_t getGotVA(Ctx &) const; uint64_t getGotPltOffset(Ctx &) const; uint64_t getGotPltVA(Ctx &) const; + uint64_t getPltOffset(Ctx &) const; uint64_t getPltVA(Ctx &) const; uint64_t getSize() const; OutputSection *getOutputSection() const; Index: gnu/llvm/lld/ELF/SyntheticSections.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/SyntheticSections.cpp,v diff -u -p -r1.10 SyntheticSections.cpp --- gnu/llvm/lld/ELF/SyntheticSections.cpp 29 May 2026 11:06:20 -0000 1.10 +++ gnu/llvm/lld/ELF/SyntheticSections.cpp 30 Jul 2026 10:58:21 -0000 @@ -1505,6 +1505,7 @@ DynamicSection::computeContents() addInt(DT_RISCV_VARIANT_CC, 0); [[fallthrough]]; default: + assert(ctx.target->usesGotPlt); addInSec(DT_PLTGOT, *ctx.in.gotPlt); break; } @@ -1720,14 +1721,20 @@ void RelocationBaseSection::finalizeCont else getParent()->link = 0; - if (ctx.in.relaPlt.get() == this && ctx.in.gotPlt->getParent()) { - getParent()->flags |= ELF::SHF_INFO_LINK; - getParent()->info = ctx.in.gotPlt->getParent()->sectionIndex; + if (ctx.in.relaPlt.get() == this) { + if (ctx.target->usesGotPlt && ctx.in.gotPlt->getParent()) { + getParent()->flags |= ELF::SHF_INFO_LINK; + getParent()->info = ctx.in.gotPlt->getParent()->sectionIndex; + } else if (ctx.in.plt->getParent()) { + getParent()->flags |= ELF::SHF_INFO_LINK; + getParent()->info = ctx.in.plt->getParent()->sectionIndex; + } } } void DynamicReloc::finalize(Ctx &ctx, SymbolTableBaseSection *symt) { r_offset = getOffset(); + ctx.target->finalizeDynamicReloc(*this); r_sym = getSymIndex(symt); addend = computeAddend(ctx); isFinal = true; // Catch errors @@ -2176,12 +2183,12 @@ void SymbolTableBaseSection::finalizeCon return; } - // If it is a .dynsym, there should be no local symbols, but we need - // to do a few things for the dynamic linker. + // If it is a .dynsym, we need to do a few things for the dynamic linker. // Section's Info field has the index of the first non-local symbol. - // Because the first symbol entry is a null entry, 1 is the first. - getParent()->info = 1; + // Because the first symbol entry is a null entry, add one to the number of + // local symbols. + getParent()->info = numLocalDynamicSymbols + 1; if (getPartition(ctx).gnuHashTab) { // NB: It also sorts Symbols to meet the GNU hash table requirements. @@ -2201,7 +2208,7 @@ void SymbolTableBaseSection::finalizeCon // The ELF spec requires that all local symbols precede global symbols, so we // sort symbol entries in this function. (For .dynsym, we don't do that because -// symbols for dynamic linking are inherently all globals.) +// local symbols are inserted before global symbols.) // // Aside from above, we put local symbols in groups starting with the STT_FILE // symbol. That is convenient for purpose of identifying where are local symbols @@ -2235,6 +2242,13 @@ void SymbolTableBaseSection::addSymbol(S symbols.push_back({b, strTabSec.addString(b->getName(), false)}); } +void SymbolTableBaseSection::addLocalSectionSymbol(Symbol *b) { + assert(this->type == SHT_DYNSYM && b->isLocal() && b->isSection()); + symbols.insert(symbols.begin() + numLocalDynamicSymbols, + {b, strTabSec.addString(b->getName(), false)}); + ++numLocalDynamicSymbols; +} + size_t SymbolTableBaseSection::getSymbolIndex(const Symbol &sym) { if (this == ctx.mainPart->dynSymTab.get()) return sym.dynsymIndex; @@ -2508,7 +2522,8 @@ void GnuHashTableSection::addSymbols(Sma // its type correctly. auto mid = std::stable_partition(v.begin(), v.end(), [&](const SymbolTableEntry &s) { - return !s.sym->isDefined() || s.sym->partition != partition; + return s.sym->isLocal() || !s.sym->isDefined() || + s.sym->partition != partition; }); // We chose load factor 4 for the on-disk hash table. For each hash @@ -2574,6 +2589,8 @@ void HashTableSection::writeTo(uint8_t * for (const SymbolTableEntry &s : symTab->getSymbols()) { Symbol *sym = s.sym; + if (sym->isLocal()) + continue; StringRef name = sym->getName(); unsigned i = sym->dynsymIndex; uint32_t hash = hashSysV(name) % numSymbols; @@ -2609,20 +2626,22 @@ PltSection::PltSection(Ctx &ctx) // The PLT needs to be writable on SPARC as the dynamic linker will // modify the instructions in the PLT entries. - if (ctx.arg.emachine == EM_SPARCV9) + if (ctx.arg.emachine == EM_SPARCV9) { this->flags |= SHF_WRITE; + addralign = 256; + } } void PltSection::writeTo(uint8_t *buf) { // At beginning of PLT, we have code to call the dynamic // linker to resolve dynsyms at runtime. Write such code. ctx.target->writePltHeader(buf); - size_t off = headerSize; for (const Symbol *sym : entries) { + size_t off = sym->getPltOffset(ctx); ctx.target->writePlt(buf + off, *sym, getVA() + off); - off += ctx.target->pltEntrySize; } + ctx.target->finalizePlt(buf); } void PltSection::addEntry(Symbol &sym) { @@ -2645,11 +2664,8 @@ bool PltSection::isNeeded() const { void PltSection::addSymbols() { ctx.target->addPltHeaderSymbols(*this); - size_t off = headerSize; - for (size_t i = 0; i < entries.size(); ++i) { - ctx.target->addPltSymbols(*this, off); - off += ctx.target->pltEntrySize; - } + for (const Symbol *sym : entries) + ctx.target->addPltSymbols(*this, sym->getPltOffset(ctx)); } IpltSection::IpltSection(Ctx &ctx) @@ -4882,10 +4898,12 @@ template void elf::createSy // _GLOBAL_OFFSET_TABLE_ is defined relative to either .got.plt or .got. Treat // it as a relocation and ensure the referenced section is created. if (ctx.sym.globalOffsetTable && ctx.arg.emachine != EM_MIPS) { - if (ctx.target->gotBaseSymInGotPlt) + if (ctx.target->gotBaseSymInGotPlt) { + assert(ctx.target->usesGotPlt); ctx.in.gotPlt->hasGotPltOffRel = true; - else + } else { ctx.in.got->hasGotOffRel = true; + } } // We always need to add rel[a].plt to output if it has entries. Index: gnu/llvm/lld/ELF/SyntheticSections.h =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/SyntheticSections.h,v diff -u -p -r1.5 SyntheticSections.h --- gnu/llvm/lld/ELF/SyntheticSections.h 29 May 2026 11:06:20 -0000 1.5 +++ gnu/llvm/lld/ELF/SyntheticSections.h 30 Jul 2026 10:58:21 -0000 @@ -433,6 +433,11 @@ public: uint64_t getOffset() const; uint32_t getSymIndex(SymbolTableBaseSection *symTab) const; bool needsDynSymIndex() const { return isAgainstSymbol; } + void convertToRelative(RelType relativeRel) { + type = relativeRel; + isAgainstSymbol = false; + expr = R_ABS; + } /// 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 @@ -661,6 +666,7 @@ public: void finalizeContents() override; size_t getSize() const override { return getNumSymbols() * entsize; } void addSymbol(Symbol *sym); + void addLocalSectionSymbol(Symbol *sym); unsigned getNumSymbols() const { return symbols.size() + 1; } size_t getSymbolIndex(const Symbol &sym); ArrayRef getSymbols() const { return symbols; } @@ -670,6 +676,7 @@ protected: // A vector of symbols and their string table offsets. SmallVector symbols; + size_t numLocalDynamicSymbols = 0; StringTableSection &strTabSec; Index: gnu/llvm/lld/ELF/Target.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Target.cpp,v diff -u -p -r1.1.1.6 Target.cpp --- gnu/llvm/lld/ELF/Target.cpp 29 May 2026 11:05:55 -0000 1.1.1.6 +++ gnu/llvm/lld/ELF/Target.cpp 30 Jul 2026 10:58:21 -0000 @@ -158,6 +158,12 @@ RelExpr TargetInfo::adjustGotPcExpr(RelT return R_GOT_PC; } +RelExpr TargetInfo::adjustGotOffExpr(RelType type, const Symbol &sym, + int64_t addend, + const uint8_t *data) const { + return R_GOT_OFF; +} + static void relocateImpl(const TargetInfo &target, InputSectionBase &sec, uint64_t secAddr, uint8_t *buf) { auto &ctx = target.ctx; Index: gnu/llvm/lld/ELF/Target.h =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Target.h,v diff -u -p -r1.1.1.6 Target.h --- gnu/llvm/lld/ELF/Target.h 29 May 2026 11:05:55 -0000 1.1.1.6 +++ gnu/llvm/lld/ELF/Target.h 30 Jul 2026 10:58:21 -0000 @@ -22,6 +22,7 @@ namespace lld { namespace elf { class Defined; +class DynamicReloc; class InputFile; class Symbol; template struct Relocs; @@ -35,6 +36,9 @@ public: virtual RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const = 0; virtual RelType getDynRel(RelType type) const { return 0; } + virtual void prepareDynamicReloc(RelType type, const Symbol &sym, + InputSectionBase &sec) {} + virtual void finalizeDynamicReloc(DynamicReloc &rel) const {} virtual void writeGotPltHeader(uint8_t *buf) const {} virtual void writeGotHeader(uint8_t *buf) const {} virtual void writeGotPlt(uint8_t *buf, const Symbol &s) const {} @@ -47,8 +51,13 @@ public: // they are called. This function writes that code. virtual void writePltHeader(uint8_t *buf) const {} + virtual uint64_t getPltEntryOffset(uint32_t pltIdx, + uint64_t headerSize) const { + return headerSize + uint64_t{pltIdx} * pltEntrySize; + } virtual void writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const {} + virtual void finalizePlt(uint8_t *buf) const {} virtual void writeIplt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const { // All but PPC32 and PPC64 use the same format for .plt and .iplt entries. @@ -135,7 +144,8 @@ public: uint64_t getImageBase() const; - // True if _GLOBAL_OFFSET_TABLE_ is relative to .got.plt, false if .got. + // True if _GLOBAL_OFFSET_TABLE_ is relative to .got.plt, false if .got. If + // true, usesGotPlt must also be true. bool gotBaseSymInGotPlt = false; static constexpr RelType noneRel = 0; @@ -162,6 +172,8 @@ public: // On PPC ELF V2 abi, the first entry in the .got is the .TOC. unsigned gotHeaderEntriesNum = 0; + bool usesGotPlt = true; + // On PPC ELF V2 abi, the dynamic section needs DT_PPC64_OPT (DT_LOPROC + 3) // to be set to 0x2 if there can be multiple TOC's. Although we do not emit // multiple TOC's, there can be a mix of TOC and NOTOC addressing which @@ -186,6 +198,8 @@ public: virtual RelExpr adjustTlsExpr(RelType type, RelExpr expr) const; virtual RelExpr adjustGotPcExpr(RelType type, int64_t addend, const uint8_t *loc) const; + virtual RelExpr adjustGotOffExpr(RelType type, const Symbol &sym, + int64_t addend, const uint8_t *loc) const; protected: // On FreeBSD x86_64 the first page cannot be mmaped. Index: gnu/llvm/lld/ELF/Writer.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Writer.cpp,v diff -u -p -r1.10 Writer.cpp --- gnu/llvm/lld/ELF/Writer.cpp 29 May 2026 11:06:20 -0000 1.10 +++ gnu/llvm/lld/ELF/Writer.cpp 30 Jul 2026 10:58:21 -0000 @@ -625,7 +625,7 @@ static bool isRelroSection(Ctx &ctx, con // by default resolved lazily, so we usually cannot put it into RELRO. // However, if "-z now" is given, the lazy symbol resolution is // disabled, which enables us to put it into RELRO. - if (sec == ctx.in.gotPlt->getParent()) + if (ctx.target->usesGotPlt && sec == ctx.in.gotPlt->getParent()) #ifndef __OpenBSD__ return ctx.arg.zNow; #else @@ -863,10 +863,14 @@ template void Writer: if (ctx.sym.globalOffsetTable) { // The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention usually // to the start of the .got or .got.plt section. - InputSection *sec = ctx.in.gotPlt.get(); - if (!ctx.target->gotBaseSymInGotPlt) + InputSection *sec; + if (ctx.target->gotBaseSymInGotPlt) { + assert(ctx.target->usesGotPlt); + sec = ctx.in.gotPlt.get(); + } else { sec = ctx.in.mipsGot ? cast(ctx.in.mipsGot.get()) : cast(ctx.in.got.get()); + } ctx.sym.globalOffsetTable->section = sec; } @@ -1544,9 +1548,9 @@ template void Writer: }; finalizeOrderDependentContent(); - // Converts call x@GDPLT to call __tls_get_addr - if (ctx.arg.emachine == EM_HEXAGON) - hexagonTLSSymbolUpdate(ctx); + // Converts call x@GDPLT/x@TLSPLT to call __tls_get_addr. + if (ctx.arg.emachine == EM_HEXAGON || ctx.arg.emachine == EM_SPARCV9) + tlsSymbolUpdate(ctx); if (ctx.arg.randomizeSectionPadding) randomizeSectionPadding(ctx); @@ -2042,10 +2046,10 @@ template void Writer: sec->addrExpr = [=] { return i->second; }; } - // With the ctx.outputSections available check for GDPLT relocations + // With the ctx.outputSections available check for GDPLT/TLSPLT relocations // and add __tls_get_addr symbol if needed. - if (ctx.arg.emachine == EM_HEXAGON && - hexagonNeedsTLSSymbol(ctx.outputSections)) { + if ((ctx.arg.emachine == EM_HEXAGON || ctx.arg.emachine == EM_SPARCV9) && + needsTLSSymbol(ctx.outputSections)) { Symbol *sym = ctx.symtab->addSymbol(Undefined{ctx.internalFile, "__tls_get_addr", STB_GLOBAL, STV_DEFAULT, STT_NOTYPE}); @@ -2311,6 +2315,15 @@ static bool needsPtLoad(OutputSection *s static uint64_t computeFlags(Ctx &ctx, uint64_t flags) { if (ctx.arg.omagic) return PF_R | PF_W | PF_X; +#ifdef __OpenBSD__ + // The sparc64 static PIE startup code reads an instruction from .text to + // locate _DYNAMIC before applying relocations. + if (ctx.arg.emachine == EM_SPARCV9 && ctx.arg.pie && !ctx.arg.shared && + ctx.sharedFiles.empty() && (flags & PF_X)) + return flags | PF_R; + if (ctx.arg.emachine == EM_SPARCV9 && (flags & PF_X) && (flags & PF_W)) + return flags; +#endif if (ctx.arg.executeOnly && (flags & PF_X)) return flags & ~PF_R; return flags; Index: gnu/llvm/lld/ELF/Arch/SPARCV9.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Arch/SPARCV9.cpp,v diff -u -p -r1.1.1.5 SPARCV9.cpp --- gnu/llvm/lld/ELF/Arch/SPARCV9.cpp 29 May 2026 11:05:55 -0000 1.1.1.5 +++ gnu/llvm/lld/ELF/Arch/SPARCV9.cpp 30 Jul 2026 10:58:21 -0000 @@ -6,10 +6,14 @@ // //===----------------------------------------------------------------------===// +#include "OutputSections.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/Support/Endian.h" +#include +#include using namespace llvm; using namespace llvm::support::endian; @@ -18,15 +22,39 @@ using namespace lld; using namespace lld::elf; namespace { +constexpr uint32_t firstLargePltIndex = 32768 - 4; +constexpr uint32_t pltLargeEntriesPerBlock = 160; +constexpr uint32_t pltLargeCodeSize = 24; +constexpr uint32_t pltLargePointerSize = 8; +constexpr uint32_t pltLargeBlockSize = + pltLargeEntriesPerBlock * (pltLargeCodeSize + pltLargePointerSize); + class SPARCV9 final : public TargetInfo { public: SPARCV9(Ctx &); RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void prepareDynamicReloc(RelType type, const Symbol &sym, + InputSectionBase &sec) override; + void finalizeDynamicReloc(DynamicReloc &rel) const override; + void writeGotHeader(uint8_t *buf) const override; + uint64_t getPltEntryOffset(uint32_t pltIdx, + uint64_t headerSize) const override; void writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const override; + void finalizePlt(uint8_t *buf) const override; void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; + RelExpr adjustGotOffExpr(RelType type, const Symbol &sym, int64_t addend, + const uint8_t *loc) const override; + +private: + uint64_t getLargePltPointerOffset(uint32_t pltIdx) const; + void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const; + + DenseMap, Defined *> + dynamicSectionSymbols; }; } // namespace @@ -35,9 +63,16 @@ SPARCV9::SPARCV9(Ctx &ctx) : TargetInfo( gotRel = R_SPARC_GLOB_DAT; pltRel = R_SPARC_JMP_SLOT; relativeRel = R_SPARC_RELATIVE; + iRelativeRel = R_SPARC_IRELATIVE; symbolicRel = R_SPARC_64; + tlsGotRel = R_SPARC_TLS_TPOFF64; + tlsModuleIndexRel = R_SPARC_TLS_DTPMOD64; + tlsOffsetRel = R_SPARC_TLS_DTPOFF64; + + gotHeaderEntriesNum = 1; pltEntrySize = 32; pltHeaderSize = 4 * pltEntrySize; + usesGotPlt = false; defaultCommonPageSize = 8192; defaultMaxPageSize = 0x100000; @@ -47,35 +82,74 @@ SPARCV9::SPARCV9(Ctx &ctx) : TargetInfo( RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { + case R_SPARC_NONE: + return R_NONE; + case R_SPARC_8: + case R_SPARC_16: case R_SPARC_32: + case R_SPARC_HI22: + case R_SPARC_13: + case R_SPARC_LO10: case R_SPARC_UA32: case R_SPARC_64: - case R_SPARC_UA64: - case R_SPARC_H44: - case R_SPARC_M44: - case R_SPARC_L44: case R_SPARC_HH22: case R_SPARC_HM10: case R_SPARC_LM22: - case R_SPARC_HI22: - case R_SPARC_LO10: + case R_SPARC_HIX22: + case R_SPARC_LOX10: + case R_SPARC_H44: + case R_SPARC_M44: + case R_SPARC_L44: + case R_SPARC_UA64: + case R_SPARC_UA16: return R_ABS; - case R_SPARC_PC10: - case R_SPARC_PC22: + case R_SPARC_DISP8: + case R_SPARC_DISP16: case R_SPARC_DISP32: case R_SPARC_WDISP30: + case R_SPARC_WDISP22: + case R_SPARC_PC10: + case R_SPARC_PC22: + case R_SPARC_WDISP16: + case R_SPARC_WDISP19: + case R_SPARC_DISP64: return R_PC; case R_SPARC_GOT10: - return R_GOT_OFF; + case R_SPARC_GOT13: case R_SPARC_GOT22: + case R_SPARC_GOTDATA_OP_HIX22: + case R_SPARC_GOTDATA_OP_LOX10: + case R_SPARC_GOTDATA_OP: return R_GOT_OFF; case R_SPARC_WPLT30: + case R_SPARC_TLS_GD_CALL: + case R_SPARC_TLS_LDM_CALL: return R_PLT_PC; - case R_SPARC_NONE: - return R_NONE; + case R_SPARC_TLS_GD_HI22: + case R_SPARC_TLS_GD_LO10: + return R_TLSGD_GOT; + case R_SPARC_TLS_GD_ADD: + case R_SPARC_TLS_LDM_ADD: + case R_SPARC_TLS_LDO_ADD: + case R_SPARC_TLS_IE_LD: + case R_SPARC_TLS_IE_LDX: + case R_SPARC_TLS_IE_ADD: + return R_NONE; // TODO: Relax TLS relocations. + case R_SPARC_TLS_LDM_HI22: + case R_SPARC_TLS_LDM_LO10: + return R_TLSLD_GOT; + case R_SPARC_TLS_LDO_HIX22: + case R_SPARC_TLS_LDO_LOX10: + return R_DTPREL; + case R_SPARC_TLS_IE_HI22: + case R_SPARC_TLS_IE_LO10: + return R_GOT; case R_SPARC_TLS_LE_HIX22: case R_SPARC_TLS_LE_LOX10: return R_TPREL; + case R_SPARC_GOTDATA_HIX22: + case R_SPARC_GOTDATA_LOX10: + return R_GOTREL; default: Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v << ") against symbol " << &s; @@ -83,73 +157,255 @@ RelExpr SPARCV9::getRelExpr(RelType type } } +RelType SPARCV9::getDynRel(RelType type) const { + switch (type) { + case R_SPARC_16: + case R_SPARC_32: + case R_SPARC_64: + case R_SPARC_UA16: + case R_SPARC_UA32: + case R_SPARC_UA64: + return type; + default: + break; + } + return R_SPARC_NONE; +} + +static bool getSparcAbsRelocPair(RelType type, RelType &aligned, + RelType &unaligned, uint64_t &alignment) { + switch (type) { + case R_SPARC_16: + case R_SPARC_UA16: + aligned = R_SPARC_16; + unaligned = R_SPARC_UA16; + alignment = 2; + return true; + case R_SPARC_32: + case R_SPARC_UA32: + aligned = R_SPARC_32; + unaligned = R_SPARC_UA32; + alignment = 4; + return true; + case R_SPARC_64: + case R_SPARC_UA64: + aligned = R_SPARC_64; + unaligned = R_SPARC_UA64; + alignment = 8; + return true; + default: + return false; + } +} + +void SPARCV9::prepareDynamicReloc(RelType type, const Symbol &sym, + InputSectionBase &sec) { + if (auto *eh = dyn_cast(&sec)) { + SyntheticSection *parent = eh->getParent(); + parent->flags |= sec.flags & SHF_WRITE; + parent->getParent()->flags |= sec.flags & SHF_WRITE; + } + + RelType aligned = R_SPARC_NONE, unaligned = R_SPARC_NONE; + uint64_t alignment = 1; + if (!getSparcAbsRelocPair(type, aligned, unaligned, alignment) || + sym.isPreemptible) + return; + + OutputSection *osec = sym.getOutputSection(); + if (!osec) + return; + + Partition &part = sec.getPartition(ctx); + unsigned partition = part.getNumber(ctx); + auto key = std::make_pair(osec, partition); + if (dynamicSectionSymbols.contains(key)) + return; + + Defined *sectionSym = + makeDefined(ctx, ctx.internalFile, "", STB_LOCAL, STV_DEFAULT, + STT_SECTION, 0, 0, osec); + sectionSym->partition = partition; + part.dynSymTab->addLocalSectionSymbol(sectionSym); + dynamicSectionSymbols[key] = sectionSym; +} + +void SPARCV9::finalizeDynamicReloc(DynamicReloc &rel) const { + // scanEhSection records an offset relative to the merged .eh_frame. + if (auto *eh = dyn_cast(rel.inputSec)) { + rel.inputSec = eh->getParent(); + rel.r_offset = rel.inputSec->getVA(rel.offsetInSec); + } + + if (rel.type == R_SPARC_JMP_SLOT && rel.inputSec == ctx.in.plt.get()) { + uint32_t pltIdx = rel.sym->getPltIdx(ctx); + if (pltIdx >= firstLargePltIndex) { + uint64_t off = + getPltEntryOffset(pltIdx, ctx.in.plt->headerSize); + rel.r_offset = + ctx.in.plt->getVA() + getLargePltPointerOffset(pltIdx); + rel.addend = -static_cast(ctx.in.plt->getVA() + off + 4); + } + } + + RelType aligned = R_SPARC_NONE, unaligned = R_SPARC_NONE; + uint64_t alignment = 1; + if (!getSparcAbsRelocPair(rel.type, aligned, unaligned, alignment)) + return; + + rel.type = rel.r_offset % alignment == 0 ? aligned : unaligned; + if (!rel.needsDynSymIndex() || rel.sym->isPreemptible) + return; + + if (rel.type == R_SPARC_64) { + rel.convertToRelative(relativeRel); + return; + } + + OutputSection *osec = rel.sym->getOutputSection(); + unsigned partition = rel.inputSec->getPartition(ctx).getNumber(ctx); + auto it = dynamicSectionSymbols.find(std::make_pair(osec, partition)); + assert(it != dynamicSectionSymbols.end()); + rel.addend = rel.sym->getVA(ctx, rel.addend); + rel.sym = it->second; +} + void SPARCV9::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + switch (rel.expr) { + case R_RELAX_GOT_OFF: + return relaxGot(loc, rel, val); + default: + break; + } + switch (rel.type) { + case R_SPARC_8: + // V-byte8 + checkUInt(ctx, loc, val, 8, rel); + *loc = val; + break; + case R_SPARC_16: + case R_SPARC_UA16: + // V-half16 + checkUInt(ctx, loc, val, 16, rel); + write16be(loc, val); + break; case R_SPARC_32: case R_SPARC_UA32: // V-word32 checkUInt(ctx, loc, val, 32, rel); write32be(loc, val); break; + case R_SPARC_DISP8: + // V-byte8 + checkIntUInt(ctx, loc, val, 8, rel); + *loc = val; + break; + case R_SPARC_DISP16: + // V-half16 + checkIntUInt(ctx, loc, val, 16, rel); + write16be(loc, val); + break; case R_SPARC_DISP32: // V-disp32 - checkInt(ctx, loc, val, 32, rel); + checkIntUInt(ctx, loc, val, 32, rel); write32be(loc, val); break; case R_SPARC_WDISP30: case R_SPARC_WPLT30: + case R_SPARC_TLS_GD_CALL: + case R_SPARC_TLS_LDM_CALL: // V-disp30 - checkInt(ctx, loc, val, 32, rel); + checkIntUInt(ctx, loc, val, 32, rel); write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff)); break; - case R_SPARC_22: - // V-imm22 - checkUInt(ctx, loc, val, 22, rel); - write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff)); + case R_SPARC_WDISP22: + // V-disp22 + checkIntUInt(ctx, loc, val, 24, rel); + write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 2) & 0x003fffff)); break; - case R_SPARC_GOT22: - case R_SPARC_PC22: - case R_SPARC_LM22: - // T-imm22 + case R_SPARC_HI22: // Only T-imm22 on 32-bit, despite binutils behavior. + // V-imm22 + checkUInt(ctx, loc, val, 32, rel); write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff)); break; - case R_SPARC_HI22: + case R_SPARC_22: // V-imm22 - checkUInt(ctx, loc, val >> 10, 22, rel); - write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff)); + checkUInt(ctx, loc, val, 22, rel); + write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff)); break; - case R_SPARC_WDISP19: - // V-disp19 - checkInt(ctx, loc, val, 21, rel); - write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff)); + case R_SPARC_13: + case R_SPARC_GOT13: + // V-simm13 + checkIntUInt(ctx, loc, val, 13, rel); + write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x00001fff)); break; + case R_SPARC_LO10: case R_SPARC_GOT10: case R_SPARC_PC10: - // T-simm10 + case R_SPARC_TLS_GD_LO10: + case R_SPARC_TLS_LDM_LO10: + case R_SPARC_TLS_IE_LO10: + // T-simm13 write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff)); break; - case R_SPARC_LO10: + case R_SPARC_TLS_LDO_LOX10: // T-simm13 write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff)); break; + case R_SPARC_GOT22: + case R_SPARC_LM22: + case R_SPARC_TLS_GD_HI22: + case R_SPARC_TLS_LDM_HI22: + case R_SPARC_TLS_LDO_HIX22: // Not V-simm22, despite binutils behavior. + case R_SPARC_TLS_IE_HI22: + // T-(s)imm22 + write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff)); + break; + case R_SPARC_PC22: + // V-disp22 + checkIntUInt(ctx, loc, val, 32, rel); + write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff)); + break; case R_SPARC_64: + case R_SPARC_DISP64: case R_SPARC_UA64: // V-xword64 write64be(loc, val); break; case R_SPARC_HH22: // V-imm22 - checkUInt(ctx, loc, val >> 42, 22, rel); write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 42) & 0x003fffff)); break; case R_SPARC_HM10: // T-simm13 - write32be(loc, (read32be(loc) & ~0x00001fff) | ((val >> 32) & 0x000003ff)); + write32be(loc, (read32be(loc) & ~0x000003ff) | ((val >> 32) & 0x000003ff)); + break; + case R_SPARC_WDISP16: + // V-d2/disp14 + checkIntUInt(ctx, loc, val, 18, rel); + write32be(loc, (read32be(loc) & ~0x0303fff) | (((val >> 2) & 0xc000) << 6) | + ((val >> 2) & 0x00003fff)); + break; + case R_SPARC_WDISP19: + // V-disp19 + checkIntUInt(ctx, loc, val, 21, rel); + write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff)); + break; + case R_SPARC_HIX22: + // V-imm22 + checkUInt(ctx, loc, ~val, 32, rel); + write32be(loc, (read32be(loc) & ~0x003fffff) | ((~val >> 10) & 0x003fffff)); + break; + case R_SPARC_LOX10: + case R_SPARC_TLS_LE_LOX10: + // T-simm13 + write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | 0x1c00); break; case R_SPARC_H44: // V-imm22 - checkUInt(ctx, loc, val >> 22, 22, rel); + checkUInt(ctx, loc, val, 44, rel); write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 22) & 0x003fffff)); break; case R_SPARC_M44: @@ -158,23 +414,134 @@ void SPARCV9::relocate(uint8_t *loc, con break; case R_SPARC_L44: // T-imm13 - write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x00000fff)); + write32be(loc, (read32be(loc) & ~0x00000fff) | (val & 0x00000fff)); break; - case R_SPARC_TLS_LE_HIX22: + case R_SPARC_TLS_GD_ADD: + case R_SPARC_TLS_LDM_ADD: + case R_SPARC_TLS_LDO_ADD: + case R_SPARC_TLS_IE_LD: + case R_SPARC_TLS_IE_LDX: + case R_SPARC_TLS_IE_ADD: + // None + break; + case R_SPARC_TLS_LE_HIX22: // Not V-imm2, despite binutils behavior. // T-imm22 write32be(loc, (read32be(loc) & ~0x003fffff) | ((~val >> 10) & 0x003fffff)); break; - case R_SPARC_TLS_LE_LOX10: - // T-simm13 - write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | 0x1C00); + case R_SPARC_GOTDATA_HIX22: + // V-imm22 + checkUInt(ctx, loc, ((int64_t)val < 0 ? ~val : val), 32, rel); + write32be(loc, (read32be(loc) & ~0x003fffff) | + ((((int64_t)val < 0 ? ~val : val) >> 10) & 0x003fffff)); + break; + case R_SPARC_GOTDATA_OP_HIX22: // Not V-imm22, despite binutils behavior. + // Non-relaxed case. + // T-imm22 + write32be(loc, (read32be(loc) & ~0x003fffff) | + ((((int64_t)val < 0 ? ~val : val) >> 10) & 0x003fffff)); + break; + case R_SPARC_GOTDATA_LOX10: + case R_SPARC_GOTDATA_OP_LOX10: // Non-relaxed case. + // T-imm13 + write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | + ((int64_t)val < 0 ? 0x1c00 : 0)); + break; + case R_SPARC_GOTDATA_OP: // Non-relaxed case. + // word32 + // Nothing needs to be done in the non-relaxed case. break; default: llvm_unreachable("unknown relocation"); } } -void SPARCV9::writePlt(uint8_t *buf, const Symbol & /*sym*/, +RelExpr SPARCV9::adjustGotOffExpr(RelType type, const Symbol &sym, + int64_t addend, const uint8_t *loc) const { + switch (type) { + case R_SPARC_GOTDATA_OP_HIX22: + case R_SPARC_GOTDATA_OP_LOX10: + case R_SPARC_GOTDATA_OP: + if (sym.isLocal()) + return R_RELAX_GOT_OFF; + + [[fallthrough]]; + default: + return R_GOT_OFF; + } +} + +void SPARCV9::relaxGot(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + switch (rel.type) { + case R_SPARC_GOTDATA_OP_HIX22: // Not V-imm22, despite binutils behavior. + // T-imm22 + write32be(loc, (read32be(loc) & ~0x003fffff) | + ((((int64_t)val < 0 ? ~val : val) >> 10) & 0x003fffff)); + break; + case R_SPARC_GOTDATA_OP_LOX10: + // T-imm13 + write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | + ((int64_t)val < 0 ? 0x1c00 : 0)); + break; + case R_SPARC_GOTDATA_OP: + // word32 + // ldx [%rs1 + %rs2], %rd -> add %rs1, %rs2, %rd + write32be(loc, (read32be(loc) & 0x3e07c01f) | 0x80000000); + break; + default: + llvm_unreachable("unknown relocation"); + } +} + +void SPARCV9::writeGotHeader(uint8_t *buf) const { + // _GLOBAL_OFFSET_TABLE_[0] = _DYNAMIC when a dynamic section exists. + if (ctx.mainPart->dynamic) + write64(ctx, buf, ctx.mainPart->dynamic->getVA()); +} + +uint64_t SPARCV9::getPltEntryOffset(uint32_t pltIdx, + uint64_t headerSize) const { + if (pltIdx < firstLargePltIndex) + return headerSize + uint64_t{pltIdx} * pltEntrySize; + + uint32_t largeIdx = pltIdx - firstLargePltIndex; + return headerSize + uint64_t{firstLargePltIndex} * pltEntrySize + + largeIdx / pltLargeEntriesPerBlock * pltLargeBlockSize + + largeIdx % pltLargeEntriesPerBlock * pltLargeCodeSize; +} + +uint64_t SPARCV9::getLargePltPointerOffset(uint32_t pltIdx) const { + assert(pltIdx >= firstLargePltIndex); + uint32_t largeIdx = pltIdx - firstLargePltIndex; + uint32_t entriesBeforeBlock = + largeIdx / pltLargeEntriesPerBlock * pltLargeEntriesPerBlock; + uint32_t indexInBlock = largeIdx % pltLargeEntriesPerBlock; + uint64_t largeEntries = ctx.in.plt->getNumEntries() - firstLargePltIndex; + uint64_t entriesInBlock = + std::min(pltLargeEntriesPerBlock, + largeEntries - entriesBeforeBlock); + uint64_t blockOffset = getPltEntryOffset( + firstLargePltIndex + entriesBeforeBlock, ctx.in.plt->headerSize); + + return blockOffset + entriesInBlock * pltLargeCodeSize + + indexInBlock * pltLargePointerSize; +} + +void SPARCV9::writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const { + uint64_t off = pltEntryAddr - ctx.in.plt->getVA(); + if (sym.getPltIdx(ctx) >= firstLargePltIndex) { + uint64_t ptrOff = getLargePltPointerOffset(sym.getPltIdx(ctx)); + write32be(buf, 0x8a10000f); // mov %o7, %g5 + write32be(buf + 4, 0x40000002); // call .+8 + write32be(buf + 8, 0x01000000); // nop + write32be(buf + 12, + 0xc25be000 | ((ptrOff - (off + 4)) & 0x1fff)); + write32be(buf + 16, 0x83c3c001); // jmpl %o7+%g1, %g1 + write32be(buf + 20, 0x9e100005); // mov %g5, %o7 + return; + } + const uint8_t pltData[] = { 0x03, 0x00, 0x00, 0x00, // sethi (. - .PLT0), %g1 0x30, 0x68, 0x00, 0x00, // ba,a %xcc, .PLT1 @@ -187,9 +554,15 @@ void SPARCV9::writePlt(uint8_t *buf, con }; memcpy(buf, pltData, sizeof(pltData)); - uint64_t off = pltEntryAddr - ctx.in.plt->getVA(); relocateNoSym(buf, R_SPARC_22, off); relocateNoSym(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize)); +} + +void SPARCV9::finalizePlt(uint8_t *buf) const { + for (uint32_t i = firstLargePltIndex; i < ctx.in.plt->getNumEntries(); ++i) { + uint64_t off = getPltEntryOffset(i, ctx.in.plt->headerSize); + write64be(buf + getLargePltPointerOffset(i), -(off + 4)); + } } void elf::setSPARCV9TargetInfo(Ctx &ctx) { ctx.target.reset(new SPARCV9(ctx)); } Index: gnu/llvm/llvm/include/llvm/BinaryFormat/ELF.h =================================================================== RCS file: /home/cvs/src/gnu/llvm/llvm/include/llvm/BinaryFormat/ELF.h,v diff -u -p -r1.8 ELF.h --- gnu/llvm/llvm/include/llvm/BinaryFormat/ELF.h 22 Jun 2026 18:17:31 -0000 1.8 +++ gnu/llvm/llvm/include/llvm/BinaryFormat/ELF.h 30 Jul 2026 10:58:21 -0000 @@ -1426,6 +1426,9 @@ enum { STT_LOPROC = 13, // Lowest processor-specific symbol type STT_HIPROC = 15, // Highest processor-specific symbol type + // SPARC symbol types + STT_SPARC_REGISTER = 13, + // AMDGPU symbol types STT_AMDGPU_HSA_KERNEL = 10 }; Index: gnu/llvm/llvm/lib/Target/Sparc/SparcISelLowering.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/llvm/lib/Target/Sparc/SparcISelLowering.cpp,v diff -u -p -r1.6 SparcISelLowering.cpp --- gnu/llvm/llvm/lib/Target/Sparc/SparcISelLowering.cpp 29 May 2026 11:03:40 -0000 1.6 +++ gnu/llvm/llvm/lib/Target/Sparc/SparcISelLowering.cpp 30 Jul 2026 10:58:21 -0000 @@ -1202,16 +1202,43 @@ SparcTargetLowering::LowerCall_32(Target // this table could be generated automatically from RegInfo. Register SparcTargetLowering::getRegisterByName(const char* RegName, LLT VT, const MachineFunction &MF) const { - Register Reg = StringSwitch(RegName) - .Case("i0", SP::I0).Case("i1", SP::I1).Case("i2", SP::I2).Case("i3", SP::I3) - .Case("i4", SP::I4).Case("i5", SP::I5).Case("i6", SP::I6).Case("i7", SP::I7) - .Case("o0", SP::O0).Case("o1", SP::O1).Case("o2", SP::O2).Case("o3", SP::O3) - .Case("o4", SP::O4).Case("o5", SP::O5).Case("o6", SP::O6).Case("o7", SP::O7) - .Case("l0", SP::L0).Case("l1", SP::L1).Case("l2", SP::L2).Case("l3", SP::L3) - .Case("l4", SP::L4).Case("l5", SP::L5).Case("l6", SP::L6).Case("l7", SP::L7) - .Case("g0", SP::G0).Case("g1", SP::G1).Case("g2", SP::G2).Case("g3", SP::G3) - .Case("g4", SP::G4).Case("g5", SP::G5).Case("g6", SP::G6).Case("g7", SP::G7) - .Default(0); + StringRef Name(RegName); + Name.consume_front("%"); + + Register Reg = StringSwitch(Name) + .Cases({"r24", "i0"}, SP::I0) + .Cases({"r25", "i1"}, SP::I1) + .Cases({"r26", "i2"}, SP::I2) + .Cases({"r27", "i3"}, SP::I3) + .Cases({"r28", "i4"}, SP::I4) + .Cases({"r29", "i5"}, SP::I5) + .Cases({"r30", "i6", "fp"}, SP::I6) + .Cases({"r31", "i7"}, SP::I7) + .Cases({"r8", "o0"}, SP::O0) + .Cases({"r9", "o1"}, SP::O1) + .Cases({"r10", "o2"}, SP::O2) + .Cases({"r11", "o3"}, SP::O3) + .Cases({"r12", "o4"}, SP::O4) + .Cases({"r13", "o5"}, SP::O5) + .Cases({"r14", "o6", "sp"}, SP::O6) + .Cases({"r15", "o7"}, SP::O7) + .Cases({"r16", "l0"}, SP::L0) + .Cases({"r17", "l1"}, SP::L1) + .Cases({"r18", "l2"}, SP::L2) + .Cases({"r19", "l3"}, SP::L3) + .Cases({"r20", "l4"}, SP::L4) + .Cases({"r21", "l5"}, SP::L5) + .Cases({"r22", "l6"}, SP::L6) + .Cases({"r23", "l7"}, SP::L7) + .Cases({"r0", "g0"}, SP::G0) + .Cases({"r1", "g1"}, SP::G1) + .Cases({"r2", "g2"}, SP::G2) + .Cases({"r3", "g3"}, SP::G3) + .Cases({"r4", "g4"}, SP::G4) + .Cases({"r5", "g5"}, SP::G5) + .Cases({"r6", "g6"}, SP::G6) + .Cases({"r7", "g7"}, SP::G7) + .Default(0); // If we're directly referencing register names // (e.g in GCC C extension `register int r asm("g1");`), Index: gnu/llvm/llvm/lib/Target/Sparc/AsmParser/SparcAsmParser.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/llvm/lib/Target/Sparc/AsmParser/SparcAsmParser.cpp,v diff -u -p -r1.1.1.6 SparcAsmParser.cpp --- gnu/llvm/llvm/lib/Target/Sparc/AsmParser/SparcAsmParser.cpp 29 May 2026 11:00:49 -0000 1.1.1.6 +++ gnu/llvm/llvm/lib/Target/Sparc/AsmParser/SparcAsmParser.cpp 30 Jul 2026 10:58:21 -0000 @@ -39,6 +39,7 @@ #include #include #include +#include using namespace llvm; @@ -81,6 +82,7 @@ class SparcAsmParser : public MCTargetAs bool parseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands) override; ParseStatus parseDirective(AsmToken DirectiveID) override; + bool parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc) override; unsigned validateTargetOperandClass(MCParsedAsmOperand &Op, unsigned Kind) override; @@ -1044,6 +1046,27 @@ ParseStatus SparcAsmParser::parseDirecti Parser.eatToEndOfStatement(); return ParseStatus::Success; } + if (IDVal == ".seg") { + std::string Name; + if (Parser.parseEscapedString(Name) || Parser.parseEOL()) + return ParseStatus::Failure; + + MCSection *Section; + uint32_t Subsection = 0; + const MCObjectFileInfo *MCOFI = getContext().getObjectFileInfo(); + if (Name == "text") { + Section = MCOFI->getTextSection(); + } else if (Name == "data" || Name == "data1") { + Section = MCOFI->getDataSection(); + Subsection = Name == "data1" ? 1 : 0; + } else if (Name == "bss") { + Section = MCOFI->getBSSSection(); + } else { + return Error(DirectiveID.getLoc(), "unknown segment type"); + } + getStreamer().switchSection(Section, Subsection); + return ParseStatus::Success; + } // Let the MC layer to handle other directives. return ParseStatus::NoMatch; @@ -1686,7 +1709,9 @@ SparcAsmParser::adjustPICRelocation(uint // actually a %pc10 or %pc22 relocation. Otherwise, they are interpreted // as %got10 or %got22 relocation. - if (getContext().getObjectFileInfo()->isPositionIndependent()) { + int64_t AbsoluteValue; + if (getContext().getObjectFileInfo()->isPositionIndependent() && + !subExpr->evaluateAsAbsolute(AbsoluteValue)) { switch (RelType) { default: break; case ELF::R_SPARC_LO10: @@ -1744,6 +1769,16 @@ bool SparcAsmParser::matchSparcAsmModifi EVal = adjustPICRelocation(VK, subExpr); return true; +} + +bool SparcAsmParser::parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc) { + if (Parser.getTok().is(AsmToken::Percent)) { + Parser.Lex(); + if (matchSparcAsmModifiers(Res, EndLoc)) + return false; + return true; + } + return Parser.parsePrimaryExpr(Res, EndLoc, nullptr); } bool SparcAsmParser::isPossibleExpression(const AsmToken &Token) {