Download raw body.
lld: add MIPS lazy binding stubs
> Date: Sun, 01 Nov 2026 23:42:39 +0100
> From: Kirill A. Korinsky <kirill@korins.ky>
>
> 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?
Given the upstream review of that diff, I think we shouldn't rush
ahead with this. I don't think we want to carry a significant local
diff like thise ~forever...
I don't have the cycles available right now to understand the
.MIPS.stubs stuff and look into the suggested rtld changes. But we
probably should investigate the suggestions being made.
> 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<SyntheticSection> mipsOptions;
> std::unique_ptr<SyntheticSection> mipsReginfo;
> std::unique_ptr<MipsRldMapSection> mipsRldMap;
> + std::unique_ptr<MipsStubsSection> mipsStubs;
> std::unique_ptr<SyntheticSection> partEnd;
> std::unique_ptr<SyntheticSection> partIndex;
> std::unique_ptr<PltSection> 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<Undefined>(*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<Symbol *, size_t> &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<GotEntry, size_t> &p : g.local16)
> write(p.second, p.first.first, p.first.second);
> + auto writeGlobal = [&](size_t i, const Symbol *s) {
> + if (std::optional<uint64_t> 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<Symbol *, size_t> &p : g.global)
> - write(p.second, p.first, 0);
> + writeGlobal(p.second, p.first);
> for (const std::pair<Symbol *, size_t> &p : g.relocs)
> - write(p.second, p.first, 0);
> + writeGlobal(p.second, p.first);
> for (const std::pair<Symbol *, size_t> &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<uint64_t>
> +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 <class ELFT> 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<uint64_t> 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 <class ELFT> void elf::createSy
> add(*ctx.in.plt);
> ctx.in.iplt = std::make_unique<IpltSection>(ctx);
> add(*ctx.in.iplt);
> +
> + if (ctx.arg.emachine == EM_MIPS && !ctx.arg.relocatable && ctx.hasDynsym) {
> + ctx.in.mipsStubs = std::make_unique<MipsStubsSection>(ctx);
> + add(*ctx.in.mipsStubs);
> + }
>
> if (ctx.arg.andFeatures || ctx.aarch64PauthAbiCoreInfo) {
> ctx.in.gnuProperty = std::make_unique<GnuPropertySection>(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<uint64_t> 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<const Symbol *, 0> entries;
> + llvm::DenseMap<const Symbol *, uint32_t> 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 <class ELFT> void Writer<ELFT>:
> }
> }
>
> + 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 <class ELFT> MIPS<ELFT>::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 <class ELFT>
> template <class RelTy>
> void MIPS<ELFT>::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
> @@ -639,6 +681,21 @@ void MIPS<ELFT>::scanSectionImpl(InputSe
> if (sym.isUndefined() && symIdx != 0 &&
> rs.maybeReportUndefined(cast<Undefined>(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<ELFT>(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 <class ELFT> MIPS<ELFT>::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 <class ELFT>
> + template <class RelTy>
> + void MIPS<ELFT>::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
> +@@ -639,6 +681,21 @@ void MIPS<ELFT>::scanSectionImpl(InputSectionBase &sec
> + if (sym.isUndefined() && symIdx != 0 &&
> + rs.maybeReportUndefined(cast<Undefined>(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<ELFT>(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<uint64_t> 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<SyntheticSection> mipsOptions;
> + std::unique_ptr<SyntheticSection> mipsReginfo;
> + std::unique_ptr<MipsRldMapSection> mipsRldMap;
> ++ std::unique_ptr<MipsStubsSection> mipsStubs;
> + std::unique_ptr<SyntheticSection> partEnd;
> + std::unique_ptr<SyntheticSection> partIndex;
> + std::unique_ptr<PltSection> 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<Undefined>(*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<OutputSection
> +@@ -2221,7 +2270,7 @@ bool elf::hexagonNeedsTLSSymbol(ArrayRef<OutputSection
> return needTlsSymbol;
> }
>
> Index: ports/devel/llvm/22/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.2 patch-lld_ELF_Symbols_cpp
> --- ports/devel/llvm/22/patches/patch-lld_ELF_Symbols_cpp 30 Jul 2026 19:28:03 -0000 1.2
> +++ ports/devel/llvm/22/patches/patch-lld_ELF_Symbols_cpp 1 Nov 2026 21:01:39 -0000
> @@ -1,5 +1,3 @@
> -XXX SymbolUnion
> -
> Index: lld/ELF/Symbols.cpp
> --- lld/ELF/Symbols.cpp.orig
> +++ lld/ELF/Symbols.cpp
> Index: ports/devel/llvm/22/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.5 patch-lld_ELF_Symbols_h
> --- ports/devel/llvm/22/patches/patch-lld_ELF_Symbols_h 30 Jul 2026 19:28:03 -0000 1.5
> +++ ports/devel/llvm/22/patches/patch-lld_ELF_Symbols_h 1 Nov 2026 21:01:39 -0000
> @@ -1,7 +1,16 @@
> Index: lld/ELF/Symbols.h
> --- lld/ELF/Symbols.h.orig
> +++ lld/ELF/Symbols.h
> -@@ -148,6 +148,9 @@ class Symbol { (public)
> +@@ -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.
> +@@ -148,6 +150,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; }
> -@@ -204,6 +207,7 @@ class Symbol { (public)
> +@@ -204,6 +209,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;
> -@@ -244,7 +248,7 @@ class Symbol { (public)
> +@@ -244,7 +250,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),
> @@ -28,7 +37,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 +544,8 @@ void reportDuplicate(Ctx &, const Symbol &sym, const I
> +@@ -540,6 +546,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/22/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.2 patch-lld_ELF_SyntheticSections_cpp
> --- ports/devel/llvm/22/patches/patch-lld_ELF_SyntheticSections_cpp 30 Jul 2026 19:28:03 -0000 1.2
> +++ ports/devel/llvm/22/patches/patch-lld_ELF_SyntheticSections_cpp 1 Nov 2026 21:01:39 -0000
> @@ -1,7 +1,98 @@
> Index: lld/ELF/SyntheticSections.cpp
> --- lld/ELF/SyntheticSections.cpp.orig
> +++ lld/ELF/SyntheticSections.cpp
> -@@ -1505,6 +1505,7 @@ DynamicSection<ELFT>::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<Symbol *, size_t> &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<GotEntry, size_t> &p : g.local16)
> + write(p.second, p.first.first, p.first.second);
> ++ auto writeGlobal = [&](size_t i, const Symbol *s) {
> ++ if (std::optional<uint64_t> 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<Symbol *, size_t> &p : g.global)
> +- write(p.second, p.first, 0);
> ++ writeGlobal(p.second, p.first);
> + for (const std::pair<Symbol *, size_t> &p : g.relocs)
> +- write(p.second, p.first, 0);
> ++ writeGlobal(p.second, p.first);
> + for (const std::pair<Symbol *, size_t> &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<uint64_t>
> ++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<ELFT>::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<S
> +@@ -2337,6 +2409,9 @@ template <class ELFT> void SymbolTableSection<ELFT>::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<uint64_t> 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(SmallVectorImpl<S
> // its type correctly.
> auto mid =
> std::stable_partition(v.begin(), v.end(), [&](const SymbolTableEntry &s) {
> @@ -83,7 +184,7 @@ Index: lld/ELF/SyntheticSections.cpp
> });
>
> // We chose load factor 4 for the on-disk hash table. For each hash
> -@@ -2574,6 +2589,8 @@ void HashTableSection::writeTo(uint8_t *buf) {
> +@@ -2574,6 +2650,8 @@ void HashTableSection::writeTo(uint8_t *buf) {
>
> for (const SymbolTableEntry &s : symTab->getSymbols()) {
> 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 <class ELFT> void elf::createSyntheticSection
> +@@ -4878,10 +4959,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) {
> @@ -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 <class ELFT> void elf::createSyntheticSection
> +@@ -4905,10 +4988,21 @@ template <class ELFT> void elf::createSyntheticSection
> + ctx.in.iplt = std::make_unique<IpltSection>(ctx);
> + add(*ctx.in.iplt);
> +
> ++ if (ctx.arg.emachine == EM_MIPS && !ctx.arg.relocatable && ctx.hasDynsym) {
> ++ ctx.in.mipsStubs = std::make_unique<MipsStubsSection>(ctx);
> ++ add(*ctx.in.mipsStubs);
> ++ }
> ++
> + if (ctx.arg.andFeatures || ctx.aarch64PauthAbiCoreInfo) {
> ctx.in.gnuProperty = std::make_unique<GnuPropertySection>(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<uint64_t> 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<const Symbol *, 0> entries;
> ++ llvm::DenseMap<const Symbol *, uint32_t> 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 <class ELFT> void Writer<ELFT>::finalizeSecti
> +@@ -2001,8 +2016,11 @@ template <class ELFT> void Writer<ELFT>::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 <class ELFT> void Writer<ELFT>::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<ELFT>::createPhdrs(Partition &part) {
> +@@ -2454,34 +2481,22 @@ Writer<ELFT>::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<ELFT>::createPhdrs(Partition &part) {
> +@@ -2493,7 +2508,19 @@ Writer<ELFT>::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 <class ELFT> void Writer<ELFT>::fixSectionAli
> +@@ -2587,6 +2614,31 @@ template <class ELFT> void Writer<ELFT>::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 <class ELFT> void Writer<ELFT>::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 <class ELFT> MIPS<ELFT>::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 <class ELFT>
> + template <class RelTy>
> + void MIPS<ELFT>::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
> +@@ -698,6 +740,21 @@ void MIPS<ELFT>::scanSectionImpl(InputSectionBase &sec
> + if (sym.isUndefined() && symIdx != 0 &&
> + rs.maybeReportUndefined(cast<Undefined>(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<ELFT>(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<uint64_t> 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<SyntheticSection> mipsOptions;
> + std::unique_ptr<SyntheticSection> mipsReginfo;
> + std::unique_ptr<SyntheticSection> mipsRldMap;
> ++ std::unique_ptr<MipsStubsSection> mipsStubs;
> + std::unique_ptr<SyntheticSection> ppc32Got2;
> + // Non-SHF_ALLOC sections
> + std::unique_ptr<SyntheticSection> 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<Undefined>(*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<ELFT>::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<uint64_t> 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<uint64_t>
> ++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<ELFT>::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<S
> +@@ -2133,6 +2209,9 @@ template <class ELFT> void SymbolTableSection<ELFT>::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<uint64_t> 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(SmallVectorImpl<S
> // its type correctly.
> auto mid =
> std::stable_partition(v.begin(), v.end(), [&](const SymbolTableEntry &s) {
> @@ -82,7 +174,7 @@ Index: lld/ELF/SyntheticSections.cpp
> });
>
> // We chose load factor 4 for the on-disk hash table. For each hash
> -@@ -2369,6 +2383,8 @@ void HashTableSection::writeTo(uint8_t *buf) {
> +@@ -2369,6 +2448,8 @@ void HashTableSection::writeTo(uint8_t *buf) {
>
> for (const SymbolTableEntry &s : symTab->getSymbols()) {
> 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 <class ELFT> void elf::createSyntheticSection
> +@@ -4527,10 +4611,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) {
> @@ -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 <class ELFT> void elf::createSyntheticSection
> +@@ -4548,10 +4634,21 @@ template <class ELFT> void elf::createSyntheticSection
> + ctx.in.iplt = std::make_unique<IpltSection>(ctx);
> + add(*ctx.in.iplt);
> +
> ++ if (ctx.arg.emachine == EM_MIPS && !ctx.arg.relocatable && ctx.hasDynsym) {
> ++ ctx.in.mipsStubs = std::make_unique<MipsStubsSection>(ctx);
> ++ add(*ctx.in.mipsStubs);
> ++ }
> ++
> + if (ctx.arg.andFeatures || ctx.aarch64PauthAbiCoreInfo) {
> ctx.in.gnuProperty = std::make_unique<GnuPropertySection>(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<uint64_t> 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<const Symbol *, 0> entries;
> ++ llvm::DenseMap<const Symbol *, uint32_t> 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 <class ELFT> void Writer<ELFT>::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<std::unique_ptr<PhdrEntry>, 0> Writer<ELFT
> +@@ -2378,34 +2405,22 @@ SmallVector<std::unique_ptr<PhdrEntry>, 0> Writer<ELFT
> addHdr(PT_GNU_EH_FRAME, ctx.in.ehFrameHdr->getParent()->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<std::unique_ptr<PhdrEntry>, 0> Writer<ELFT
> +@@ -2417,7 +2432,19 @@ SmallVector<std::unique_ptr<PhdrEntry>, 0> Writer<ELFT
> // OpenBSD.
> if (ctx.arg.zWxneeded)
> addHdr(PT_OPENBSD_WXNEEDED, PF_X);
> @@ -156,7 +168,7 @@ Index: lld/ELF/Writer.cpp
> if (OutputSection *cmd = findSection(ctx, ".note.gnu.property"))
> addHdr(PT_GNU_PROPERTY, PF_R)->add(cmd);
>
> -@@ -2501,6 +2525,31 @@ template <class ELFT> void Writer<ELFT>::fixSectionAli
> +@@ -2501,6 +2528,31 @@ template <class ELFT> void Writer<ELFT>::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 <class ELFT> void Writer<ELFT>::assignFileOffsets() {
> ctx.out.programHeaders->offset = ctx.out.elfHeader->size;
> uint64_t off = ctx.out.elfHeader->size + ctx.out.programHeaders->size;
>
>
> --
> wbr, Kirill
>
>
lld: add MIPS lazy binding stubs