Index | Thread | Search

From:
Mark Kettenis <mark.kettenis@xs4all.nl>
Subject:
Re: lld: add MIPS lazy binding stubs
To:
Kirill A. Korinsky <kirill@korins.ky>
Cc:
tech@openbsd.org, semarie@openbsd.org, robert@openbsd.org
Date:
Mon, 02 Nov 2026 07:51:50 +0100

Download raw body.

Thread
> 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
> 
>