From: Robert Nagy Subject: Re: LLVM toolchain for sparc64 To: semarie@online.fr, tech@openbsd.org Date: Thu, 30 Jul 2026 14:29:14 +0200 fine by me On 30/07/26 13:38 +0200, Kirill A. Korinsky wrote: > Folks, > > I'd like to commit to devel/llvm/22 and gnu/llvm my cumulative patch for > support sparc64 by LLVM toolchain. > > Not OpenBSD related bits was upstreamed as a few PRs and some parts are > actually already merged. > > More or less this diff was tested by me and tb@ who runs his bulk during > which many small edge cases was discovered and fixed. > > Here two diffs for the base and ports, I did my best to keep in > synchronized. > > I haven't backported that changes to LLVM before 22, but we already mainly > switched to LLVM 22 everywhere. > > I think it may have some missed pieces, but working in the tree will make > testing it simpler. > > Ok to commit? > > -- > wbr, Kirill > 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 RE_PPC64_CALL, RE_PPC64_RELAX_TOC, RE_AARCH64_PAGE_PC, > - R_RELAX_GOT_PC, RE_RISCV_PC_INDIRECT, RE_PPC64_RELAX_GOT_PC, > - RE_LOONGARCH_PAGE_PC, RE_LOONGARCH_PC_INDIRECT>(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) { > Index: Makefile > =================================================================== > RCS file: /home/cvs/ports/devel/llvm/22/Makefile,v > diff -u -p -r1.21 Makefile > --- Makefile 25 Jul 2026 20:07:48 -0000 1.21 > +++ Makefile 29 Jul 2026 19:24:27 -0000 > @@ -2,7 +2,7 @@ LLVM_MAJOR = 22 > LLVM_VERSION = ${LLVM_MAJOR}.1.8 > LLVM_PKGSPEC = >=22,<23 > > -REVISION = 3 > +REVISION = 4 > > SHARED_LIBS += LLVM 0.0 \ > LTO 0.0 \ > Index: patches/patch-lld_ELF_Arch_SPARCV9_cpp > =================================================================== > RCS file: patches/patch-lld_ELF_Arch_SPARCV9_cpp > diff -N patches/patch-lld_ELF_Arch_SPARCV9_cpp > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ patches/patch-lld_ELF_Arch_SPARCV9_cpp 29 Jul 2026 19:22:39 -0000 > @@ -0,0 +1,597 @@ > +Index: lld/ELF/Arch/SPARCV9.cpp > +--- lld/ELF/Arch/SPARCV9.cpp.orig > ++++ lld/ELF/Arch/SPARCV9.cpp > +@@ -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(ctx) { > + 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(ctx) { > + 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, const Symbol > + } > + } > + > ++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_WDISP22: > ++ // V-disp22 > ++ checkIntUInt(ctx, loc, val, 24, rel); > ++ write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 2) & 0x003fffff)); > ++ break; > ++ 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_22: > + // V-imm22 > + checkUInt(ctx, loc, val, 22, rel); > + write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff)); > + break; > +- case R_SPARC_GOT22: > +- case R_SPARC_PC22: > +- case R_SPARC_LM22: > +- // T-imm22 > +- write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff)); > ++ 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_HI22: > +- // V-imm22 > +- checkUInt(ctx, loc, val >> 10, 22, rel); > +- write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff)); > +- break; > +- case R_SPARC_WDISP19: > +- // V-disp19 > +- checkInt(ctx, loc, val, 21, rel); > +- write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff)); > +- 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, const Relocation > + 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, const Symbol & /* > + }; > + 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: patches/patch-lld_ELF_Driver_cpp > =================================================================== > RCS file: /home/cvs/ports/devel/llvm/22/patches/patch-lld_ELF_Driver_cpp,v > diff -u -p -r1.2 patch-lld_ELF_Driver_cpp > --- patches/patch-lld_ELF_Driver_cpp 2 Apr 2026 09:15:11 -0000 1.2 > +++ patches/patch-lld_ELF_Driver_cpp 29 Jul 2026 19:22:39 -0000 > @@ -33,7 +33,21 @@ Index: lld/ELF/Driver.cpp > if (ctx.arg.emachine != EM_AARCH64 && ctx.arg.emachine != EM_ARM && > ctx.arg.zExecuteOnlyReport != ReportPolicy::None) > ErrAlways(ctx) > -@@ -1420,8 +1434,6 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &a > +@@ -1302,9 +1316,10 @@ static SmallVector getSymbolOrderingFile > + > + 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()); > +@@ -1420,8 +1435,6 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &a > > ctx.e.errorHandlingScript = args.getLastArgValue(OPT_error_handling_script); > > @@ -42,7 +56,7 @@ Index: lld/ELF/Driver.cpp > ctx.arg.exportDynamic = > args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false) || > args.hasArg(OPT_shared); > -@@ -1442,8 +1454,15 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &a > +@@ -1442,8 +1455,15 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &a > ctx.arg.icf = getICF(args); > ctx.arg.ignoreDataAddressEquality = > args.hasArg(OPT_ignore_data_address_equality); > @@ -58,7 +72,7 @@ Index: lld/ELF/Driver.cpp > ctx.arg.init = args.getLastArgValue(OPT_init, "_init"); > ctx.arg.ltoAAPipeline = args.getLastArgValue(OPT_lto_aa_pipeline); > ctx.arg.ltoCSProfileGenerate = args.hasArg(OPT_lto_cs_profile_generate); > -@@ -1512,7 +1531,12 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &a > +@@ -1512,7 +1532,12 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &a > ctx.arg.outputFile = args.getLastArgValue(OPT_o); > if (auto *arg = args.getLastArg(OPT_package_metadata)) > parsePackageMetadata(ctx, *arg); > @@ -71,7 +85,7 @@ Index: lld/ELF/Driver.cpp > ctx.arg.printIcfSections = > args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false); > if (auto *arg = > -@@ -1604,7 +1628,11 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &a > +@@ -1604,7 +1629,11 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &a > ctx.arg.trace = args.hasArg(OPT_trace); > ctx.arg.undefined = args::getStrings(args, OPT_undefined); > ctx.arg.undefinedVersion = > @@ -83,7 +97,7 @@ Index: lld/ELF/Driver.cpp > ctx.arg.unique = args.hasArg(OPT_unique); > ctx.arg.useAndroidRelrTags = args.hasFlag( > OPT_use_android_relr_tags, OPT_no_use_android_relr_tags, false); > -@@ -1851,9 +1879,9 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &a > +@@ -1851,9 +1880,9 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &a > << arg->getValue() << "'"; > parallel::strategy = hardware_concurrency(threads); > ctx.arg.thinLTOJobs = v; > @@ -96,7 +110,7 @@ Index: lld/ELF/Driver.cpp > } > if (auto *arg = args.getLastArg(OPT_thinlto_jobs_eq)) > ctx.arg.thinLTOJobs = arg->getValue(); > -@@ -2070,6 +2098,23 @@ static void setConfigs(Ctx &ctx, opt::InputArgList &ar > +@@ -2070,6 +2099,23 @@ static void setConfigs(Ctx &ctx, opt::InputArgList &ar > ErrAlways(ctx) << "cannot open --why-extract= file " << ctx.arg.whyExtract > << ": " << e.message(); > } > @@ -120,7 +134,7 @@ Index: lld/ELF/Driver.cpp > } > > static bool isFormatBinary(Ctx &ctx, StringRef s) { > -@@ -2234,7 +2279,7 @@ void LinkerDriver::inferMachineType() { > +@@ -2234,7 +2280,7 @@ void LinkerDriver::inferMachineType() { > } > > // Parse -z max-page-size=. The default value is defined by > @@ -129,7 +143,7 @@ Index: lld/ELF/Driver.cpp > static uint64_t getMaxPageSize(Ctx &ctx, opt::InputArgList &args) { > uint64_t val = args::getZOptionValue(args, OPT_z, "max-page-size", > ctx.target->defaultMaxPageSize); > -@@ -2252,7 +2297,7 @@ static uint64_t getMaxPageSize(Ctx &ctx, opt::InputArg > +@@ -2252,7 +2298,7 @@ static uint64_t getMaxPageSize(Ctx &ctx, opt::InputArg > } > > // Parse -z common-page-size=. The default value is defined by > @@ -138,7 +152,7 @@ Index: lld/ELF/Driver.cpp > static uint64_t getCommonPageSize(Ctx &ctx, opt::InputArgList &args) { > uint64_t val = args::getZOptionValue(args, OPT_z, "common-page-size", > ctx.target->defaultCommonPageSize); > -@@ -2272,6 +2317,16 @@ static uint64_t getCommonPageSize(Ctx &ctx, opt::Input > +@@ -2272,6 +2318,16 @@ static uint64_t getCommonPageSize(Ctx &ctx, opt::Input > return val; > } > > @@ -155,7 +169,7 @@ Index: lld/ELF/Driver.cpp > // Parses --image-base option. > static std::optional getImageBase(Ctx &ctx, opt::InputArgList &args) { > // Because we are using `ctx.arg.maxPageSize` here, this function has to be > -@@ -3435,6 +3490,11 @@ template void LinkerDriver::link(opt::Inp > +@@ -3435,6 +3491,11 @@ template void LinkerDriver::link(opt::Inp > // optimizations such as DATA_SEGMENT_ALIGN in linker scripts. LLD's use of it > // is limited to writing trap instructions on the last executable segment. > ctx.arg.commonPageSize = getCommonPageSize(ctx, args); > Index: patches/patch-lld_ELF_InputFiles_cpp > =================================================================== > RCS file: /home/cvs/ports/devel/llvm/22/patches/patch-lld_ELF_InputFiles_cpp,v > diff -u -p -r1.2 patch-lld_ELF_InputFiles_cpp > --- patches/patch-lld_ELF_InputFiles_cpp 2 Jun 2026 13:16:58 -0000 1.2 > +++ patches/patch-lld_ELF_InputFiles_cpp 29 Jul 2026 19:22:39 -0000 > @@ -43,7 +43,66 @@ Index: lld/ELF/InputFiles.cpp > break; > case SHT_LLVM_LTO: > // Discard .llvm.lto in a relocatable link that does not use the bitcode. > -@@ -1565,6 +1586,9 @@ template void SharedFile::parse() { > +@@ -1192,7 +1213,20 @@ InputSectionBase *ObjFile::createInputSection(ui > + > + // 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(); > + if (!symbols) > +@@ -1200,14 +1234,25 @@ void ObjFile::initializeSymbols(const object::EL > + > + // 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); > +@@ -1307,6 +1352,8 @@ template void ObjFile::postParse() > + 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(); > +@@ -1565,6 +1612,9 @@ template void SharedFile::parse() { > const ELFFile obj = this->getObj(); > ArrayRef sections = getELFShdrs(); > > @@ -53,7 +112,7 @@ Index: lld/ELF/InputFiles.cpp > const Elf_Shdr *versymSec = nullptr; > const Elf_Shdr *verdefSec = nullptr; > const Elf_Shdr *verneedSec = nullptr; > -@@ -1588,6 +1612,13 @@ template void SharedFile::parse() { > +@@ -1588,7 +1638,14 @@ template void SharedFile::parse() { > case SHT_GNU_verneed: > verneedSec = &sec; > break; > @@ -63,7 +122,18 @@ Index: lld/ELF/InputFiles.cpp > + CHECK2(obj.template getSectionContentsAsArray(sec), this); > + parseGNUWarning(ctx, name, data, sec.sh_size); > + break; > -+ } > } > ++ } > } > > + if (versymSec && numSymbols == 0) { > +@@ -1659,6 +1716,9 @@ template void SharedFile::parse() { > + 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: patches/patch-lld_ELF_InputSection_cpp > =================================================================== > RCS file: patches/patch-lld_ELF_InputSection_cpp > diff -N patches/patch-lld_ELF_InputSection_cpp > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ patches/patch-lld_ELF_InputSection_cpp 29 Jul 2026 19:22:39 -0000 > @@ -0,0 +1,11 @@ > +Index: lld/ELF/InputSection.cpp > +--- lld/ELF/InputSection.cpp.orig > ++++ lld/ELF/InputSection.cpp > +@@ -838,6 +838,7 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, > + 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: patches/patch-lld_ELF_Relocations_cpp > =================================================================== > RCS file: /home/cvs/ports/devel/llvm/22/patches/patch-lld_ELF_Relocations_cpp,v > diff -u -p -r1.2 patch-lld_ELF_Relocations_cpp > --- patches/patch-lld_ELF_Relocations_cpp 6 May 2026 12:47:03 -0000 1.2 > +++ patches/patch-lld_ELF_Relocations_cpp 29 Jul 2026 19:22:39 -0000 > @@ -31,17 +31,171 @@ Index: lld/ELF/Relocations.cpp > // Construct a message in the following format. > // > // >>> defined in /home/alice/src/foo.o > -@@ -681,6 +704,13 @@ bool RelocScan::maybeReportUndefined(Undefined &sym, u > - // .got2 > +@@ -147,8 +170,9 @@ bool lld::elf::needsGot(RelExpr expr) { > + static bool isRelExpr(RelExpr expr) { > + return oneof + RE_PPC64_CALL, RE_PPC64_RELAX_TOC, RE_AARCH64_PAGE_PC, > +- R_RELAX_GOT_PC, RE_RISCV_PC_INDIRECT, RE_PPC64_RELAX_GOT_PC, > +- RE_LOONGARCH_PAGE_PC, RE_LOONGARCH_PC_INDIRECT>(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) { > +@@ -682,6 +706,13 @@ bool RelocScan::maybeReportUndefined(Undefined &sym, u > if (sym.discardedSecIdx != 0 && (sec->name == ".got2" || sec->name == ".toc")) > return false; > -+ > + > +#ifdef __OpenBSD__ > + // GCC (at least 8 and 11) can produce a ".gcc_except_table" with relocations > + // to discarded sections on riscv64 > + if (sym.discardedSecIdx != 0 && sec->name == ".gcc_except_table") > + return false; > +#endif > - > ++ > bool isWarning = > (ctx.arg.unresolvedSymbols == UnresolvedPolicy::Warn && canBeExternal) || > + ctx.arg.noinhibitExec; > +@@ -740,14 +771,18 @@ static void addRelativeReloc(Ctx &ctx, InputSectionBas > + 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) > ++ 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), true, sym, 0, R_ADDEND}); > +- else > +- 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) { > +@@ -915,25 +950,32 @@ void RelocScan::process(RelExpr expr, RelType type, ui > + // 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); > + } > + } > +@@ -992,8 +1034,14 @@ void RelocScan::process(RelExpr expr, RelType type, ui > + (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; > + } > +@@ -1036,6 +1084,7 @@ void RelocScan::process(RelExpr expr, RelType type, ui > + 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. > +@@ -1206,12 +1255,13 @@ unsigned RelocScan::handleTlsRelocation(RelExpr expr, > + > + // 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; > +@@ -1555,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 > +@@ -2203,11 +2250,11 @@ bool ThunkCreator::createThunks(uint32_t pass, > + 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) { > +@@ -2221,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: patches/patch-lld_ELF_Relocations_h > =================================================================== > RCS file: /home/cvs/ports/devel/llvm/22/patches/patch-lld_ELF_Relocations_h,v > diff -u -p -r1.1 patch-lld_ELF_Relocations_h > --- patches/patch-lld_ELF_Relocations_h 6 May 2026 12:47:03 -0000 1.1 > +++ patches/patch-lld_ELF_Relocations_h 29 Jul 2026 19:22:39 -0000 > @@ -1,13 +1,27 @@ > Index: lld/ELF/Relocations.h > --- lld/ELF/Relocations.h.orig > +++ lld/ELF/Relocations.h > -@@ -176,6 +176,9 @@ bool hexagonNeedsTLSSymbol(ArrayRef o > +@@ -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,10 +172,13 @@ bool maybeReportUndefined(Ctx &, Undefined &sym, Input > + void postScanRelocations(Ctx &ctx); > + void addGotEntry(Ctx &ctx, Symbol &sym); > > - bool isAbsolute(const Symbol &sym); > +-void hexagonTLSSymbolUpdate(Ctx &ctx); > +-bool hexagonNeedsTLSSymbol(ArrayRef outputSections); > ++void tlsSymbolUpdate(Ctx &ctx); > ++bool needsTLSSymbol(ArrayRef outputSections); > > + bool isAbsolute(const Symbol &sym); > ++ > +void reportGNUWarning(Ctx &ctx, Symbol &sym, InputSectionBase &sec, > + uint64_t offset); > -+ > + > class ThunkSection; > class Thunk; > - class InputSectionDescription; > Index: patches/patch-lld_ELF_Symbols_cpp > =================================================================== > RCS file: /home/cvs/ports/devel/llvm/22/patches/patch-lld_ELF_Symbols_cpp,v > diff -u -p -r1.1.1.1 patch-lld_ELF_Symbols_cpp > --- patches/patch-lld_ELF_Symbols_cpp 25 Feb 2026 13:31:43 -0000 1.1.1.1 > +++ patches/patch-lld_ELF_Symbols_cpp 29 Jul 2026 19:22:39 -0000 > @@ -12,3 +12,26 @@ Index: lld/ELF/Symbols.cpp > > template struct AssertSymbol { > static_assert(std::is_trivially_destructible(), > +@@ -173,11 +173,20 @@ uint64_t Symbol::getGotPltOffset(Ctx &ctx) const { > + 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: patches/patch-lld_ELF_Symbols_h > =================================================================== > RCS file: /home/cvs/ports/devel/llvm/22/patches/patch-lld_ELF_Symbols_h,v > diff -u -p -r1.4 patch-lld_ELF_Symbols_h > --- patches/patch-lld_ELF_Symbols_h 10 Jul 2026 06:50:19 -0000 1.4 > +++ patches/patch-lld_ELF_Symbols_h 29 Jul 2026 19:22:39 -0000 > @@ -11,7 +11,15 @@ Index: lld/ELF/Symbols.h > // Symbol visibility. This is the computed minimum visibility of all > // observed non-DSO symbols. > uint8_t visibility() const { return stOther & 3; } > -@@ -244,7 +247,7 @@ class Symbol { (public) > +@@ -204,6 +207,7 @@ class Symbol { (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; > +@@ -244,7 +248,7 @@ class Symbol { (public) > : file(file), nameData(name.data()), nameSize(name.size()), type(type), > binding(binding), stOther(stOther), symbolKind(k), isPreemptible(false), > isUsedInRegularObj(false), used(false), isExported(false), > @@ -20,7 +28,7 @@ Index: lld/ELF/Symbols.h > isInIplt(false), gotInIgot(false), folded(false), > archSpecificBit(false), scriptDefined(false), dsoDefined(false), > dsoProtected(false), versionScriptAssigned(false), thunkAccessed(false), > -@@ -540,6 +543,8 @@ void reportDuplicate(Ctx &, const Symbol &sym, const I > +@@ -540,6 +544,8 @@ void reportDuplicate(Ctx &, const Symbol &sym, const I > void maybeWarnUnorderableSymbol(Ctx &, const Symbol *sym); > bool computeIsPreemptible(Ctx &, const Symbol &sym); > void parseVersionAndComputeIsPreemptible(Ctx &); > Index: patches/patch-lld_ELF_SyntheticSections_cpp > =================================================================== > RCS file: /home/cvs/ports/devel/llvm/22/patches/patch-lld_ELF_SyntheticSections_cpp,v > diff -u -p -r1.1.1.1 patch-lld_ELF_SyntheticSections_cpp > --- patches/patch-lld_ELF_SyntheticSections_cpp 25 Feb 2026 13:31:43 -0000 1.1.1.1 > +++ patches/patch-lld_ELF_SyntheticSections_cpp 29 Jul 2026 19:22:39 -0000 > @@ -1,7 +1,98 @@ > Index: lld/ELF/SyntheticSections.cpp > --- lld/ELF/SyntheticSections.cpp.orig > +++ lld/ELF/SyntheticSections.cpp > -@@ -2602,6 +2602,10 @@ PltSection::PltSection(Ctx &ctx) > +@@ -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::finalizeContents() { > + 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::finalizeContents() { > + 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::finalizeContents() { > + > + // 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(Symbol *b) { > + 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(SmallVectorImpl + // 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 *buf) { > + > + 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; > +@@ -2602,23 +2619,29 @@ PltSection::PltSection(Ctx &ctx) > if ((ctx.arg.emachine == EM_386 || ctx.arg.emachine == EM_X86_64) && > (ctx.arg.andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT)) > name = ".plt.sec"; > @@ -12,7 +103,58 @@ Index: lld/ELF/SyntheticSections.cpp > > // The PLT needs to be writable on SPARC as the dynamic linker will > // modify the instructions in the PLT entries. > -@@ -4909,6 +4913,12 @@ template void elf::createSyntheticSection > +- 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) { > +@@ -2641,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) > +@@ -4878,10 +4898,12 @@ template void elf::createSyntheticSection > + // _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. > +@@ -4909,6 +4931,12 @@ template void elf::createSyntheticSection > ctx.in.gnuProperty = std::make_unique(ctx); > add(*ctx.in.gnuProperty); > } > Index: patches/patch-lld_ELF_SyntheticSections_h > =================================================================== > RCS file: /home/cvs/ports/devel/llvm/22/patches/patch-lld_ELF_SyntheticSections_h,v > diff -u -p -r1.1.1.1 patch-lld_ELF_SyntheticSections_h > --- patches/patch-lld_ELF_SyntheticSections_h 25 Feb 2026 13:31:43 -0000 1.1.1.1 > +++ patches/patch-lld_ELF_SyntheticSections_h 29 Jul 2026 19:22:39 -0000 > @@ -1,7 +1,35 @@ > Index: lld/ELF/SyntheticSections.h > --- lld/ELF/SyntheticSections.h.orig > +++ lld/ELF/SyntheticSections.h > -@@ -1462,7 +1462,11 @@ void addVerneed(Ctx &, Symbol &ss); > +@@ -433,6 +433,11 @@ class DynamicReloc { (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 @@ class SymbolTableBaseSection : public SyntheticSection > + 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 @@ class SymbolTableBaseSection : public SyntheticSection > + > + // A vector of symbols and their string table offsets. > + SmallVector symbols; > ++ size_t numLocalDynamicSymbols = 0; > + > + StringTableSection &strTabSec; > + > +@@ -1462,7 +1469,11 @@ void addVerneed(Ctx &, Symbol &ss); > // placed in it. > struct PhdrEntry { > PhdrEntry(Ctx &ctx, unsigned type, unsigned flags) > Index: patches/patch-lld_ELF_Target_cpp > =================================================================== > RCS file: patches/patch-lld_ELF_Target_cpp > diff -N patches/patch-lld_ELF_Target_cpp > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ patches/patch-lld_ELF_Target_cpp 29 Jul 2026 19:22:39 -0000 > @@ -0,0 +1,16 @@ > +Index: lld/ELF/Target.cpp > +--- lld/ELF/Target.cpp.orig > ++++ lld/ELF/Target.cpp > +@@ -158,6 +158,12 @@ RelExpr TargetInfo::adjustGotPcExpr(RelType type, int6 > + 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: patches/patch-lld_ELF_Target_h > =================================================================== > RCS file: patches/patch-lld_ELF_Target_h > diff -N patches/patch-lld_ELF_Target_h > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ patches/patch-lld_ELF_Target_h 29 Jul 2026 19:22:39 -0000 > @@ -0,0 +1,63 @@ > +Index: lld/ELF/Target.h > +--- lld/ELF/Target.h.orig > ++++ lld/ELF/Target.h > +@@ -22,6 +22,7 @@ > + namespace lld { > + namespace elf { > + class Defined; > ++class DynamicReloc; > + class InputFile; > + class Symbol; > + template struct Relocs; > +@@ -35,6 +36,9 @@ class TargetInfo { (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 @@ class TargetInfo { (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 @@ class TargetInfo { (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 @@ class TargetInfo { (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 @@ class TargetInfo { (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: patches/patch-lld_ELF_Writer_cpp > =================================================================== > RCS file: /home/cvs/ports/devel/llvm/22/patches/patch-lld_ELF_Writer_cpp,v > diff -u -p -r1.1.1.1 patch-lld_ELF_Writer_cpp > --- patches/patch-lld_ELF_Writer_cpp 25 Feb 2026 13:31:43 -0000 1.1.1.1 > +++ patches/patch-lld_ELF_Writer_cpp 29 Jul 2026 19:22:39 -0000 > @@ -18,10 +18,12 @@ Index: lld/ELF/Writer.cpp > (ctx.arg.discard == DiscardPolicy::Locals || > (sym.section && (sym.section->flags & SHF_MERGE)))) > return false; > -@@ -625,7 +626,11 @@ static bool isRelroSection(Ctx &ctx, const OutputSecti > +@@ -624,8 +625,12 @@ static bool isRelroSection(Ctx &ctx, const OutputSecti > + // 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 (sec == ctx.in.gotPlt->getParent()) > ++ if (ctx.target->usesGotPlt && sec == ctx.in.gotPlt->getParent()) > +#ifndef __OpenBSD__ > return ctx.arg.zNow; > +#else > @@ -43,7 +45,24 @@ Index: lld/ELF/Writer.cpp > > return abiAgnostic || abiSpecific; > } > -@@ -927,6 +935,9 @@ template void Writer::setReservedSy > +@@ -855,10 +863,14 @@ template void Writer::setReservedSy > + 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; > + } > + > +@@ -927,6 +939,9 @@ template void Writer::setReservedSy > ctx.sym.bss->section = sbss ? sbss : findSection(ctx, ".bss"); > } > > @@ -53,7 +72,50 @@ Index: lld/ELF/Writer.cpp > // Setup MIPS _gp_disp/__gnu_local_gp symbols which should > // be equal to the _gp symbol's value. > if (ctx.sym.mipsGp) { > -@@ -2454,34 +2465,22 @@ Writer::createPhdrs(Partition &part) { > +@@ -1533,9 +1548,9 @@ template void Writer::finalizeAddre > + }; > + 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); > +@@ -2031,10 +2046,10 @@ template void Writer::finalizeSecti > + 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}); > +@@ -2300,6 +2315,15 @@ static bool needsPtLoad(OutputSection *sec) { > + 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; > +@@ -2454,34 +2478,22 @@ Writer::createPhdrs(Partition &part) { > addHdr(PT_GNU_EH_FRAME, part.ehFrameHdr->getParent()->getPhdrFlags()) > ->add(part.ehFrameHdr->getParent()); > > @@ -101,7 +163,7 @@ Index: lld/ELF/Writer.cpp > // PT_OPENBSD_NOBTCFI is an OpenBSD-specific header to mark that the > // executable is expected to violate branch-target CFI checks. > if (ctx.arg.zNoBtCfi) > -@@ -2493,7 +2492,19 @@ Writer::createPhdrs(Partition &part) { > +@@ -2493,7 +2505,19 @@ Writer::createPhdrs(Partition &part) { > // OpenBSD. > if (ctx.arg.zWxneeded) > addHdr(PT_OPENBSD_WXNEEDED, PF_X); > @@ -121,7 +183,7 @@ Index: lld/ELF/Writer.cpp > if (OutputSection *cmd = findSection(ctx, ".note.gnu.property", partNo)) > addHdr(PT_GNU_PROPERTY, PF_R)->add(cmd); > > -@@ -2586,6 +2597,31 @@ template void Writer::fixSectionAli > +@@ -2586,6 +2610,31 @@ template void Writer::fixSectionAli > }; > } > }; > Index: patches/patch-llvm_include_llvm_BinaryFormat_ELF_h > =================================================================== > RCS file: patches/patch-llvm_include_llvm_BinaryFormat_ELF_h > diff -N patches/patch-llvm_include_llvm_BinaryFormat_ELF_h > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ patches/patch-llvm_include_llvm_BinaryFormat_ELF_h 29 Jul 2026 19:22:39 -0000 > @@ -0,0 +1,13 @@ > +Index: llvm/include/llvm/BinaryFormat/ELF.h > +--- llvm/include/llvm/BinaryFormat/ELF.h.orig > ++++ llvm/include/llvm/BinaryFormat/ELF.h > +@@ -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: patches/patch-llvm_lib_Target_Sparc_AsmParser_SparcAsmParser_cpp > =================================================================== > RCS file: patches/patch-llvm_lib_Target_Sparc_AsmParser_SparcAsmParser_cpp > diff -N patches/patch-llvm_lib_Target_Sparc_AsmParser_SparcAsmParser_cpp > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ patches/patch-llvm_lib_Target_Sparc_AsmParser_SparcAsmParser_cpp 29 Jul 2026 19:22:39 -0000 > @@ -0,0 +1,76 @@ > +Index: llvm/lib/Target/Sparc/AsmParser/SparcAsmParser.cpp > +--- llvm/lib/Target/Sparc/AsmParser/SparcAsmParser.cpp.orig > ++++ llvm/lib/Target/Sparc/AsmParser/SparcAsmParser.cpp > +@@ -39,6 +39,7 @@ > + #include > + #include > + #include > ++#include > + > + using namespace llvm; > + > +@@ -81,6 +82,7 @@ class SparcAsmParser : public MCTargetAsmParser { > + 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,7 +1046,28 @@ ParseStatus SparcAsmParser::parseDirective(AsmToken Di > + 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(uint16_t RelType, > + // 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::matchSparcAsmModifiers(const MCEx > + > + 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) { > Index: patches/patch-llvm_lib_Target_Sparc_SparcISelLowering_cpp > =================================================================== > RCS file: patches/patch-llvm_lib_Target_Sparc_SparcISelLowering_cpp > diff -N patches/patch-llvm_lib_Target_Sparc_SparcISelLowering_cpp > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ patches/patch-llvm_lib_Target_Sparc_SparcISelLowering_cpp 29 Jul 2026 19:22:39 -0000 > @@ -0,0 +1,57 @@ > +Index: llvm/lib/Target/Sparc/SparcISelLowering.cpp > +--- llvm/lib/Target/Sparc/SparcISelLowering.cpp.orig > ++++ llvm/lib/Target/Sparc/SparcISelLowering.cpp > +@@ -1202,16 +1202,43 @@ SparcTargetLowering::LowerCall_32(TargetLowering::Call > + // 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");`), -- Regards, Robert Nagy