Download raw body.
LLVM toolchain for sparc64
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");`),
LLVM toolchain for sparc64