From: Kirill A. Korinsky Subject: OpenBSD/spac64 by LLVM toolchain To: OpenBSD tech Date: Sun, 05 Jul 2026 11:56:06 +0200 tech@, Here a diff which allows me to build OpenBSD/spac64 by llvm toolchain. My test cycle from clean snapshot was: 1. Rebuild clang / lld cd /usr/src/gnu/usr.bin/clang doas make clean doas -u build make CC=clang CXX=clang++ -j8 doas make install 2. Rebuild clang / lld as system compiler cd /usr/src/share/mk doas make install cd /usr/src/gnu/usr.bin/clang doas make clean doas -u build make CC=clang CXX=clang++ LD=ld.lld -j8 doas make install 3. Rebuild the system itself cd /usr/src doas make build -j8 doas make install 4. Rebuild kernel cd /usr/src/sys/arch/sparc64/compile/GENERIC.MP doas make clean doas -u build make config doas -u make doas make install It boots and seems to works, but I haven't tried to build ports. The medium size diff which is based on https://github.com/llvm/llvm-project/pull/137919 Index: gnu/llvm/lld/ELF/Driver.cpp =================================================================== RCS file: /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 1 Jul 2026 12:05:26 -0000 @@ -1316,9 +1316,10 @@ static SmallVector getSymb static bool getIsRela(Ctx &ctx, opt::InputArgList &args) { // The psABI specifies the default relocation entry format. - bool rela = is_contained({EM_AARCH64, EM_AMDGPU, EM_HEXAGON, EM_LOONGARCH, - EM_PPC, EM_PPC64, EM_RISCV, EM_S390, EM_X86_64}, - ctx.arg.emachine); + bool rela = + is_contained({EM_AARCH64, EM_AMDGPU, EM_HEXAGON, EM_LOONGARCH, EM_PPC, + EM_PPC64, EM_RISCV, EM_S390, EM_SPARCV9, EM_X86_64}, + ctx.arg.emachine); // If -z rel or -z rela is specified, use the last option. for (auto *arg : args.filtered(OPT_z)) { StringRef s(arg->getValue()); Index: gnu/llvm/lld/ELF/InputSection.cpp =================================================================== RCS file: /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 1 Jul 2026 12:05:26 -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: /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 1 Jul 2026 12:05:26 -0000 @@ -770,14 +770,18 @@ static void addRelativeReloc(Ctx &ctx, I template static void addPltEntry(Ctx &ctx, PltSection &plt, GotPltSection &gotPlt, RelocationBaseSection &rel, RelType type, Symbol &sym) { + RelExpr expr = sym.isPreemptible ? R_ADDEND : R_ABS; plt.addEntry(sym); - gotPlt.addEntry(sym); - if (sym.isPreemptible) - rel.addReloc( - {type, &gotPlt, sym.getGotPltOffset(ctx), true, sym, 0, R_ADDEND}); - else + if (ctx.target->usesGotPlt) { + gotPlt.addEntry(sym); + // The relocation is applied to the .got.plt entry. + rel.addReloc({type, &gotPlt, sym.getGotPltOffset(ctx), !!sym.isPreemptible, + sym, 0, expr}); + } else { + // The relocation is applied to the .plt entry. rel.addReloc( - {type, &gotPlt, sym.getGotPltOffset(ctx), false, sym, 0, R_ABS}); + {type, &plt, sym.getPltOffset(ctx), !!sym.isPreemptible, sym, 0, expr}); + } } void elf::addGotEntry(Ctx &ctx, Symbol &sym) { @@ -945,25 +949,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); } } @@ -1236,12 +1247,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; @@ -2233,11 +2245,11 @@ bool ThunkCreator::createThunks(uint32_t return addressesChanged; } -// The following aid in the conversion of call x@GDPLT to call __tls_get_addr -// hexagonNeedsTLSSymbol scans for relocations would require a call to -// __tls_get_addr. -// hexagonTLSSymbolUpdate rebinds the relocation to __tls_get_addr. -bool elf::hexagonNeedsTLSSymbol(ArrayRef outputSections) { +// The following aid in the conversion of call x@GDPLT (Hexagon) and +// call x@TLSPLT (SPARC) to call __tls_get_addr. needsTLSSymbol scans +// for relocations that would require a call to __tls_get_addr. +// tlsSymbolUpdate rebinds the relocation to __tls_get_addr. +bool elf::needsTLSSymbol(ArrayRef outputSections) { bool needTlsSymbol = false; forEachInputSectionDescription( outputSections, [&](OutputSection *os, InputSectionDescription *isd) { @@ -2251,7 +2263,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: /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 1 Jul 2026 12:05:26 -0000 @@ -61,6 +61,7 @@ enum RelExpr { R_PLT_GOTPLT, R_PLT_GOTREL, R_RELAX_HINT, + R_RELAX_GOT_OFF, R_RELAX_GOT_PC, R_RELAX_GOT_PC_NOPIC, R_RELAX_TLS_GD_TO_IE, @@ -171,8 +172,8 @@ bool maybeReportUndefined(Ctx &, Undefin void postScanRelocations(Ctx &ctx); void addGotEntry(Ctx &ctx, Symbol &sym); -void hexagonTLSSymbolUpdate(Ctx &ctx); -bool hexagonNeedsTLSSymbol(ArrayRef outputSections); +void tlsSymbolUpdate(Ctx &ctx); +bool needsTLSSymbol(ArrayRef outputSections); bool isAbsolute(const Symbol &sym); Index: gnu/llvm/lld/ELF/Symbols.cpp =================================================================== RCS file: /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 1 Jul 2026 12:05:26 -0000 @@ -173,6 +173,12 @@ 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.in.plt->headerSize + getPltIdx(ctx) * ctx.target->pltEntrySize; +} + uint64_t Symbol::getPltVA(Ctx &ctx) const { uint64_t outVA = isInIplt ? ctx.in.iplt->getVA() + getPltIdx(ctx) * ctx.target->ipltEntrySize Index: gnu/llvm/lld/ELF/Symbols.h =================================================================== RCS file: /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 1 Jul 2026 12:05:26 -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: /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 1 Jul 2026 12:05:26 -0000 @@ -1505,6 +1505,7 @@ DynamicSection::computeContents() addInt(DT_RISCV_VARIANT_CC, 0); [[fallthrough]]; default: + assert(ctx.target->usesGotPlt); addInSec(DT_PLTGOT, *ctx.in.gotPlt); break; } @@ -1720,14 +1721,31 @@ 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(); + if (ctx.arg.emachine == EM_SPARCV9 && type == R_SPARC_UA64 && + needsDynSymIndex() && !sym->isPreemptible) { + if (r_offset % 8 != 0) { + Err(ctx) << "R_SPARC_UA64 relocation at offset " << r_offset + << " against non-preemptible symbol " << sym + << " is not 8-byte aligned"; + } else { + type = ctx.target->relativeRel; + isAgainstSymbol = false; + expr = R_ABS; + } + } r_sym = getSymIndex(symt); addend = computeAddend(ctx); isFinal = true; // Catch errors @@ -2609,8 +2627,10 @@ 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) { @@ -4882,10 +4902,12 @@ template void elf::createSy // _GLOBAL_OFFSET_TABLE_ is defined relative to either .got.plt or .got. Treat // it as a relocation and ensure the referenced section is created. if (ctx.sym.globalOffsetTable && ctx.arg.emachine != EM_MIPS) { - if (ctx.target->gotBaseSymInGotPlt) + if (ctx.target->gotBaseSymInGotPlt) { + assert(ctx.target->usesGotPlt); ctx.in.gotPlt->hasGotPltOffRel = true; - else + } else { ctx.in.got->hasGotOffRel = true; + } } // We always need to add rel[a].plt to output if it has entries. Index: gnu/llvm/lld/ELF/Target.cpp =================================================================== RCS file: /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 1 Jul 2026 12:05:26 -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: /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 1 Jul 2026 12:05:26 -0000 @@ -135,7 +135,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 +163,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 +189,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: /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 1 Jul 2026 12:05:26 -0000 @@ -625,7 +625,7 @@ static bool isRelroSection(Ctx &ctx, con // by default resolved lazily, so we usually cannot put it into RELRO. // However, if "-z now" is given, the lazy symbol resolution is // disabled, which enables us to put it into RELRO. - if (sec == ctx.in.gotPlt->getParent()) + if (ctx.target->usesGotPlt && sec == ctx.in.gotPlt->getParent()) #ifndef __OpenBSD__ return ctx.arg.zNow; #else @@ -863,10 +863,14 @@ template void Writer: if (ctx.sym.globalOffsetTable) { // The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention usually // to the start of the .got or .got.plt section. - InputSection *sec = ctx.in.gotPlt.get(); - if (!ctx.target->gotBaseSymInGotPlt) + InputSection *sec; + if (ctx.target->gotBaseSymInGotPlt) { + assert(ctx.target->usesGotPlt); + sec = ctx.in.gotPlt.get(); + } else { sec = ctx.in.mipsGot ? cast(ctx.in.mipsGot.get()) : cast(ctx.in.got.get()); + } ctx.sym.globalOffsetTable->section = sec; } @@ -1544,9 +1548,9 @@ template void Writer: }; finalizeOrderDependentContent(); - // Converts call x@GDPLT to call __tls_get_addr - if (ctx.arg.emachine == EM_HEXAGON) - hexagonTLSSymbolUpdate(ctx); + // Converts call x@GDPLT/x@TLSPLT to call __tls_get_addr. + if (ctx.arg.emachine == EM_HEXAGON || ctx.arg.emachine == EM_SPARCV9) + tlsSymbolUpdate(ctx); if (ctx.arg.randomizeSectionPadding) randomizeSectionPadding(ctx); @@ -2042,10 +2046,10 @@ template void Writer: sec->addrExpr = [=] { return i->second; }; } - // With the ctx.outputSections available check for GDPLT relocations + // With the ctx.outputSections available check for GDPLT/TLSPLT relocations // and add __tls_get_addr symbol if needed. - if (ctx.arg.emachine == EM_HEXAGON && - hexagonNeedsTLSSymbol(ctx.outputSections)) { + if ((ctx.arg.emachine == EM_HEXAGON || ctx.arg.emachine == EM_SPARCV9) && + needsTLSSymbol(ctx.outputSections)) { Symbol *sym = ctx.symtab->addSymbol(Undefined{ctx.internalFile, "__tls_get_addr", STB_GLOBAL, STV_DEFAULT, STT_NOTYPE}); @@ -2311,6 +2315,10 @@ 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__ + 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: /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 1 Jul 2026 12:05:26 -0000 @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "OutputSections.h" #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" @@ -23,10 +24,17 @@ public: SPARCV9(Ctx &); RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; + RelType getDynRel(RelType type) const override; + void writeGotHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) 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: + void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const; }; } // namespace @@ -35,9 +43,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 +62,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 +137,148 @@ RelExpr SPARCV9::getRelExpr(RelType type } } +RelType SPARCV9::getDynRel(RelType type) const { + if (type == symbolicRel || type == R_SPARC_UA64) + return type; + return R_SPARC_NONE; +} + 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,19 +287,89 @@ 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"); } +} + +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) + write32(ctx, buf, ctx.mainPart->dynamic->getVA()); } void SPARCV9::writePlt(uint8_t *buf, const Symbol & /*sym*/, Index: share/mk/bsd.own.mk =================================================================== RCS file: /cvs/src/share/mk/bsd.own.mk,v diff -u -p -r1.216 bsd.own.mk --- share/mk/bsd.own.mk 17 Nov 2025 16:06:09 -0000 1.216 +++ share/mk/bsd.own.mk 1 Jul 2026 12:05:26 -0000 @@ -16,8 +16,8 @@ SKEY?= yes YP?= yes CLANG_ARCH=aarch64 amd64 arm i386 mips64 mips64el powerpc powerpc64 riscv64 sparc64 -GCC4_ARCH=alpha hppa m88k sh sparc64 -LLD_ARCH=aarch64 amd64 arm i386 powerpc powerpc64 riscv64 +GCC4_ARCH=alpha hppa m88k sh +LLD_ARCH=aarch64 amd64 arm i386 powerpc powerpc64 riscv64 sparc64 LLDB_ARCH=aarch64 amd64 # Can't use ${CLANG_ARCH} ${GCC4_ARCH} below because of sparc64 Index: sys/arch/sparc64/conf/Makefile.sparc64 =================================================================== RCS file: /cvs/src/sys/arch/sparc64/conf/Makefile.sparc64,v diff -u -p -r1.113 Makefile.sparc64 --- sys/arch/sparc64/conf/Makefile.sparc64 5 May 2025 20:43:32 -0000 1.113 +++ sys/arch/sparc64/conf/Makefile.sparc64 1 Jul 2026 12:05:26 -0000 @@ -122,12 +122,16 @@ ioconf.o: ioconf.c ld.script: ${_machdir}/conf/ld.script cp ${_machdir}/conf/ld.script $@ +gapdummy.o: + echo '__asm(".section .rodata,\"a\"");' > gapdummy.c + ${CC} -c ${CFLAGS} ${CPPFLAGS} gapdummy.c -o $@ + makegap.sh: cp $S/conf/makegap.sh $@ -MAKE_GAP = LD="${LD}" sh makegap.sh 0x00000000 +MAKE_GAP = LD="${LD}" sh makegap.sh 0x00000000 gapdummy.o -gap.o: Makefile makegap.sh vers.o +gap.o: Makefile makegap.sh gapdummy.o vers.o ${MAKE_GAP} vers.o: ${SYSTEM_DEP:Ngap.o} @@ -136,7 +140,7 @@ vers.o: ${SYSTEM_DEP:Ngap.o} clean: rm -f *bsd *bsd.gdb *.[dio] [a-z]*.s assym.* \ - gap.link ld.script lorder makegap.sh param.c + gap.link gapdummy.c ld.script lorder makegap.sh param.c cleandir: clean rm -f Makefile *.h ioconf.c options machine ${_mach} vers.c Index: sys/arch/sparc64/stand/bootblk/genassym.sh =================================================================== RCS file: /cvs/src/sys/arch/sparc64/stand/bootblk/genassym.sh,v diff -u -p -r1.4 genassym.sh --- sys/arch/sparc64/stand/bootblk/genassym.sh 2 Apr 2020 06:06:22 -0000 1.4 +++ sys/arch/sparc64/stand/bootblk/genassym.sh 1 Jul 2026 12:05:26 -0000 @@ -164,9 +164,9 @@ $0 ~ /^endif/ { printf("printf(\"#define " $2 " %%ld\\n\", (%s)" value ");\n", type); else if (fcode) { if (doing_member) - printf("__asm(\"XYZZY : %s d# %%%s0 + ;\" : : \"%s\" (%s));\n", $2, asmprint, asmtype, value); + printf("__asm(\".ascii \\\"XYZZY : %s d# %%%s0 + ;\\\"\" : : \"%s\" (%s));\n", $2, asmprint, asmtype, value); else - printf("__asm(\"XYZZY d# %%%s0 constant %s\" : : \"%s\" (%s));\n", asmprint, $2, asmtype, value); + printf("__asm(\".ascii \\\"XYZZY d# %%%s0 constant %s\\\"\" : : \"%s\" (%s));\n", asmprint, $2, asmtype, value); } else printf("__asm(\"XYZZY %s %%%s0\" : : \"%s\" (%s));\n", $2, asmprint, asmtype, value); next; @@ -204,7 +204,7 @@ elif [ "$fcode" = 1 ]; then # Kill all of the "#" and "$" modifiers; locore.s already # prepends the correct "constant" modifier. "$@" -S "${genassym_temp}/assym.c" -o - | sed -e 's/\$//g' | \ - sed -n 's/.*XYZZY//gp' + sed -n 's/.*\.ascii[[:space:]]*"XYZZY\([^"]*\)".*/\1/p' else # Kill all of the "#" and "$" modifiers; locore.s already # prepends the correct "constant" modifier. -- wbr, Kirill