Download raw body.
LLVM toolchain for sparc64
fine by me
On 30/07/26 13:38 +0200, Kirill A. Korinsky wrote:
> Folks,
>
> I'd like to commit to devel/llvm/22 and gnu/llvm my cumulative patch for
> support sparc64 by LLVM toolchain.
>
> Not OpenBSD related bits was upstreamed as a few PRs and some parts are
> actually already merged.
>
> More or less this diff was tested by me and tb@ who runs his bulk during
> which many small edge cases was discovered and fixed.
>
> Here two diffs for the base and ports, I did my best to keep in
> synchronized.
>
> I haven't backported that changes to LLVM before 22, but we already mainly
> switched to LLVM 22 everywhere.
>
> I think it may have some missed pieces, but working in the tree will make
> testing it simpler.
>
> Ok to commit?
>
> --
> wbr, Kirill
> Index: gnu/llvm/lld/ELF/Driver.cpp
> ===================================================================
> RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Driver.cpp,v
> diff -u -p -r1.24 Driver.cpp
> --- gnu/llvm/lld/ELF/Driver.cpp 29 May 2026 11:06:20 -0000 1.24
> +++ gnu/llvm/lld/ELF/Driver.cpp 30 Jul 2026 10:58:21 -0000
> @@ -1316,9 +1316,10 @@ static SmallVector<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");`),
--
Regards,
Robert Nagy
LLVM toolchain for sparc64