Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
LLVM toolchain for sparc64
To:
Sebastien Marie <semarie@online.fr>, Robert Nagy <robert@openbsd.org>
Cc:
OpenBSD tech <tech@openbsd.org>
Date:
Thu, 30 Jul 2026 13:38:55 +0200

Download raw body.

Thread
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<StringRef, 0> 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<ELFT>::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 <class ELFT>
+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 <class ELFT>
 void ObjFile<ELFT>::initializeSymbols(const object::ELFFile<ELFT> &obj) {
   ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
@@ -1221,14 +1234,25 @@ void ObjFile<ELFT>::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<ELFT>(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<unsigned, 32> undefineds;
   for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) {
     const Elf_Sym &eSym = eSyms[i];
+    if (isSparcRegister<ELFT>(ctx, eSym))
+      continue;
     uint32_t secIdx = eSym.st_shndx;
     if (secIdx == SHN_UNDEF) {
       undefineds.push_back(i);
@@ -1330,6 +1354,8 @@ template <class ELFT> void ObjFile<ELFT>
   ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
   for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) {
     const Elf_Sym &eSym = eSyms[i];
+    if (isSparcRegister<ELFT>(ctx, eSym))
+      continue;
     Symbol &sym = *symbols[i];
     uint32_t secIdx = eSym.st_shndx;
     uint8_t binding = eSym.getBinding();
@@ -1692,6 +1718,9 @@ template <class ELFT> void SharedFile::p
   ArrayRef<Elf_Sym> syms = this->getGlobalELFSyms<ELFT>();
   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<ELFT>(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<R_PC, R_GOTREL, R_GOTPLTREL, RE_ARM_PCA, RE_MIPS_GOTREL,
                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 <class PltSection, class GotPltSection>
 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<EhInputSection>(sec) && ctx.arg.emachine != EM_MIPS));
   if (canWrite) {
     RelType rel = ctx.target->getDynRel(type);
-    if (oneof<R_GOT, RE_LOONGARCH_GOT>(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<R_GOT, RE_LOONGARCH_GOT>(expr) || useRelative) {
       addRelativeReloc<true>(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<OutputSection *> 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<OutputSection *> 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<OutputSection *> outputSections);
+void tlsSymbolUpdate(Ctx &ctx);
+bool needsTLSSymbol(ArrayRef<OutputSection *> 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<ELFT>::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 <class ELFT> 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<SymbolTableEntry> getSymbols() const { return symbols; }
@@ -670,6 +676,7 @@ protected:
 
   // A vector of symbols and their string table offsets.
   SmallVector<SymbolTableEntry, 0> 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 <class RelTy> 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 <class ELFT> void Writer<ELFT>:
   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<InputSection>(ctx.in.mipsGot.get())
                            : cast<InputSection>(ctx.in.got.get());
+    }
     ctx.sym.globalOffsetTable->section = sec;
   }
 
@@ -1544,9 +1548,9 @@ template <class ELFT> void Writer<ELFT>:
   };
   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 <class ELFT> void Writer<ELFT>:
       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 <algorithm>
+#include <cassert>
 
 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<std::pair<OutputSection *, unsigned>, 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<EhInputSection>(&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<EhInputSection>(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<int64_t>(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<uint64_t>(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<Register>(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<Register>(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 <cassert>
 #include <cstdint>
 #include <memory>
+#include <string>
 
 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 <algorithm>
++#include <cassert>
+ 
+ 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<std::pair<OutputSection *, unsigned>, 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<EhInputSection>(&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<EhInputSection>(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<int64_t>(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<uint64_t>(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<StringRef, 0> 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=<value>. 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=<value>. 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<uint64_t> getImageBase(Ctx &ctx, opt::InputArgList &args) {
    // Because we are using `ctx.arg.maxPageSize` here, this function has to be
-@@ -3435,6 +3490,11 @@ template <class ELFT> void LinkerDriver::link(opt::Inp
+@@ -3435,6 +3491,11 @@ template <class ELFT> 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 <class ELFT> void SharedFile::parse() {
+@@ -1192,7 +1213,20 @@ InputSectionBase *ObjFile<ELFT>::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 <class ELFT>
++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 <class ELFT>
+ void ObjFile<ELFT>::initializeSymbols(const object::ELFFile<ELFT> &obj) {
+   ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
+   if (!symbols)
+@@ -1200,14 +1234,25 @@ void ObjFile<ELFT>::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<ELFT>(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<unsigned, 32> undefineds;
+   for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) {
+     const Elf_Sym &eSym = eSyms[i];
++    if (isSparcRegister<ELFT>(ctx, eSym))
++      continue;
+     uint32_t secIdx = eSym.st_shndx;
+     if (secIdx == SHN_UNDEF) {
+       undefineds.push_back(i);
+@@ -1307,6 +1352,8 @@ template <class ELFT> void ObjFile<ELFT>::postParse() 
+   ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
+   for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) {
+     const Elf_Sym &eSym = eSyms[i];
++    if (isSparcRegister<ELFT>(ctx, eSym))
++      continue;
+     Symbol &sym = *symbols[i];
+     uint32_t secIdx = eSym.st_shndx;
+     uint8_t binding = eSym.getBinding();
+@@ -1565,6 +1612,9 @@ template <class ELFT> void SharedFile::parse() {
    const ELFFile<ELFT> obj = this->getObj<ELFT>();
    ArrayRef<Elf_Shdr> sections = getELFShdrs<ELFT>();
  
@@ -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 <class ELFT> void SharedFile::parse() {
+@@ -1588,7 +1638,14 @@ template <class ELFT> void SharedFile::parse() {
      case SHT_GNU_verneed:
        verneedSec = &sec;
        break;
@@ -63,7 +122,18 @@ Index: lld/ELF/InputFiles.cpp
 +          CHECK2(obj.template getSectionContentsAsArray<char>(sec), this);
 +      parseGNUWarning(ctx, name, data, sec.sh_size);
 +      break;
-+    }
      }
++    }
    }
  
+   if (versymSec && numSymbols == 0) {
+@@ -1659,6 +1716,9 @@ template <class ELFT> void SharedFile::parse() {
+   ArrayRef<Elf_Sym> syms = this->getGlobalELFSyms<ELFT>();
+   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<ELFT>(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<R_PC, R_GOTREL, R_GOTPLTREL, RE_ARM_PCA, RE_MIPS_GOTREL,
+                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 <class PltSection, class GotPltSection>
+ 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<EhInputSection>(sec) && ctx.arg.emachine != EM_MIPS));
+   if (canWrite) {
+     RelType rel = ctx.target->getDynRel(type);
+-    if (oneof<R_GOT, RE_LOONGARCH_GOT>(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<R_GOT, RE_LOONGARCH_GOT>(expr) || useRelative) {
+       addRelativeReloc<true>(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<OutputSection *> 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<OutputSection *> outputSections) {
+   bool needTlsSymbol = false;
+   forEachInputSectionDescription(
+       outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
+@@ -2221,7 +2268,7 @@ bool elf::hexagonNeedsTLSSymbol(ArrayRef<OutputSection
+   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<OutputSection *> 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<OutputSection *> outputSections);
++void tlsSymbolUpdate(Ctx &ctx);
++bool needsTLSSymbol(ArrayRef<OutputSection *> 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 <typename T> struct AssertSymbol {
    static_assert(std::is_trivially_destructible<T>(),
+@@ -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<ELFT>::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<S
+   // 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 <class ELFT> 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 <class ELFT> 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 <class ELFT> void elf::createSyntheticSection
      ctx.in.gnuProperty = std::make_unique<GnuPropertySection>(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<SymbolTableEntry> getSymbols() const { return symbols; }
+@@ -670,6 +676,7 @@ class SymbolTableBaseSection : public SyntheticSection
+ 
+   // A vector of symbols and their string table offsets.
+   SmallVector<SymbolTableEntry, 0> 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 <class RelTy> 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 <class ELFT> void Writer<ELFT>::setReservedSy
+@@ -855,10 +863,14 @@ template <class ELFT> void Writer<ELFT>::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<InputSection>(ctx.in.mipsGot.get())
+                            : cast<InputSection>(ctx.in.got.get());
++    }
+     ctx.sym.globalOffsetTable->section = sec;
+   }
+ 
+@@ -927,6 +939,9 @@ template <class ELFT> void Writer<ELFT>::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<ELFT>::createPhdrs(Partition &part) {
+@@ -1533,9 +1548,9 @@ template <class ELFT> void Writer<ELFT>::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 <class ELFT> void Writer<ELFT>::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<ELFT>::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<ELFT>::createPhdrs(Partition &part) {
+@@ -2493,7 +2505,19 @@ Writer<ELFT>::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 <class ELFT> void Writer<ELFT>::fixSectionAli
+@@ -2586,6 +2610,31 @@ template <class ELFT> void Writer<ELFT>::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 <cassert>
+ #include <cstdint>
+ #include <memory>
++#include <string>
+ 
+ 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<Register>(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<Register>(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");`),