Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
lld: add MIPS lazy binding stubs
To:
OpenBSD tech <tech@openbsd.org>
Cc:
Sebastien Marie <semarie@openbsd.org>, Robert Nagy <robert@openbsd.org>
Date:
Sun, 01 Nov 2026 23:42:39 +0100

Download raw body.

Thread
tech@,

here a diff for base llvm and both 22 and 23 in ports to implement lazy
binding in LLD for mips64 which is based on visa@'s sketch diff.

regress/libexec/ld.so/lazy is passed, and visa@ tested it on his device.

This diff was backported to uptream as
https://github.com/llvm/llvm-project/pull/237790

Ok?

Index: src/gnu/llvm/lld/ELF/Config.h
===================================================================
RCS file: /home/cvs/src/gnu/llvm/lld/ELF/Config.h,v
diff -u -p -r1.7 Config.h
--- src/gnu/llvm/lld/ELF/Config.h	29 May 2026 11:06:20 -0000	1.7
+++ src/gnu/llvm/lld/ELF/Config.h	1 Nov 2026 20:37:58 -0000
@@ -65,6 +65,7 @@ class InputSection;
 class IpltSection;
 class MipsGotSection;
 class MipsRldMapSection;
+class MipsStubsSection;
 class PPC32Got2Section;
 class PPC64LongBranchTargetSection;
 class PltSection;
@@ -592,6 +593,7 @@ struct InStruct {
   std::unique_ptr<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