From: Kirill A. Korinsky Subject: lld: add MIPS lazy binding stubs To: OpenBSD tech Cc: Sebastien Marie , Robert Nagy Date: Sun, 01 Nov 2026 23:42:39 +0100 tech@, here a diff for base llvm and both 22 and 23 in ports to implement lazy binding in LLD for mips64 which is based on visa@'s sketch diff. regress/libexec/ld.so/lazy is passed, and visa@ tested it on his device. This diff was backported to uptream as https://github.com/llvm/llvm-project/pull/237790 Ok? Index: src/gnu/llvm/lld/ELF/Config.h =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Config.h,v diff -u -p -r1.7 Config.h --- src/gnu/llvm/lld/ELF/Config.h 29 May 2026 11:06:20 -0000 1.7 +++ src/gnu/llvm/lld/ELF/Config.h 1 Nov 2026 20:37:58 -0000 @@ -65,6 +65,7 @@ class InputSection; class IpltSection; class MipsGotSection; class MipsRldMapSection; +class MipsStubsSection; class PPC32Got2Section; class PPC64LongBranchTargetSection; class PltSection; @@ -592,6 +593,7 @@ struct InStruct { std::unique_ptr mipsOptions; std::unique_ptr mipsReginfo; std::unique_ptr mipsRldMap; + std::unique_ptr mipsStubs; std::unique_ptr partEnd; std::unique_ptr partIndex; std::unique_ptr plt; Index: src/gnu/llvm/lld/ELF/Relocations.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Relocations.cpp,v diff -u -p -r1.12 Relocations.cpp --- src/gnu/llvm/lld/ELF/Relocations.cpp 30 Jul 2026 19:28:37 -0000 1.12 +++ src/gnu/llvm/lld/ELF/Relocations.cpp 1 Nov 2026 20:37:58 -0000 @@ -1442,6 +1442,8 @@ void RelocScan::scanEhSection(EhInputSec if (sym->isUndefined() && maybeReportUndefined(cast(*sym), offset)) continue; + if (ctx.arg.emachine == EM_MIPS) + sym->setFlags(NO_MIPS_STUB); process(r.expr, r.type, offset, *sym, r.addend); } } Index: src/gnu/llvm/lld/ELF/Symbols.h =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Symbols.h,v diff -u -p -r1.9 Symbols.h --- src/gnu/llvm/lld/ELF/Symbols.h 30 Jul 2026 19:28:37 -0000 1.9 +++ src/gnu/llvm/lld/ELF/Symbols.h 1 Nov 2026 20:37:58 -0000 @@ -55,6 +55,8 @@ enum { NEEDS_GOT_NONAUTH = 1 << 10, NEEDS_TLSDESC_AUTH = 1 << 11, NEEDS_TLSDESC_NONAUTH = 1 << 12, + HAS_MIPS_CALL_RELOC = 1 << 13, + NO_MIPS_STUB = 1 << 14, }; // The base class for real symbol classes. Index: src/gnu/llvm/lld/ELF/SyntheticSections.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/SyntheticSections.cpp,v diff -u -p -r1.11 SyntheticSections.cpp --- src/gnu/llvm/lld/ELF/SyntheticSections.cpp 30 Jul 2026 19:28:37 -0000 1.11 +++ src/gnu/llvm/lld/ELF/SyntheticSections.cpp 1 Nov 2026 20:37:58 -0000 @@ -1095,8 +1095,10 @@ void MipsGotSection::build() { if (&got == primGot) continue; - // Dynamic relocations for "global" entries. + // A lazy stub requires the primary GOT's resolver slots and $gp. + // Calls through secondary GOTs must be resolved eagerly. for (const std::pair &p : got.global) { + p.first->setFlags(NO_MIPS_STUB); uint64_t offset = p.second * ctx.arg.wordsize; ctx.mainPart->relaDyn->addSymbolReloc(ctx.target->relativeRel, *this, offset, *p.first); @@ -1177,13 +1179,21 @@ void MipsGotSection::writeTo(uint8_t *bu // http://web.archive.org/web/20190324223224/https://www.linux-mips.org/wiki/NPTL for (const std::pair &p : g.local16) write(p.second, p.first.first, p.first.second); + auto writeGlobal = [&](size_t i, const Symbol *s) { + if (std::optional va = + ctx.in.mipsStubs ? ctx.in.mipsStubs->getStubVA(*s) + : std::nullopt) + write(i, nullptr, *va); + else + write(i, s, 0); + }; // Write VA to the primary GOT only. For secondary GOTs that // will be done by REL32 dynamic relocations. if (&g == &gots.front()) for (const std::pair &p : g.global) - write(p.second, p.first, 0); + writeGlobal(p.second, p.first); for (const std::pair &p : g.relocs) - write(p.second, p.first, 0); + writeGlobal(p.second, p.first); for (const std::pair &p : g.tls) write(p.second, p.first, p.first->isPreemptible || ctx.arg.shared ? 0 : -0x7000); @@ -1202,6 +1212,54 @@ void MipsGotSection::writeTo(uint8_t *bu } } +MipsStubsSection::MipsStubsSection(Ctx &ctx) + : SyntheticSection(ctx, ".MIPS.stubs", SHT_PROGBITS, + SHF_ALLOC | SHF_EXECINSTR, ctx.arg.wordsize) {} + +void MipsStubsSection::addSymbols() { + // microMIPS stubs are not implemented. + if (isMicroMips(ctx)) + return; + for (Symbol *sym : ctx.symtab->getSymbols()) { + if (!sym->isPreemptible || !(sym->isUndefined() || sym->isShared()) || + !sym->isFunc() || !sym->hasFlag(HAS_MIPS_CALL_RELOC) || + sym->hasFlag(NO_MIPS_STUB) || sym->hasFlag(NEEDS_PLT)) + continue; + entries.push_back(sym); + } + + // Include the full stub size in GOT page counts before GOT layout. + // Removing secondary GOT candidates later can only shrink this size. + stubSize = ctx.mainPart->dynSymTab->getNumSymbols() > 0x10000 + ? stubBigSize + : stubNormalSize; +} + +std::optional +MipsStubsSection::getStubVA(const Symbol &sym) const { + if (!getParent()) + return std::nullopt; + auto it = indices.find(&sym); + if (it == indices.end()) + return std::nullopt; + return getVA() + it->second * stubSize; +} + +void MipsStubsSection::finalizeContents() { + llvm::erase_if(entries, [&](const Symbol *sym) { + return sym->hasFlag(NO_MIPS_STUB) || !sym->isInGot(ctx); + }); + for (size_t i = 0; i != entries.size(); ++i) + indices[entries[i]] = i; +} + +void MipsStubsSection::writeTo(uint8_t *buf) { + for (const Symbol *sym : entries) { + writeMipsStub(ctx, buf, sym->dynsymIndex, stubSize == stubBigSize); + buf += stubSize; + } +} + // On PowerPC the .plt section is used to hold the table of function addresses // instead of the .got.plt, and the type is SHT_NOBITS similar to a .bss // section. I don't know why we have a BSS style type for the section but it is @@ -2351,6 +2409,9 @@ template void SymbolTableSe Symbol *sym = ent.sym; if (sym->isInPlt(ctx) && sym->hasFlag(NEEDS_COPY)) eSym->st_other |= STO_MIPS_PLT; + if (type == SHT_DYNSYM && ctx.in.mipsStubs) + if (std::optional va = ctx.in.mipsStubs->getStubVA(*sym)) + eSym->st_value = *va; if (isMicroMips(ctx)) { // We already set the less-significant bit for symbols // marked by the `STO_MIPS_MICROMIPS` flag and for microMIPS PLT @@ -4926,6 +4987,11 @@ template void elf::createSy add(*ctx.in.plt); ctx.in.iplt = std::make_unique(ctx); add(*ctx.in.iplt); + + if (ctx.arg.emachine == EM_MIPS && !ctx.arg.relocatable && ctx.hasDynsym) { + ctx.in.mipsStubs = std::make_unique(ctx); + add(*ctx.in.mipsStubs); + } if (ctx.arg.andFeatures || ctx.aarch64PauthAbiCoreInfo) { ctx.in.gnuProperty = std::make_unique(ctx); Index: src/gnu/llvm/lld/ELF/SyntheticSections.h =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/SyntheticSections.h,v diff -u -p -r1.6 SyntheticSections.h --- src/gnu/llvm/lld/ELF/SyntheticSections.h 30 Jul 2026 19:28:37 -0000 1.6 +++ src/gnu/llvm/lld/ELF/SyntheticSections.h 1 Nov 2026 20:37:58 -0000 @@ -1202,6 +1202,24 @@ public: void writeTo(uint8_t *buf) override {} }; +class MipsStubsSection final : public SyntheticSection { + static constexpr size_t stubNormalSize = 16; + static constexpr size_t stubBigSize = 20; +public: + MipsStubsSection(Ctx &); + void addSymbols(); + std::optional getStubVA(const Symbol &sym) const; + size_t getSize() const override { return entries.size() * stubSize; } + bool isNeeded() const override { return !entries.empty(); } + void finalizeContents() override; + void writeTo(uint8_t *buf) override; + +private: + SmallVector entries; + llvm::DenseMap indices; + size_t stubSize = stubNormalSize; +}; + // Representation of the combined .ARM.Exidx input sections. We process these // as a SyntheticSection like .eh_frame as we need to merge duplicate entries // and add terminating sentinel entries. Index: src/gnu/llvm/lld/ELF/Target.h =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Target.h,v diff -u -p -r1.2 Target.h --- src/gnu/llvm/lld/ELF/Target.h 30 Jul 2026 19:28:37 -0000 1.2 +++ src/gnu/llvm/lld/ELF/Target.h 1 Nov 2026 20:37:58 -0000 @@ -247,6 +247,7 @@ uint64_t getMipsPageAddr(uint64_t addr); bool isMipsN32Abi(Ctx &, const InputFile &f); bool isMicroMips(Ctx &); bool isMipsR6(Ctx &); +void writeMipsStub(Ctx &, uint8_t *buf, uint32_t dynsymIndex, bool big); void writePPC32GlinkSection(Ctx &, uint8_t *buf, size_t numEntries); Index: src/gnu/llvm/lld/ELF/Writer.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Writer.cpp,v diff -u -p -r1.12 Writer.cpp --- src/gnu/llvm/lld/ELF/Writer.cpp 20 Aug 2026 14:39:18 -0000 1.12 +++ src/gnu/llvm/lld/ELF/Writer.cpp 1 Nov 2026 20:37:58 -0000 @@ -2016,8 +2016,11 @@ template void Writer: } } + if (ctx.in.mipsStubs) + ctx.in.mipsStubs->addSymbols(); if (ctx.in.mipsGot) ctx.in.mipsGot->build(); + finalizeSynthetic(ctx, ctx.in.mipsStubs.get()); removeUnusedSyntheticSections(ctx); ctx.script->diagnoseOrphanHandling(); Index: src/gnu/llvm/lld/ELF/Arch/Mips.cpp =================================================================== RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Arch/Mips.cpp,v diff -u -p -r1.1.1.6 Mips.cpp --- src/gnu/llvm/lld/ELF/Arch/Mips.cpp 29 May 2026 11:05:55 -0000 1.1.1.6 +++ src/gnu/llvm/lld/ELF/Arch/Mips.cpp 1 Nov 2026 20:37:58 -0000 @@ -48,6 +48,41 @@ uint64_t elf::getMipsPageAddr(uint64_t a return (addr + 0x8000) & ~0xffff; } +void elf::writeMipsStub(Ctx &ctx, uint8_t *buf, uint32_t dynsymIndex, + bool big) { + if (dynsymIndex > 0x7fffffff) { + ErrAlways(ctx) << "MIPS lazy-binding stub dynamic symbol index " + << dynsymIndex << " exceeds 31 bits"; + return; + } + + const uint32_t load = ctx.arg.is64 ? 0xdf998010 // ld $25, -0x7ff0($28) + : 0x8f998010; // lw $25, -0x7ff0($28) + const uint32_t lui = 0x3c180000 | ((dynsymIndex >> 16) & 0x7fff); + const uint32_t jalr = ctx.arg.zHazardplt ? 0x0320fc09 : 0x0320f809; + uint32_t loadIndex; + if (big) + loadIndex = 0x37180000 | (dynsymIndex & 0xffff); // ori $24, $24, idx + else if (dynsymIndex & ~0x7fff) + loadIndex = 0x34180000 | (dynsymIndex & 0xffff); // ori $24, $0, idx + else + loadIndex = (ctx.arg.is64 ? 0x64180000 : 0x24180000) | // [d]addiu $24, + dynsymIndex; // $0, idx + + if (big) { + write32(ctx, buf, load); + write32(ctx, buf + 4, 0x03e07825); // move $15, $31 + write32(ctx, buf + 8, lui); + write32(ctx, buf + 12, jalr); // jalr[.hb] $25 + write32(ctx, buf + 16, loadIndex); // delay slot + } else { + write32(ctx, buf, load); + write32(ctx, buf + 4, 0x03e07825); // move $15, $31 + write32(ctx, buf + 8, jalr); // jalr[.hb] $25 + write32(ctx, buf + 12, loadIndex); // delay slot + } +} + template MIPS::MIPS(Ctx &ctx) : TargetInfo(ctx) { gotPltHeaderEntriesNum = 2; defaultMaxPageSize = 65536; @@ -609,6 +644,13 @@ static RelType getMipsPairType(RelType t } } +static bool isCallReloc(RelType type) { + return type == R_MIPS_CALL16 || + type == R_MIPS_CALL_HI16 || type == R_MIPS_CALL_LO16 || + type == R_MICROMIPS_CALL16 || + type == R_MICROMIPS_CALL_HI16 || type == R_MICROMIPS_CALL_LO16; +} + template template void MIPS::scanSectionImpl(InputSectionBase &sec, Relocs rels) { @@ -639,6 +681,21 @@ void MIPS::scanSectionImpl(InputSe if (sym.isUndefined() && symIdx != 0 && rs.maybeReportUndefined(cast(sym), offset)) continue; + + // In a shared object, turn untyped undefined symbols with a call + // relocation into function symbols so that the dynamic linker handles + // them correctly. + if (ctx.arg.shared && sym.isUndefined() && symIdx != 0 && + sym.type == STT_NOTYPE && isCallReloc(type.v & 0xff)) + sym.type = STT_FUNC; + + if (symIdx != 0) { + uint32_t baseType = type.v & 0xff; + if (isCallReloc(baseType)) + sym.setFlags(HAS_MIPS_CALL_RELOC); + else if (baseType != R_MIPS_JALR && baseType != R_MICROMIPS_JALR) + sym.setFlags(NO_MIPS_STUB); + } auto addend = rs.getAddend(rel, type); if (expr == RE_MIPS_GOTREL && sym.isLocal()) { Index: ports/devel/llvm/22/Makefile =================================================================== RCS file: /home/cvs/ports/devel/llvm/22/Makefile,v diff -u -p -r1.28 Makefile --- ports/devel/llvm/22/Makefile 16 Sep 2026 01:03:57 -0000 1.28 +++ ports/devel/llvm/22/Makefile 1 Nov 2026 21:04:16 -0000 @@ -2,7 +2,7 @@ LLVM_MAJOR = 22 LLVM_VERSION = ${LLVM_MAJOR}.1.8 LLVM_PKGSPEC = >=22,<23 -REVISION = 10 +REVISION = 11 SHARED_LIBS += LLVM 0.0 \ LTO 0.0 \ Index: ports/devel/llvm/22/patches/patch-lld_ELF_Arch_Mips_cpp =================================================================== RCS file: ports/devel/llvm/22/patches/patch-lld_ELF_Arch_Mips_cpp diff -N ports/devel/llvm/22/patches/patch-lld_ELF_Arch_Mips_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ports/devel/llvm/22/patches/patch-lld_ELF_Arch_Mips_cpp 1 Nov 2026 21:01:39 -0000 @@ -0,0 +1,81 @@ +Index: lld/ELF/Arch/Mips.cpp +--- lld/ELF/Arch/Mips.cpp.orig ++++ lld/ELF/Arch/Mips.cpp +@@ -48,6 +48,41 @@ uint64_t elf::getMipsPageAddr(uint64_t addr) { + return (addr + 0x8000) & ~0xffff; + } + ++void elf::writeMipsStub(Ctx &ctx, uint8_t *buf, uint32_t dynsymIndex, ++ bool big) { ++ if (dynsymIndex > 0x7fffffff) { ++ ErrAlways(ctx) << "MIPS lazy-binding stub dynamic symbol index " ++ << dynsymIndex << " exceeds 31 bits"; ++ return; ++ } ++ ++ const uint32_t load = ctx.arg.is64 ? 0xdf998010 // ld $25, -0x7ff0($28) ++ : 0x8f998010; // lw $25, -0x7ff0($28) ++ const uint32_t lui = 0x3c180000 | ((dynsymIndex >> 16) & 0x7fff); ++ const uint32_t jalr = ctx.arg.zHazardplt ? 0x0320fc09 : 0x0320f809; ++ uint32_t loadIndex; ++ if (big) ++ loadIndex = 0x37180000 | (dynsymIndex & 0xffff); // ori $24, $24, idx ++ else if (dynsymIndex & ~0x7fff) ++ loadIndex = 0x34180000 | (dynsymIndex & 0xffff); // ori $24, $0, idx ++ else ++ loadIndex = (ctx.arg.is64 ? 0x64180000 : 0x24180000) | // [d]addiu $24, ++ dynsymIndex; // $0, idx ++ ++ if (big) { ++ write32(ctx, buf, load); ++ write32(ctx, buf + 4, 0x03e07825); // move $15, $31 ++ write32(ctx, buf + 8, lui); ++ write32(ctx, buf + 12, jalr); // jalr[.hb] $25 ++ write32(ctx, buf + 16, loadIndex); // delay slot ++ } else { ++ write32(ctx, buf, load); ++ write32(ctx, buf + 4, 0x03e07825); // move $15, $31 ++ write32(ctx, buf + 8, jalr); // jalr[.hb] $25 ++ write32(ctx, buf + 12, loadIndex); // delay slot ++ } ++} ++ + template MIPS::MIPS(Ctx &ctx) : TargetInfo(ctx) { + gotPltHeaderEntriesNum = 2; + defaultMaxPageSize = 65536; +@@ -609,6 +644,13 @@ static RelType getMipsPairType(RelType type, bool isLo + } + } + ++static bool isCallReloc(RelType type) { ++ return type == R_MIPS_CALL16 || ++ type == R_MIPS_CALL_HI16 || type == R_MIPS_CALL_LO16 || ++ type == R_MICROMIPS_CALL16 || ++ type == R_MICROMIPS_CALL_HI16 || type == R_MICROMIPS_CALL_LO16; ++} ++ + template + template + void MIPS::scanSectionImpl(InputSectionBase &sec, Relocs rels) { +@@ -639,6 +681,21 @@ void MIPS::scanSectionImpl(InputSectionBase &sec + if (sym.isUndefined() && symIdx != 0 && + rs.maybeReportUndefined(cast(sym), offset)) + continue; ++ ++ // In a shared object, turn untyped undefined symbols with a call ++ // relocation into function symbols so that the dynamic linker handles ++ // them correctly. ++ if (ctx.arg.shared && sym.isUndefined() && symIdx != 0 && ++ sym.type == STT_NOTYPE && isCallReloc(type.v & 0xff)) ++ sym.type = STT_FUNC; ++ ++ if (symIdx != 0) { ++ uint32_t baseType = type.v & 0xff; ++ if (isCallReloc(baseType)) ++ sym.setFlags(HAS_MIPS_CALL_RELOC); ++ else if (baseType != R_MIPS_JALR && baseType != R_MICROMIPS_JALR) ++ sym.setFlags(NO_MIPS_STUB); ++ } + + auto addend = rs.getAddend(rel, type); + if (expr == RE_MIPS_GOTREL && sym.isLocal()) { Index: ports/devel/llvm/22/patches/patch-lld_ELF_Config_h =================================================================== RCS file: /home/cvs/ports/devel/llvm/22/patches/patch-lld_ELF_Config_h,v diff -u -p -r1.1.1.1 patch-lld_ELF_Config_h --- ports/devel/llvm/22/patches/patch-lld_ELF_Config_h 25 Feb 2026 13:31:42 -0000 1.1.1.1 +++ ports/devel/llvm/22/patches/patch-lld_ELF_Config_h 1 Nov 2026 21:01:39 -0000 @@ -1,7 +1,15 @@ Index: lld/ELF/Config.h --- lld/ELF/Config.h.orig +++ lld/ELF/Config.h -@@ -443,8 +443,13 @@ struct Config { +@@ -65,6 +65,7 @@ class InputSection; + class IpltSection; + class MipsGotSection; + class MipsRldMapSection; ++class MipsStubsSection; + class PPC32Got2Section; + class PPC64LongBranchTargetSection; + class PltSection; +@@ -443,8 +444,13 @@ struct Config { ELFKind ekind = ELFNoneKind; uint16_t emachine = llvm::ELF::EM_NONE; std::optional imageBase; @@ -15,7 +23,15 @@ Index: lld/ELF/Config.h uint64_t mipsGotSize; uint64_t zStackSize; unsigned ltoPartitions; -@@ -632,6 +637,9 @@ struct Ctx : CommonLinkerContext { +@@ -587,6 +593,7 @@ struct InStruct { + std::unique_ptr mipsOptions; + std::unique_ptr mipsReginfo; + std::unique_ptr mipsRldMap; ++ std::unique_ptr mipsStubs; + std::unique_ptr partEnd; + std::unique_ptr partIndex; + std::unique_ptr plt; +@@ -632,6 +639,9 @@ struct Ctx : CommonLinkerContext { struct ElfSym { // __bss_start Defined *bss; Index: ports/devel/llvm/22/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.3 patch-lld_ELF_Relocations_cpp --- ports/devel/llvm/22/patches/patch-lld_ELF_Relocations_cpp 30 Jul 2026 19:28:03 -0000 1.3 +++ ports/devel/llvm/22/patches/patch-lld_ELF_Relocations_cpp 1 Nov 2026 21:01:39 -0000 @@ -161,7 +161,16 @@ Index: lld/ELF/Relocations.cpp (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) { +@@ -1392,6 +1442,8 @@ void RelocScan::scanEhSection(EhInputSection &s) { + if (sym->isUndefined() && + maybeReportUndefined(cast(*sym), offset)) + continue; ++ if (ctx.arg.emachine == EM_MIPS) ++ sym->setFlags(NO_MIPS_STUB); + process(r.expr, r.type, offset, *sym, r.addend); + } + } +@@ -1555,10 +1607,7 @@ void elf::postScanRelocations(Ctx &ctx) { } else { assert(sym.isFunc() && sym.hasFlag(NEEDS_PLT)); if (!sym.isDefined()) { @@ -173,7 +182,7 @@ Index: lld/ELF/Relocations.cpp 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, +@@ -2203,11 +2252,11 @@ bool ThunkCreator::createThunks(uint32_t pass, return addressesChanged; } @@ -190,7 +199,7 @@ Index: lld/ELF/Relocations.cpp bool needTlsSymbol = false; forEachInputSectionDescription( outputSections, [&](OutputSection *os, InputSectionDescription *isd) { -@@ -2221,7 +2268,7 @@ bool elf::hexagonNeedsTLSSymbol(ArrayRef::computeContents() { +@@ -1095,8 +1095,10 @@ void MipsGotSection::build() { + if (&got == primGot) + continue; + +- // Dynamic relocations for "global" entries. ++ // A lazy stub requires the primary GOT's resolver slots and $gp. ++ // Calls through secondary GOTs must be resolved eagerly. + for (const std::pair &p : got.global) { ++ p.first->setFlags(NO_MIPS_STUB); + uint64_t offset = p.second * ctx.arg.wordsize; + ctx.mainPart->relaDyn->addSymbolReloc(ctx.target->relativeRel, *this, + offset, *p.first); +@@ -1177,13 +1179,21 @@ void MipsGotSection::writeTo(uint8_t *buf) { + // http://web.archive.org/web/20190324223224/https://www.linux-mips.org/wiki/NPTL + for (const std::pair &p : g.local16) + write(p.second, p.first.first, p.first.second); ++ auto writeGlobal = [&](size_t i, const Symbol *s) { ++ if (std::optional va = ++ ctx.in.mipsStubs ? ctx.in.mipsStubs->getStubVA(*s) ++ : std::nullopt) ++ write(i, nullptr, *va); ++ else ++ write(i, s, 0); ++ }; + // Write VA to the primary GOT only. For secondary GOTs that + // will be done by REL32 dynamic relocations. + if (&g == &gots.front()) + for (const std::pair &p : g.global) +- write(p.second, p.first, 0); ++ writeGlobal(p.second, p.first); + for (const std::pair &p : g.relocs) +- write(p.second, p.first, 0); ++ writeGlobal(p.second, p.first); + for (const std::pair &p : g.tls) + write(p.second, p.first, + p.first->isPreemptible || ctx.arg.shared ? 0 : -0x7000); +@@ -1202,6 +1212,54 @@ void MipsGotSection::writeTo(uint8_t *buf) { + } + } + ++MipsStubsSection::MipsStubsSection(Ctx &ctx) ++ : SyntheticSection(ctx, ".MIPS.stubs", SHT_PROGBITS, ++ SHF_ALLOC | SHF_EXECINSTR, ctx.arg.wordsize) {} ++ ++void MipsStubsSection::addSymbols() { ++ // microMIPS stubs are not implemented. ++ if (isMicroMips(ctx)) ++ return; ++ for (Symbol *sym : ctx.symtab->getSymbols()) { ++ if (!sym->isPreemptible || !(sym->isUndefined() || sym->isShared()) || ++ !sym->isFunc() || !sym->hasFlag(HAS_MIPS_CALL_RELOC) || ++ sym->hasFlag(NO_MIPS_STUB) || sym->hasFlag(NEEDS_PLT)) ++ continue; ++ entries.push_back(sym); ++ } ++ ++ // Include the full stub size in GOT page counts before GOT layout. ++ // Removing secondary GOT candidates later can only shrink this size. ++ stubSize = ctx.mainPart->dynSymTab->getNumSymbols() > 0x10000 ++ ? stubBigSize ++ : stubNormalSize; ++} ++ ++std::optional ++MipsStubsSection::getStubVA(const Symbol &sym) const { ++ if (!getParent()) ++ return std::nullopt; ++ auto it = indices.find(&sym); ++ if (it == indices.end()) ++ return std::nullopt; ++ return getVA() + it->second * stubSize; ++} ++ ++void MipsStubsSection::finalizeContents() { ++ llvm::erase_if(entries, [&](const Symbol *sym) { ++ return sym->hasFlag(NO_MIPS_STUB) || !sym->isInGot(ctx); ++ }); ++ for (size_t i = 0; i != entries.size(); ++i) ++ indices[entries[i]] = i; ++} ++ ++void MipsStubsSection::writeTo(uint8_t *buf) { ++ for (const Symbol *sym : entries) { ++ writeMipsStub(ctx, buf, sym->dynsymIndex, stubSize == stubBigSize); ++ buf += stubSize; ++ } ++} ++ + // On PowerPC the .plt section is used to hold the table of function addresses + // instead of the .got.plt, and the type is SHT_NOBITS similar to a .bss + // section. I don't know why we have a BSS style type for the section but it is +@@ -1505,6 +1563,7 @@ DynamicSection::computeContents() { addInt(DT_RISCV_VARIANT_CC, 0); [[fallthrough]]; default: @@ -9,7 +100,7 @@ Index: lld/ELF/SyntheticSections.cpp addInSec(DT_PLTGOT, *ctx.in.gotPlt); break; } -@@ -1720,14 +1721,20 @@ void RelocationBaseSection::finalizeContents() { +@@ -1720,14 +1779,20 @@ void RelocationBaseSection::finalizeContents() { else getParent()->link = 0; @@ -33,7 +124,7 @@ Index: lld/ELF/SyntheticSections.cpp r_sym = getSymIndex(symt); addend = computeAddend(ctx); isFinal = true; // Catch errors -@@ -2176,12 +2183,12 @@ void SymbolTableBaseSection::finalizeContents() { +@@ -2176,12 +2241,12 @@ void SymbolTableBaseSection::finalizeContents() { return; } @@ -50,7 +141,7 @@ Index: lld/ELF/SyntheticSections.cpp if (getPartition(ctx).gnuHashTab) { // NB: It also sorts Symbols to meet the GNU hash table requirements. -@@ -2201,7 +2208,7 @@ void SymbolTableBaseSection::finalizeContents() { +@@ -2201,7 +2266,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 @@ -59,7 +150,7 @@ Index: lld/ELF/SyntheticSections.cpp // // 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) { +@@ -2235,6 +2300,13 @@ void SymbolTableBaseSection::addSymbol(Symbol *b) { symbols.push_back({b, strTabSec.addString(b->getName(), false)}); } @@ -73,7 +164,17 @@ Index: lld/ELF/SyntheticSections.cpp size_t SymbolTableBaseSection::getSymbolIndex(const Symbol &sym) { if (this == ctx.mainPart->dynSymTab.get()) return sym.dynsymIndex; -@@ -2508,7 +2522,8 @@ void GnuHashTableSection::addSymbols(SmallVectorImpl void SymbolTableSection::w + Symbol *sym = ent.sym; + if (sym->isInPlt(ctx) && sym->hasFlag(NEEDS_COPY)) + eSym->st_other |= STO_MIPS_PLT; ++ if (type == SHT_DYNSYM && ctx.in.mipsStubs) ++ if (std::optional va = ctx.in.mipsStubs->getStubVA(*sym)) ++ eSym->st_value = *va; + if (isMicroMips(ctx)) { + // We already set the less-significant bit for symbols + // marked by the `STO_MIPS_MICROMIPS` flag and for microMIPS PLT +@@ -2508,7 +2583,8 @@ void GnuHashTableSection::addSymbols(SmallVectorImplgetSymbols()) { Symbol *sym = s.sym; @@ -92,7 +193,7 @@ Index: lld/ELF/SyntheticSections.cpp StringRef name = sym->getName(); unsigned i = sym->dynsymIndex; uint32_t hash = hashSysV(name) % numSymbols; -@@ -2602,23 +2619,29 @@ PltSection::PltSection(Ctx &ctx) +@@ -2602,23 +2680,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"; @@ -125,7 +226,7 @@ Index: lld/ELF/SyntheticSections.cpp } void PltSection::addEntry(Symbol &sym) { -@@ -2641,11 +2664,8 @@ bool PltSection::isNeeded() const { +@@ -2641,11 +2725,8 @@ bool PltSection::isNeeded() const { void PltSection::addSymbols() { ctx.target->addPltHeaderSymbols(*this); @@ -139,7 +240,7 @@ Index: lld/ELF/SyntheticSections.cpp } IpltSection::IpltSection(Ctx &ctx) -@@ -4878,10 +4898,12 @@ template void elf::createSyntheticSection +@@ -4878,10 +4959,12 @@ template void elf::createSyntheticSection // _GLOBAL_OFFSET_TABLE_ is defined relative to either .got.plt or .got. Treat // it as a relocation and ensure the referenced section is created. if (ctx.sym.globalOffsetTable && ctx.arg.emachine != EM_MIPS) { @@ -154,7 +255,16 @@ Index: lld/ELF/SyntheticSections.cpp } // We always need to add rel[a].plt to output if it has entries. -@@ -4909,6 +4931,12 @@ template void elf::createSyntheticSection +@@ -4905,10 +4988,21 @@ template void elf::createSyntheticSection + ctx.in.iplt = std::make_unique(ctx); + add(*ctx.in.iplt); + ++ if (ctx.arg.emachine == EM_MIPS && !ctx.arg.relocatable && ctx.hasDynsym) { ++ ctx.in.mipsStubs = std::make_unique(ctx); ++ add(*ctx.in.mipsStubs); ++ } ++ + if (ctx.arg.andFeatures || ctx.aarch64PauthAbiCoreInfo) { ctx.in.gnuProperty = std::make_unique(ctx); add(*ctx.in.gnuProperty); } Index: ports/devel/llvm/22/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.2 patch-lld_ELF_SyntheticSections_h --- ports/devel/llvm/22/patches/patch-lld_ELF_SyntheticSections_h 30 Jul 2026 19:28:03 -0000 1.2 +++ ports/devel/llvm/22/patches/patch-lld_ELF_SyntheticSections_h 1 Nov 2026 21:01:39 -0000 @@ -29,7 +29,32 @@ Index: lld/ELF/SyntheticSections.h StringTableSection &strTabSec; -@@ -1462,7 +1469,11 @@ void addVerneed(Ctx &, Symbol &ss); +@@ -1195,6 +1202,24 @@ class MipsRldMapSection final : public SyntheticSectio + void writeTo(uint8_t *buf) override {} + }; + ++class MipsStubsSection final : public SyntheticSection { ++ static constexpr size_t stubNormalSize = 16; ++ static constexpr size_t stubBigSize = 20; ++public: ++ MipsStubsSection(Ctx &); ++ void addSymbols(); ++ std::optional getStubVA(const Symbol &sym) const; ++ size_t getSize() const override { return entries.size() * stubSize; } ++ bool isNeeded() const override { return !entries.empty(); } ++ void finalizeContents() override; ++ void writeTo(uint8_t *buf) override; ++ ++private: ++ SmallVector entries; ++ llvm::DenseMap indices; ++ size_t stubSize = stubNormalSize; ++}; ++ + // Representation of the combined .ARM.Exidx input sections. We process these + // as a SyntheticSection like .eh_frame as we need to merge duplicate entries + // and add terminating sentinel entries. +@@ -1462,7 +1487,11 @@ void addVerneed(Ctx &, Symbol &ss); // placed in it. struct PhdrEntry { PhdrEntry(Ctx &ctx, unsigned type, unsigned flags) Index: ports/devel/llvm/22/patches/patch-lld_ELF_Target_h =================================================================== RCS file: /home/cvs/ports/devel/llvm/22/patches/patch-lld_ELF_Target_h,v diff -u -p -r1.1 patch-lld_ELF_Target_h --- ports/devel/llvm/22/patches/patch-lld_ELF_Target_h 30 Jul 2026 19:28:03 -0000 1.1 +++ ports/devel/llvm/22/patches/patch-lld_ELF_Target_h 1 Nov 2026 21:01:39 -0000 @@ -61,3 +61,11 @@ Index: lld/ELF/Target.h protected: // On FreeBSD x86_64 the first page cannot be mmaped. +@@ -233,6 +247,7 @@ uint64_t getMipsPageAddr(uint64_t addr); + bool isMipsN32Abi(Ctx &, const InputFile &f); + bool isMicroMips(Ctx &); + bool isMipsR6(Ctx &); ++void writeMipsStub(Ctx &, uint8_t *buf, uint32_t dynsymIndex, bool big); + + void writePPC32GlinkSection(Ctx &, uint8_t *buf, size_t numEntries); + Index: ports/devel/llvm/22/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.3 patch-lld_ELF_Writer_cpp --- ports/devel/llvm/22/patches/patch-lld_ELF_Writer_cpp 20 Aug 2026 14:38:00 -0000 1.3 +++ ports/devel/llvm/22/patches/patch-lld_ELF_Writer_cpp 1 Nov 2026 21:01:39 -0000 @@ -85,7 +85,19 @@ Index: lld/ELF/Writer.cpp if (ctx.arg.randomizeSectionPadding) randomizeSectionPadding(ctx); -@@ -2031,10 +2046,10 @@ template void Writer::finalizeSecti +@@ -2001,8 +2016,11 @@ template void Writer::finalizeSecti + } + } + ++ if (ctx.in.mipsStubs) ++ ctx.in.mipsStubs->addSymbols(); + if (ctx.in.mipsGot) + ctx.in.mipsGot->build(); ++ finalizeSynthetic(ctx, ctx.in.mipsStubs.get()); + + removeUnusedSyntheticSections(ctx); + ctx.script->diagnoseOrphanHandling(); +@@ -2031,10 +2049,10 @@ template void Writer::finalizeSecti sec->addrExpr = [=] { return i->second; }; } @@ -99,7 +111,7 @@ Index: lld/ELF/Writer.cpp 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) { +@@ -2300,6 +2318,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; @@ -115,7 +127,7 @@ Index: lld/ELF/Writer.cpp if (ctx.arg.executeOnly && (flags & PF_X)) return flags & ~PF_R; return flags; -@@ -2454,34 +2478,22 @@ Writer::createPhdrs(Partition &part) { +@@ -2454,34 +2481,22 @@ Writer::createPhdrs(Partition &part) { addHdr(PT_GNU_EH_FRAME, part.ehFrameHdr->getParent()->getPhdrFlags()) ->add(part.ehFrameHdr->getParent()); @@ -163,7 +175,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 +2505,19 @@ Writer::createPhdrs(Partition &part) { +@@ -2493,7 +2508,19 @@ Writer::createPhdrs(Partition &part) { // OpenBSD. if (ctx.arg.zWxneeded) addHdr(PT_OPENBSD_WXNEEDED, PF_X); @@ -183,7 +195,7 @@ Index: lld/ELF/Writer.cpp if (OutputSection *cmd = findSection(ctx, ".note.gnu.property", partNo)) addHdr(PT_GNU_PROPERTY, PF_R)->add(cmd); -@@ -2587,6 +2611,31 @@ template void Writer::fixSectionAli +@@ -2587,6 +2614,31 @@ template void Writer::fixSectionAli } }; @@ -215,7 +227,7 @@ Index: lld/ELF/Writer.cpp for (Partition &part : ctx.partitions) { prev = nullptr; for (auto &p : part.phdrs) -@@ -2651,6 +2700,16 @@ static std::string rangeToString(uint64_t addr, uint64 +@@ -2651,6 +2703,16 @@ static std::string rangeToString(uint64_t addr, uint64 template void Writer::assignFileOffsets() { ctx.out.programHeaders->offset = ctx.out.elfHeader->size; uint64_t off = ctx.out.elfHeader->size + ctx.out.programHeaders->size; Index: ports/devel/llvm/23/patches/patch-lld_ELF_Arch_Mips_cpp =================================================================== RCS file: ports/devel/llvm/23/patches/patch-lld_ELF_Arch_Mips_cpp diff -N ports/devel/llvm/23/patches/patch-lld_ELF_Arch_Mips_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ports/devel/llvm/23/patches/patch-lld_ELF_Arch_Mips_cpp 1 Nov 2026 21:13:24 -0000 @@ -0,0 +1,81 @@ +Index: lld/ELF/Arch/Mips.cpp +--- lld/ELF/Arch/Mips.cpp.orig ++++ lld/ELF/Arch/Mips.cpp +@@ -94,6 +94,41 @@ uint64_t elf::getMipsPageAddr(uint64_t addr) { + return (addr + 0x8000) & ~0xffff; + } + ++void elf::writeMipsStub(Ctx &ctx, uint8_t *buf, uint32_t dynsymIndex, ++ bool big) { ++ if (dynsymIndex > 0x7fffffff) { ++ ErrAlways(ctx) << "MIPS lazy-binding stub dynamic symbol index " ++ << dynsymIndex << " exceeds 31 bits"; ++ return; ++ } ++ ++ const uint32_t load = ctx.arg.is64 ? 0xdf998010 // ld $25, -0x7ff0($28) ++ : 0x8f998010; // lw $25, -0x7ff0($28) ++ const uint32_t lui = 0x3c180000 | ((dynsymIndex >> 16) & 0x7fff); ++ const uint32_t jalr = ctx.arg.zHazardplt ? 0x0320fc09 : 0x0320f809; ++ uint32_t loadIndex; ++ if (big) ++ loadIndex = 0x37180000 | (dynsymIndex & 0xffff); // ori $24, $24, idx ++ else if (dynsymIndex & ~0x7fff) ++ loadIndex = 0x34180000 | (dynsymIndex & 0xffff); // ori $24, $0, idx ++ else ++ loadIndex = (ctx.arg.is64 ? 0x64180000 : 0x24180000) | // [d]addiu $24, ++ dynsymIndex; // $0, idx ++ ++ if (big) { ++ write32(ctx, buf, load); ++ write32(ctx, buf + 4, 0x03e07825); // move $15, $31 ++ write32(ctx, buf + 8, lui); ++ write32(ctx, buf + 12, jalr); // jalr[.hb] $25 ++ write32(ctx, buf + 16, loadIndex); // delay slot ++ } else { ++ write32(ctx, buf, load); ++ write32(ctx, buf + 4, 0x03e07825); // move $15, $31 ++ write32(ctx, buf + 8, jalr); // jalr[.hb] $25 ++ write32(ctx, buf + 12, loadIndex); // delay slot ++ } ++} ++ + template MIPS::MIPS(Ctx &ctx) : TargetInfo(ctx) { + gotPltHeaderEntriesNum = 2; + defaultMaxPageSize = 65536; +@@ -668,6 +703,13 @@ static RelType getMipsPairType(RelType type, bool isLo + } + } + ++static bool isCallReloc(RelType type) { ++ return type == R_MIPS_CALL16 || ++ type == R_MIPS_CALL_HI16 || type == R_MIPS_CALL_LO16 || ++ type == R_MICROMIPS_CALL16 || ++ type == R_MICROMIPS_CALL_HI16 || type == R_MICROMIPS_CALL_LO16; ++} ++ + template + template + void MIPS::scanSectionImpl(InputSectionBase &sec, Relocs rels, +@@ -698,6 +740,21 @@ void MIPS::scanSectionImpl(InputSectionBase &sec + if (sym.isUndefined() && symIdx != 0 && + rs.maybeReportUndefined(cast(sym), offset)) + continue; ++ ++ // In a shared object, turn untyped undefined symbols with a call ++ // relocation into function symbols so that the dynamic linker handles ++ // them correctly. ++ if (ctx.arg.shared && sym.isUndefined() && symIdx != 0 && ++ sym.type == STT_NOTYPE && isCallReloc(type.v & 0xff)) ++ sym.type = STT_FUNC; ++ ++ if (symIdx != 0) { ++ uint32_t baseType = type.v & 0xff; ++ if (isCallReloc(baseType)) ++ sym.setFlags(HAS_MIPS_CALL_RELOC); ++ else if (baseType != R_MIPS_JALR && baseType != R_MICROMIPS_JALR) ++ sym.setFlags(NO_MIPS_STUB); ++ } + + auto addend = rs.getAddend(rel, type); + if (expr == RE_MIPS_GOTREL && sym.isLocal()) { Index: ports/devel/llvm/23/patches/patch-lld_ELF_Config_h =================================================================== RCS file: /home/cvs/ports/devel/llvm/23/patches/patch-lld_ELF_Config_h,v diff -u -p -r1.1.1.1 patch-lld_ELF_Config_h --- ports/devel/llvm/23/patches/patch-lld_ELF_Config_h 22 Sep 2026 03:34:34 -0000 1.1.1.1 +++ ports/devel/llvm/23/patches/patch-lld_ELF_Config_h 1 Nov 2026 21:13:22 -0000 @@ -1,7 +1,15 @@ Index: lld/ELF/Config.h --- lld/ELF/Config.h.orig +++ lld/ELF/Config.h -@@ -476,8 +476,13 @@ struct Config { +@@ -71,6 +71,7 @@ class IpltSection; + class MemtagAndroidNote; + class MemtagGlobalDescriptors; + class MipsGotSection; ++class MipsStubsSection; + class PPC64LongBranchTargetSection; + class PackageMetadataNote; + class PltSection; +@@ -476,8 +477,13 @@ struct Config { ELFKind ekind = ELFNoneKind; uint16_t emachine = llvm::ELF::EM_NONE; std::optional imageBase; @@ -15,7 +23,15 @@ Index: lld/ELF/Config.h uint64_t mipsGotSize; uint64_t zStackSize; unsigned ltoPartitions; -@@ -676,6 +681,9 @@ struct Ctx : CommonLinkerContext { +@@ -636,6 +642,7 @@ struct InStruct { + std::unique_ptr mipsOptions; + std::unique_ptr mipsReginfo; + std::unique_ptr mipsRldMap; ++ std::unique_ptr mipsStubs; + std::unique_ptr ppc32Got2; + // Non-SHF_ALLOC sections + std::unique_ptr debugNames; +@@ -676,6 +683,9 @@ struct Ctx : CommonLinkerContext { struct ElfSym { // __bss_start Defined *bss; Index: ports/devel/llvm/23/patches/patch-lld_ELF_Relocations_cpp =================================================================== RCS file: /home/cvs/ports/devel/llvm/23/patches/patch-lld_ELF_Relocations_cpp,v diff -u -p -r1.2 patch-lld_ELF_Relocations_cpp --- ports/devel/llvm/23/patches/patch-lld_ELF_Relocations_cpp 23 Sep 2026 12:14:28 -0000 1.2 +++ ports/devel/llvm/23/patches/patch-lld_ELF_Relocations_cpp 1 Nov 2026 21:13:22 -0000 @@ -134,7 +134,16 @@ Index: lld/ELF/Relocations.cpp ctx.in.relaDyn->addSymbolReloc(rel, *sec, offset, sym, addend, type); // MIPS ABI turns using of GOT and dynamic relocations inside out. -@@ -1317,10 +1363,7 @@ void elf::postScanRelocations(Ctx &ctx) { +@@ -1167,6 +1213,8 @@ void RelocScan::scanEhSection(EhInputSection &s) { + if (sym->isUndefined() && + maybeReportUndefined(cast(*sym), offset)) + continue; ++ if (ctx.arg.emachine == EM_MIPS) ++ sym->setFlags(NO_MIPS_STUB); + process(r.expr, r.type, offset, *sym, r.addend); + } + } +@@ -1317,10 +1365,7 @@ void elf::postScanRelocations(Ctx &ctx) { } else { assert(sym.isFunc() && sym.hasFlag(NEEDS_PLT)); if (!sym.isDefined()) { Index: ports/devel/llvm/23/patches/patch-lld_ELF_Symbols_h =================================================================== RCS file: /home/cvs/ports/devel/llvm/23/patches/patch-lld_ELF_Symbols_h,v diff -u -p -r1.1.1.1 patch-lld_ELF_Symbols_h --- ports/devel/llvm/23/patches/patch-lld_ELF_Symbols_h 22 Sep 2026 03:34:35 -0000 1.1.1.1 +++ ports/devel/llvm/23/patches/patch-lld_ELF_Symbols_h 1 Nov 2026 21:13:22 -0000 @@ -1,7 +1,16 @@ Index: lld/ELF/Symbols.h --- lld/ELF/Symbols.h.orig +++ lld/ELF/Symbols.h -@@ -142,6 +142,9 @@ class Symbol { (public) +@@ -59,6 +59,8 @@ enum { + NEEDS_GOT_NONAUTH = 1 << 10, + NEEDS_TLSDESC_AUTH = 1 << 11, + NEEDS_TLSDESC_NONAUTH = 1 << 12, ++ HAS_MIPS_CALL_RELOC = 1 << 13, ++ NO_MIPS_STUB = 1 << 14, + }; + + // The base class for real symbol classes. +@@ -142,6 +144,9 @@ class Symbol { (public) LLVM_PREFERRED_TYPE(bool) uint8_t hasVersionSuffix : 1; @@ -11,7 +20,7 @@ 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; } -@@ -198,6 +201,7 @@ class Symbol { (public) +@@ -198,6 +203,7 @@ class Symbol { (public) uint64_t getGotVA(Ctx &) const; uint64_t getGotPltOffset(Ctx &) const; uint64_t getGotPltVA(Ctx &) const; @@ -19,7 +28,7 @@ Index: lld/ELF/Symbols.h uint64_t getPltVA(Ctx &) const; uint64_t getSize() const; OutputSection *getOutputSection() const; -@@ -238,7 +242,7 @@ class Symbol { (public) +@@ -238,7 +244,7 @@ class Symbol { (public) : file(file), nameData(name.data()), nameSize(name.size()), type(type), binding(binding), stOther(stOther), symbolKind(k), isPreemptible(false), isUsedInRegularObj(false), isExported(false), ltoCanOmit(false), @@ -28,7 +37,7 @@ Index: lld/ELF/Symbols.h gotInIgot(false), folded(false), archSpecificBit(false), scriptDefined(false), dsoDefined(false), dsoProtected(false), versionScriptAssigned(false), thunkAccessed(false), -@@ -534,6 +538,8 @@ void reportDuplicate(Ctx &, const Symbol &sym, const I +@@ -534,6 +540,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: ports/devel/llvm/23/patches/patch-lld_ELF_SyntheticSections_cpp =================================================================== RCS file: /home/cvs/ports/devel/llvm/23/patches/patch-lld_ELF_SyntheticSections_cpp,v diff -u -p -r1.2 patch-lld_ELF_SyntheticSections_cpp --- ports/devel/llvm/23/patches/patch-lld_ELF_SyntheticSections_cpp 23 Sep 2026 12:14:28 -0000 1.2 +++ ports/devel/llvm/23/patches/patch-lld_ELF_SyntheticSections_cpp 1 Nov 2026 21:13:22 -0000 @@ -1,7 +1,89 @@ Index: lld/ELF/SyntheticSections.cpp --- lld/ELF/SyntheticSections.cpp.orig +++ lld/ELF/SyntheticSections.cpp -@@ -1310,6 +1310,7 @@ DynamicSection::computeContents() { +@@ -934,9 +934,13 @@ void MipsGotSection::build() { + uint64_t offset = p.second * ctx.arg.wordsize; + if (&got == primGot) + addConstant({R_ABS, ctx.target->relativeRel, offset, 0, p.first}); +- else ++ else { ++ // A lazy stub requires the primary GOT's resolver slots and $gp. ++ // Calls through secondary GOTs must be resolved eagerly. ++ p.first->setFlags(NO_MIPS_STUB); + ctx.in.relaDyn->addSymbolReloc(ctx.target->relativeRel, *this, offset, + *p.first); ++ } + } + // Relocation-only entries exist as dummy entries for dynamic symbols that + // aren't otherwise in the primary GOT, as the ABI requires an entry for +@@ -1013,8 +1017,66 @@ void MipsGotSection::writeTo(uint8_t *buf) { + writeUint(ctx, buf + ctx.arg.wordsize, + (uint64_t)1 << (ctx.arg.wordsize * 8 - 1)); + ctx.target->relocateAlloc(*this, buf); ++ ++ if (!ctx.in.mipsStubs || gots.empty()) ++ return; ++ auto writeStubAddresses = [&](const auto &entries) { ++ for (const auto &[sym, index] : entries) ++ if (std::optional va = ctx.in.mipsStubs->getStubVA(*sym)) ++ writeUint(ctx, buf + index * ctx.arg.wordsize, *va); ++ }; ++ writeStubAddresses(gots.front().global); ++ writeStubAddresses(gots.front().relocs); + } + ++MipsStubsSection::MipsStubsSection(Ctx &ctx) ++ : SyntheticSection(ctx, ".MIPS.stubs", SHT_PROGBITS, ++ SHF_ALLOC | SHF_EXECINSTR, ctx.arg.wordsize) {} ++ ++void MipsStubsSection::addSymbols() { ++ // microMIPS stubs are not implemented. ++ if (isMicroMips(ctx)) ++ return; ++ for (Symbol *sym : ctx.symtab->getSymbols()) { ++ if (!sym->isPreemptible || !(sym->isUndefined() || sym->isShared()) || ++ !sym->isFunc() || !sym->hasFlag(HAS_MIPS_CALL_RELOC) || ++ sym->hasFlag(NO_MIPS_STUB) || sym->hasFlag(NEEDS_PLT)) ++ continue; ++ entries.push_back(sym); ++ } ++ ++ // Include the full stub size in GOT page counts before GOT layout. ++ // Removing secondary GOT candidates later can only shrink this size. ++ stubSize = ctx.in.dynSymTab->getNumSymbols() > 0x10000 ++ ? stubBigSize ++ : stubNormalSize; ++} ++ ++std::optional ++MipsStubsSection::getStubVA(const Symbol &sym) const { ++ if (!getParent()) ++ return std::nullopt; ++ auto it = indices.find(&sym); ++ if (it == indices.end()) ++ return std::nullopt; ++ return getVA() + it->second * stubSize; ++} ++ ++void MipsStubsSection::finalizeContents() { ++ llvm::erase_if(entries, [&](const Symbol *sym) { ++ return sym->hasFlag(NO_MIPS_STUB) || !sym->isInGot(ctx); ++ }); ++ for (size_t i = 0; i != entries.size(); ++i) ++ indices[entries[i]] = i; ++} ++ ++void MipsStubsSection::writeTo(uint8_t *buf) { ++ for (const Symbol *sym : entries) { ++ writeMipsStub(ctx, buf, sym->dynsymIndex, stubSize == stubBigSize); ++ buf += stubSize; ++ } ++} ++ + // On PowerPC the .plt section is used to hold the table of function addresses + // instead of the .got.plt, and the type is SHT_NOBITS similar to a .bss + // section. I don't know why we have a BSS style type for the section but it is +@@ -1310,6 +1372,7 @@ DynamicSection::computeContents() { addInt(DT_RISCV_VARIANT_CC, 0); [[fallthrough]]; default: @@ -9,7 +91,7 @@ Index: lld/ELF/SyntheticSections.cpp addInSec(DT_PLTGOT, *ctx.in.gotPlt); break; } -@@ -1521,14 +1522,20 @@ void RelocationBaseSection::finalizeContents() { +@@ -1521,14 +1584,20 @@ void RelocationBaseSection::finalizeContents() { else getParent()->link = 0; @@ -33,7 +115,7 @@ Index: lld/ELF/SyntheticSections.cpp r_sym = getSymIndex(symt); addend = computeAddend(ctx); isFinal = true; // Catch errors -@@ -1981,12 +1988,12 @@ void SymbolTableBaseSection::finalizeContents() { +@@ -1981,12 +2050,12 @@ void SymbolTableBaseSection::finalizeContents() { return; } @@ -50,7 +132,7 @@ Index: lld/ELF/SyntheticSections.cpp if (ctx.in.gnuHashTab) { // NB: It also sorts Symbols to meet the GNU hash table requirements. -@@ -2005,7 +2012,7 @@ void SymbolTableBaseSection::finalizeContents() { +@@ -2005,7 +2074,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 @@ -59,7 +141,7 @@ Index: lld/ELF/SyntheticSections.cpp // // 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 -@@ -2039,6 +2046,13 @@ void SymbolTableBaseSection::addSymbol(Symbol *b) { +@@ -2039,6 +2108,13 @@ void SymbolTableBaseSection::addSymbol(Symbol *b) { symbols.push_back({b, strTabSec.addString(b->getName(), false)}); } @@ -73,7 +155,17 @@ Index: lld/ELF/SyntheticSections.cpp size_t SymbolTableBaseSection::getSymbolIndex(const Symbol &sym) { if (this == ctx.in.dynSymTab.get()) return sym.dynsymIndex; -@@ -2303,7 +2317,7 @@ void GnuHashTableSection::addSymbols(SmallVectorImpl void SymbolTableSection::w + Symbol *sym = ent.sym; + if (sym->isInPlt(ctx) && sym->hasFlag(NEEDS_COPY)) + eSym->st_other |= STO_MIPS_PLT; ++ if (type == SHT_DYNSYM && ctx.in.mipsStubs) ++ if (std::optional va = ctx.in.mipsStubs->getStubVA(*sym)) ++ eSym->st_value = *va; + if (isMicroMips(ctx)) { + // We already set the less-significant bit for symbols + // marked by the `STO_MIPS_MICROMIPS` flag and for microMIPS PLT +@@ -2303,7 +2382,7 @@ void GnuHashTableSection::addSymbols(SmallVectorImplgetSymbols()) { Symbol *sym = s.sym; @@ -91,7 +183,7 @@ Index: lld/ELF/SyntheticSections.cpp StringRef name = sym->getName(); unsigned i = sym->dynsymIndex; uint32_t hash = hashSysV(name) % numSymbols; -@@ -2397,23 +2413,29 @@ PltSection::PltSection(Ctx &ctx) +@@ -2397,23 +2478,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"; @@ -124,7 +216,7 @@ Index: lld/ELF/SyntheticSections.cpp } void PltSection::addEntry(Symbol &sym) { -@@ -2436,11 +2458,8 @@ bool PltSection::isNeeded() const { +@@ -2436,11 +2523,8 @@ bool PltSection::isNeeded() const { void PltSection::addSymbols() { ctx.target->addPltHeaderSymbols(*this); @@ -138,7 +230,7 @@ Index: lld/ELF/SyntheticSections.cpp } IpltSection::IpltSection(Ctx &ctx) -@@ -4527,10 +4546,12 @@ template void elf::createSyntheticSection +@@ -4527,10 +4611,12 @@ template void elf::createSyntheticSection // _GLOBAL_OFFSET_TABLE_ is defined relative to either .got.plt or .got. Treat // it as a relocation and ensure the referenced section is created. if (ctx.sym.globalOffsetTable && ctx.arg.emachine != EM_MIPS) { @@ -153,7 +245,16 @@ Index: lld/ELF/SyntheticSections.cpp } // We always need to add rel[a].plt to output if it has entries. -@@ -4552,6 +4573,12 @@ template void elf::createSyntheticSection +@@ -4548,10 +4634,21 @@ template void elf::createSyntheticSection + ctx.in.iplt = std::make_unique(ctx); + add(*ctx.in.iplt); + ++ if (ctx.arg.emachine == EM_MIPS && !ctx.arg.relocatable && ctx.hasDynsym) { ++ ctx.in.mipsStubs = std::make_unique(ctx); ++ add(*ctx.in.mipsStubs); ++ } ++ + if (ctx.arg.andFeatures || ctx.aarch64PauthAbiCoreInfo) { ctx.in.gnuProperty = std::make_unique(ctx); add(*ctx.in.gnuProperty); } Index: ports/devel/llvm/23/patches/patch-lld_ELF_SyntheticSections_h =================================================================== RCS file: /home/cvs/ports/devel/llvm/23/patches/patch-lld_ELF_SyntheticSections_h,v diff -u -p -r1.3 patch-lld_ELF_SyntheticSections_h --- ports/devel/llvm/23/patches/patch-lld_ELF_SyntheticSections_h 10 Oct 2026 09:09:22 -0000 1.3 +++ ports/devel/llvm/23/patches/patch-lld_ELF_SyntheticSections_h 1 Nov 2026 21:13:22 -0000 @@ -29,7 +29,32 @@ Index: lld/ELF/SyntheticSections.h StringTableSection &strTabSec; -@@ -1329,7 +1336,11 @@ void addVerneed(Ctx &, Symbol &ss); +@@ -1136,6 +1143,24 @@ class MergeNoTailSection final : public MergeSynthetic + size_t shardOffsets[numShards]; + }; + ++class MipsStubsSection final : public SyntheticSection { ++ static constexpr size_t stubNormalSize = 16; ++ static constexpr size_t stubBigSize = 20; ++public: ++ MipsStubsSection(Ctx &); ++ void addSymbols(); ++ std::optional getStubVA(const Symbol &sym) const; ++ size_t getSize() const override { return entries.size() * stubSize; } ++ bool isNeeded() const override { return !entries.empty(); } ++ void finalizeContents() override; ++ void writeTo(uint8_t *buf) override; ++ ++private: ++ SmallVector entries; ++ llvm::DenseMap indices; ++ size_t stubSize = stubNormalSize; ++}; ++ + // Representation of the combined .ARM.Exidx input sections. We process these + // as a SyntheticSection like .eh_frame as we need to merge duplicate entries + // and add terminating sentinel entries. +@@ -1329,7 +1354,11 @@ void addVerneed(Ctx &, Symbol &ss); // placed in it. struct PhdrEntry { PhdrEntry(Ctx &ctx, unsigned type, unsigned flags) Index: ports/devel/llvm/23/patches/patch-lld_ELF_Target_h =================================================================== RCS file: /home/cvs/ports/devel/llvm/23/patches/patch-lld_ELF_Target_h,v diff -u -p -r1.1 patch-lld_ELF_Target_h --- ports/devel/llvm/23/patches/patch-lld_ELF_Target_h 23 Sep 2026 12:14:28 -0000 1.1 +++ ports/devel/llvm/23/patches/patch-lld_ELF_Target_h 1 Nov 2026 21:13:22 -0000 @@ -61,3 +61,11 @@ Index: lld/ELF/Target.h protected: // On FreeBSD x86_64 the first page cannot be mmaped. +@@ -243,6 +257,7 @@ uint64_t getMipsPageAddr(uint64_t addr); + bool isMipsN32Abi(Ctx &, const InputFile &f); + bool isMicroMips(Ctx &); + bool isMipsR6(Ctx &); ++void writeMipsStub(Ctx &, uint8_t *buf, uint32_t dynsymIndex, bool big); + + void writePPC32GlinkSection(Ctx &, uint8_t *buf, size_t numEntries); + Index: ports/devel/llvm/23/patches/patch-lld_ELF_Writer_cpp =================================================================== RCS file: /home/cvs/ports/devel/llvm/23/patches/patch-lld_ELF_Writer_cpp,v diff -u -p -r1.3 patch-lld_ELF_Writer_cpp --- ports/devel/llvm/23/patches/patch-lld_ELF_Writer_cpp 25 Oct 2026 07:38:37 -0000 1.3 +++ ports/devel/llvm/23/patches/patch-lld_ELF_Writer_cpp 1 Nov 2026 21:13:22 -0000 @@ -72,7 +72,19 @@ 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) { -@@ -2247,6 +2262,15 @@ static bool needsPtLoad(OutputSection *sec) { +@@ -1968,8 +1983,11 @@ template void Writer::finalizeSecti + + } + ++ if (ctx.in.mipsStubs) ++ ctx.in.mipsStubs->addSymbols(); + if (ctx.in.mipsGot) + ctx.in.mipsGot->build(); ++ finalizeSynthetic(ctx, ctx.in.mipsStubs.get()); + + removeUnusedSyntheticSections(ctx); + ctx.script->diagnoseOrphanHandling(); +@@ -2247,6 +2265,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; @@ -88,7 +100,7 @@ Index: lld/ELF/Writer.cpp if (ctx.arg.executeOnly && (flags & PF_X)) return flags & ~PF_R; return flags; -@@ -2378,34 +2402,22 @@ SmallVector, 0> Writer, 0> WritergetParent()->getPhdrFlags()) ->add(ctx.in.ehFrameHdr->getParent()); @@ -136,7 +148,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) -@@ -2417,7 +2429,19 @@ SmallVector, 0> Writer, 0> Writeradd(cmd); -@@ -2501,6 +2525,31 @@ template void Writer::fixSectionAli +@@ -2501,6 +2528,31 @@ template void Writer::fixSectionAli } }; @@ -188,7 +200,7 @@ Index: lld/ELF/Writer.cpp prev = nullptr; for (auto &ph : ctx.phdrs) if (ph->p_type == PT_LOAD && ph->firstSec) { -@@ -2563,6 +2612,16 @@ static std::string rangeToString(uint64_t addr, uint64 +@@ -2563,6 +2615,16 @@ static std::string rangeToString(uint64_t addr, uint64 template void Writer::assignFileOffsets() { ctx.out.programHeaders->offset = ctx.out.elfHeader->size; uint64_t off = ctx.out.elfHeader->size + ctx.out.programHeaders->size; -- wbr, Kirill