[lld] Enough LLD changes to bootstrap OpenBSD on sparc64 (PR #207609)

Kirill A. Korinsky via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 2 04:01:23 PDT 2026


https://github.com/catap updated https://github.com/llvm/llvm-project/pull/207609

>From a7d79ba943ef5e5f9de7f7d1a2570a59923511df Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill at korins.ky>
Date: Mon, 27 Jul 2026 12:08:53 +0200
Subject: [PATCH 1/2] [lld][ELF][SPARC] Canonicalize absolute dynamic
 relocations

Co-authored-by: LemonBoy <thatlemon at gmail.com>
---
 lld/ELF/Arch/SPARCV9.cpp         | 60 +++++++++++++++++++++++-
 lld/ELF/Relocations.cpp          | 17 +++++--
 lld/ELF/SyntheticSections.cpp    |  1 +
 lld/ELF/SyntheticSections.h      |  5 ++
 lld/ELF/Target.h                 |  2 +
 lld/test/ELF/sparcv9-abs-pic.s   |  8 ++--
 lld/test/ELF/sparcv9-ua-dynrel.s | 80 ++++++++++++++++++++++++++++++++
 7 files changed, 164 insertions(+), 9 deletions(-)
 create mode 100644 lld/test/ELF/sparcv9-ua-dynrel.s

diff --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index 7797990571def..eb948cbd366fe 100644
--- a/lld/ELF/Arch/SPARCV9.cpp
+++ b/lld/ELF/Arch/SPARCV9.cpp
@@ -27,6 +27,7 @@ class SPARCV9 final : public TargetInfo {
                      const uint8_t *loc) const override;
   RelType getDynRel(RelType type) const override;
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
+  void finalizeDynamicReloc(DynamicReloc &rel) const override;
   void writeGotHeader(uint8_t *buf) const override;
   void writePlt(uint8_t *buf, const Symbol &sym,
                 uint64_t pltEntryAddr) const override;
@@ -83,8 +84,17 @@ RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
 }
 
 RelType SPARCV9::getDynRel(RelType type) const {
-  if (type == R_SPARC_64)
+  switch (type) {
+  case R_SPARC_16:
+  case R_SPARC_32:
+  case R_SPARC_64:
+  case R_SPARC_UA16:
+  case R_SPARC_UA32:
+  case R_SPARC_UA64:
     return type;
+  default:
+    break;
+  }
   return R_SPARC_NONE;
 }
 
@@ -92,6 +102,7 @@ int64_t SPARCV9::getImplicitAddend(const uint8_t *buf, RelType type) const {
   switch (type) {
   case R_SPARC_64:
   case R_SPARC_GLOB_DAT:
+  case R_SPARC_RELATIVE:
     return read64be(buf);
   default:
     InternalErr(ctx, buf) << "cannot read addend for relocation " << type;
@@ -99,6 +110,52 @@ int64_t SPARCV9::getImplicitAddend(const uint8_t *buf, RelType type) const {
   }
 }
 
+static bool getSparcAbsRelocPair(RelType type, RelType &aligned,
+                                 RelType &unaligned, uint64_t &alignment) {
+  switch (type) {
+  case R_SPARC_16:
+  case R_SPARC_UA16:
+    aligned = R_SPARC_16;
+    unaligned = R_SPARC_UA16;
+    alignment = 2;
+    return true;
+  case R_SPARC_32:
+  case R_SPARC_UA32:
+    aligned = R_SPARC_32;
+    unaligned = R_SPARC_UA32;
+    alignment = 4;
+    return true;
+  case R_SPARC_64:
+  case R_SPARC_UA64:
+    aligned = R_SPARC_64;
+    unaligned = R_SPARC_UA64;
+    alignment = 8;
+    return true;
+  default:
+    return false;
+  }
+}
+
+void SPARCV9::finalizeDynamicReloc(DynamicReloc &rel) const {
+  RelType aligned = R_SPARC_NONE, unaligned = R_SPARC_NONE;
+  uint64_t alignment = 1;
+  if (!getSparcAbsRelocPair(rel.type, aligned, unaligned, alignment))
+    return;
+
+  rel.type = rel.r_offset % alignment == 0 ? aligned : unaligned;
+  if (!rel.needsDynSymIndex() || rel.sym->isPreemptible)
+    return;
+
+  if (rel.type == R_SPARC_64) {
+    rel.convertToRelative(relativeRel);
+    return;
+  }
+
+  Err(ctx) << "relocation " << rel.type << " at offset " << rel.r_offset
+           << " against non-preemptible symbol " << rel.sym
+           << " cannot be converted to " << relativeRel;
+}
+
 template <class ELFT, class RelTy>
 void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
                               unsigned shard) {
@@ -311,6 +368,7 @@ void SPARCV9::relocate(uint8_t *loc, const Relocation &rel,
     write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff));
     break;
   case R_SPARC_64:
+  case R_SPARC_RELATIVE:
   case R_SPARC_DISP64:
   case R_SPARC_UA64:
     // V-xword64
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 4addf28bbffac..9c707dfa2e51f 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -995,10 +995,17 @@ void RelocScan::processAux(RelExpr expr, RelType type, uint64_t offset,
                     (isa<EhInputSection>(sec) && ctx.arg.emachine != EM_MIPS));
   if (canWrite) {
     RelType rel = ctx.target->getDynRel(type);
-    if (oneof<R_GOT, RE_LOONGARCH_GOT>(expr) ||
-        ((rel == ctx.target->symbolicRel ||
-          (ctx.arg.emachine == EM_AARCH64 && type == R_AARCH64_AUTH_ABS64)) &&
-         !sym.isPreemptible)) {
+    bool useRelative =
+        (rel == ctx.target->symbolicRel ||
+         (ctx.arg.emachine == EM_AARCH64 && type == R_AARCH64_AUTH_ABS64)) &&
+        !sym.isPreemptible;
+    bool isSparcAbsReloc =
+        ctx.arg.emachine == EM_SPARCV9 &&
+        (type == R_SPARC_16 || type == R_SPARC_32 || type == R_SPARC_64 ||
+         type == R_SPARC_UA16 || type == R_SPARC_UA32 || type == R_SPARC_UA64);
+    if (isSparcAbsReloc)
+      useRelative = false;
+    if (oneof<R_GOT, RE_LOONGARCH_GOT>(expr) || useRelative) {
       addRelativeReloc<true>(ctx, *sec, offset, sym, addend, expr, type, shard);
       return;
     }
@@ -1024,6 +1031,8 @@ void RelocScan::processAux(RelExpr expr, RelType type, uint64_t offset,
         }
       }
       ctx.in.relaDyn->addSymbolReloc(rel, *sec, offset, sym, addend, type);
+      if (isSparcAbsReloc && !sym.isPreemptible && ctx.arg.writeAddends)
+        sec->addReloc({expr, type, offset, addend, &sym});
 
       // MIPS ABI turns using of GOT and dynamic relocations inside out.
       // While regular ABI uses dynamic relocations to fill up GOT entries
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 6bac881446fd0..60a89dff2a22b 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1534,6 +1534,7 @@ void RelocationBaseSection::finalizeContents() {
 
 void DynamicReloc::finalize(Ctx &ctx, SymbolTableBaseSection *symt) {
   r_offset = getOffset();
+  ctx.target->finalizeDynamicReloc(*this);
   r_sym = getSymIndex(symt);
   addend = computeAddend(ctx);
   isFinal = true; // Catch errors
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 19d4461348f3f..57820525a7c78 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -439,6 +439,11 @@ class DynamicReloc {
   uint64_t getOffset() const;
   uint32_t getSymIndex(SymbolTableBaseSection *symTab) const;
   bool needsDynSymIndex() const { return isAgainstSymbol; }
+  void convertToRelative(RelType relativeRel) {
+    type = relativeRel;
+    isAgainstSymbol = false;
+    expr = R_ABS;
+  }
 
   /// Computes the addend of the dynamic relocation. Note that this is not the
   /// same as the #addend member variable as it may also include the symbol
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 174e389db155d..3224c02c52e5f 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -22,6 +22,7 @@
 namespace lld {
 namespace elf {
 class Defined;
+class DynamicReloc;
 class InputFile;
 class Symbol;
 template <class RelTy> struct Relocs;
@@ -37,6 +38,7 @@ class TargetInfo {
   virtual RelExpr getRelExpr(RelType type, const Symbol &s,
                              const uint8_t *loc) const = 0;
   virtual RelType getDynRel(RelType type) const { return 0; }
+  virtual void finalizeDynamicReloc(DynamicReloc &rel) const {}
   virtual void writeGotPltHeader(uint8_t *buf) const {}
   virtual void writeGotHeader(uint8_t *buf) const {}
   virtual void writeGotPlt(uint8_t *buf, const Symbol &s) const {}
diff --git a/lld/test/ELF/sparcv9-abs-pic.s b/lld/test/ELF/sparcv9-abs-pic.s
index 7513fcb8ecee6..4e0186b530af4 100644
--- a/lld/test/ELF/sparcv9-abs-pic.s
+++ b/lld/test/ELF/sparcv9-abs-pic.s
@@ -9,15 +9,15 @@
 ## R_SPARC_64 is an absolute relocation type.
 ## In PIC mode, it creates a relative relocation if the symbol is non-preemptable.
 
-# NM: 0000000000300350 d b
+# NM: 0000000000300340 d b
 
 # RELOC:      .rela.dyn {
-# RELOC-NEXT:   0x300350 R_SPARC_RELATIVE - 0x300350
-# RELOC-NEXT:   0x300348 R_SPARC_64 a 0x0
+# RELOC-NEXT:   0x300340 R_SPARC_RELATIVE - 0x300340
+# RELOC-NEXT:   0x300338 R_SPARC_64 a 0x0
 # RELOC-NEXT: }
 
 # HEX:      Contents of section .data:
-# HEX-NEXT: 300348 00000000 00000000 00000000 00300350
+# HEX-NEXT: 300338 00000000 00000000 00000000 00300340
 
 .globl a, b
 .hidden b
diff --git a/lld/test/ELF/sparcv9-ua-dynrel.s b/lld/test/ELF/sparcv9-ua-dynrel.s
new file mode 100644
index 0000000000000..4f58f95852312
--- /dev/null
+++ b/lld/test/ELF/sparcv9-ua-dynrel.s
@@ -0,0 +1,80 @@
+# REQUIRES: sparc
+# RUN: llvm-mc -filetype=obj -triple=sparcv9 %s -o %t.o
+# RUN: ld.lld -shared %t.o -o %t.so
+# RUN: llvm-readelf -r %t.so | FileCheck %s
+
+# RUN: llvm-mc -filetype=obj -triple=sparcv9 --defsym ERR=1 %s -o %t.err.o
+# RUN: not ld.lld -shared %t.err.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR %s
+
+# CHECK:      R_SPARC_RELATIVE
+# CHECK:      R_SPARC_16 {{.*}} external
+# CHECK:      R_SPARC_32 {{.*}} external
+# CHECK:      R_SPARC_64 {{.*}} external
+# CHECK:      R_SPARC_UA16 {{.*}} external
+# CHECK:      R_SPARC_UA32 {{.*}} external
+# CHECK:      R_SPARC_UA64 {{.*}} external
+
+# ERR-DAG: error: relocation R_SPARC_16 at offset {{[0-9]+}} against non-preemptible symbol local cannot be converted to R_SPARC_RELATIVE
+# ERR-DAG: error: relocation R_SPARC_32 at offset {{[0-9]+}} against non-preemptible symbol local cannot be converted to R_SPARC_RELATIVE
+# ERR-DAG: error: relocation R_SPARC_UA64 at offset {{[0-9]+}} against non-preemptible symbol local cannot be converted to R_SPARC_RELATIVE
+
+.data
+.p2align 3
+aligned_local64:
+  .xword 0
+  .reloc aligned_local64, R_SPARC_UA64, local
+
+.p2align 1
+aligned_external16:
+  .half 0
+  .reloc aligned_external16, R_SPARC_UA16, external
+
+.p2align 2
+aligned_external32:
+  .word 0
+  .reloc aligned_external32, R_SPARC_UA32, external
+
+.p2align 3
+aligned_external64:
+  .xword 0
+  .reloc aligned_external64, R_SPARC_UA64, external
+
+.p2align 1
+  .byte 0
+unaligned_external16:
+  .half 0
+  .reloc unaligned_external16, R_SPARC_16, external
+
+.p2align 2
+  .byte 0
+unaligned_external32:
+  .word 0
+  .reloc unaligned_external32, R_SPARC_32, external
+
+.p2align 3
+  .byte 0
+unaligned_external64:
+  .xword 0
+  .reloc unaligned_external64, R_SPARC_64, external
+
+.ifdef ERR
+.p2align 1
+aligned_local16:
+  .half 0
+  .reloc aligned_local16, R_SPARC_UA16, local
+
+.p2align 2
+aligned_local32:
+  .word 0
+  .reloc aligned_local32, R_SPARC_UA32, local
+
+.p2align 3
+  .byte 0
+unaligned_local64:
+  .xword 0
+  .reloc unaligned_local64, R_SPARC_64, local
+.endif
+
+.hidden local
+local:
+  .xword 0

>From 450c7ba7a6beaf1ec264523fa2d7563b47fe9bce Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill at korins.ky>
Date: Mon, 27 Jul 2026 12:09:31 +0200
Subject: [PATCH 2/2] [lld][ELF][SPARC] Support large PLTs

SPARC V9 switches to a block-based PLT layout after the first 32768 ABI
entries. Emit its large entries and apply JMP_SLOT relocations directly
to .plt.

This avoids R_SPARC_WDISP19 overflow when a short PLT entry can no
longer reach .PLT1.
---
 lld/ELF/Arch/SPARCV9.cpp                 | 73 +++++++++++++++++++++++-
 lld/ELF/Symbols.cpp                      |  2 +-
 lld/ELF/SyntheticSections.cpp            | 16 +++---
 lld/ELF/Target.h                         |  5 ++
 lld/ELF/Writer.cpp                       | 10 +++-
 lld/test/ELF/Inputs/sparcv9-large-plt.py |  6 ++
 lld/test/ELF/sparcv9-large-plt.s         | 11 ++++
 lld/test/ELF/sparcv9-plt.s               | 30 +++++-----
 8 files changed, 123 insertions(+), 30 deletions(-)
 create mode 100644 lld/test/ELF/Inputs/sparcv9-large-plt.py
 create mode 100644 lld/test/ELF/sparcv9-large-plt.s

diff --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index eb948cbd366fe..2270673af82a8 100644
--- a/lld/ELF/Arch/SPARCV9.cpp
+++ b/lld/ELF/Arch/SPARCV9.cpp
@@ -11,6 +11,8 @@
 #include "SyntheticSections.h"
 #include "Target.h"
 #include "llvm/Support/Endian.h"
+#include <algorithm>
+#include <cassert>
 
 using namespace llvm;
 using namespace llvm::object;
@@ -20,6 +22,13 @@ using namespace lld;
 using namespace lld::elf;
 
 namespace {
+constexpr uint32_t firstLargePltIndex = 32768 - 4;
+constexpr uint32_t pltLargeEntriesPerBlock = 160;
+constexpr uint32_t pltLargeCodeSize = 24;
+constexpr uint32_t pltLargePointerSize = 8;
+constexpr uint32_t pltLargeBlockSize =
+    pltLargeEntriesPerBlock * (pltLargeCodeSize + pltLargePointerSize);
+
 class SPARCV9 final : public TargetInfo {
 public:
   SPARCV9(Ctx &);
@@ -29,8 +38,11 @@ class SPARCV9 final : public TargetInfo {
   int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
   void finalizeDynamicReloc(DynamicReloc &rel) const override;
   void writeGotHeader(uint8_t *buf) const override;
+  uint64_t getPltEntryOffset(uint32_t pltIdx,
+                             uint64_t headerSize) const override;
   void writePlt(uint8_t *buf, const Symbol &sym,
                 uint64_t pltEntryAddr) const override;
+  void finalizePlt(uint8_t *buf) const override;
   template <class ELFT, class RelTy>
   void scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
                        unsigned shard);
@@ -39,6 +51,9 @@ class SPARCV9 final : public TargetInfo {
   }
   void relocate(uint8_t *loc, const Relocation &rel,
                 uint64_t val) const override;
+
+private:
+  uint64_t getLargePltPointerOffset(uint32_t pltIdx) const;
 };
 } // namespace
 
@@ -137,6 +152,15 @@ static bool getSparcAbsRelocPair(RelType type, RelType &aligned,
 }
 
 void SPARCV9::finalizeDynamicReloc(DynamicReloc &rel) const {
+  if (rel.type == R_SPARC_JMP_SLOT && rel.inputSec == ctx.in.plt.get()) {
+    uint32_t pltIdx = rel.sym->getPltIdx(ctx);
+    if (pltIdx >= firstLargePltIndex) {
+      uint64_t off = getPltEntryOffset(pltIdx, ctx.in.plt->headerSize);
+      rel.r_offset = ctx.in.plt->getVA() + getLargePltPointerOffset(pltIdx);
+      rel.addend = -static_cast<int64_t>(ctx.in.plt->getVA() + off + 4);
+    }
+  }
+
   RelType aligned = R_SPARC_NONE, unaligned = R_SPARC_NONE;
   uint64_t alignment = 1;
   if (!getSparcAbsRelocPair(rel.type, aligned, unaligned, alignment))
@@ -462,8 +486,47 @@ void SPARCV9::writeGotHeader(uint8_t *buf) const {
   write64be(buf, ctx.in.dynamic->getVA());
 }
 
-void SPARCV9::writePlt(uint8_t *buf, const Symbol & /*sym*/,
+uint64_t SPARCV9::getPltEntryOffset(uint32_t pltIdx,
+                                    uint64_t headerSize) const {
+  if (pltIdx < firstLargePltIndex)
+    return headerSize + uint64_t{pltIdx} * pltEntrySize;
+
+  uint32_t largeIdx = pltIdx - firstLargePltIndex;
+  return headerSize + uint64_t{firstLargePltIndex} * pltEntrySize +
+         largeIdx / pltLargeEntriesPerBlock * pltLargeBlockSize +
+         largeIdx % pltLargeEntriesPerBlock * pltLargeCodeSize;
+}
+
+uint64_t SPARCV9::getLargePltPointerOffset(uint32_t pltIdx) const {
+  assert(pltIdx >= firstLargePltIndex);
+  uint32_t largeIdx = pltIdx - firstLargePltIndex;
+  uint32_t entriesBeforeBlock =
+      largeIdx / pltLargeEntriesPerBlock * pltLargeEntriesPerBlock;
+  uint32_t indexInBlock = largeIdx % pltLargeEntriesPerBlock;
+  uint64_t largeEntries = ctx.in.plt->getNumEntries() - firstLargePltIndex;
+  uint64_t entriesInBlock = std::min<uint64_t>(
+      pltLargeEntriesPerBlock, largeEntries - entriesBeforeBlock);
+  uint64_t blockOffset = getPltEntryOffset(
+      firstLargePltIndex + entriesBeforeBlock, ctx.in.plt->headerSize);
+
+  return blockOffset + entriesInBlock * pltLargeCodeSize +
+         indexInBlock * pltLargePointerSize;
+}
+
+void SPARCV9::writePlt(uint8_t *buf, const Symbol &sym,
                        uint64_t pltEntryAddr) const {
+  uint64_t off = pltEntryAddr - ctx.in.plt->getVA();
+  if (sym.getPltIdx(ctx) >= firstLargePltIndex) {
+    uint64_t ptrOff = getLargePltPointerOffset(sym.getPltIdx(ctx));
+    write32be(buf, 0x8a10000f);
+    write32be(buf + 4, 0x40000002);
+    write32be(buf + 8, 0x01000000);
+    write32be(buf + 12, 0xc25be000 | ((ptrOff - (off + 4)) & 0x1fff));
+    write32be(buf + 16, 0x83c3c001);
+    write32be(buf + 20, 0x9e100005);
+    return;
+  }
+
   const uint8_t pltData[] = {
       0x03, 0x00, 0x00, 0x00, // sethi   (. - .PLT0), %g1
       0x30, 0x68, 0x00, 0x00, // ba,a    %xcc, .PLT1
@@ -476,9 +539,15 @@ void SPARCV9::writePlt(uint8_t *buf, const Symbol & /*sym*/,
   };
   memcpy(buf, pltData, sizeof(pltData));
 
-  uint64_t off = pltEntryAddr - ctx.in.plt->getVA();
   relocateNoSym(buf, R_SPARC_22, off);
   relocateNoSym(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize));
 }
 
+void SPARCV9::finalizePlt(uint8_t *buf) const {
+  for (uint32_t i = firstLargePltIndex; i < ctx.in.plt->getNumEntries(); ++i) {
+    uint64_t off = getPltEntryOffset(i, ctx.in.plt->headerSize);
+    write64be(buf + getLargePltPointerOffset(i), -(off + 4));
+  }
+}
+
 void elf::setSPARCV9TargetInfo(Ctx &ctx) { ctx.target.reset(new SPARCV9(ctx)); }
diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index 951041466cec5..d8beac37130a1 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -185,7 +185,7 @@ uint64_t Symbol::getGotPltOffset(Ctx &ctx) const {
 uint64_t Symbol::getPltOffset(Ctx &ctx) const {
   if (isInIplt)
     return getPltIdx(ctx) * ctx.target->ipltEntrySize;
-  return ctx.in.plt->headerSize + getPltIdx(ctx) * ctx.target->pltEntrySize;
+  return ctx.target->getPltEntryOffset(getPltIdx(ctx), ctx.in.plt->headerSize);
 }
 
 uint64_t Symbol::getPltVA(Ctx &ctx) const {
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 60a89dff2a22b..5772a4a38c0c9 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -2429,20 +2429,21 @@ PltSection::PltSection(Ctx &ctx)
 
   // The PLT needs to be writable on SPARC as the dynamic linker will
   // modify the instructions in the PLT entries.
-  if (ctx.arg.emachine == EM_SPARCV9)
+  if (ctx.arg.emachine == EM_SPARCV9) {
     this->flags |= SHF_WRITE;
+    addralign = 256;
+  }
 }
 
 void PltSection::writeTo(uint8_t *buf) {
   // At beginning of PLT, we have code to call the dynamic
   // linker to resolve dynsyms at runtime. Write such code.
   ctx.target->writePltHeader(buf);
-  size_t off = headerSize;
-
   for (const Symbol *sym : entries) {
+    size_t off = sym->getPltOffset(ctx);
     ctx.target->writePlt(buf + off, *sym, getVA() + off);
-    off += ctx.target->pltEntrySize;
   }
+  ctx.target->finalizePlt(buf);
 }
 
 void PltSection::addEntry(Symbol &sym) {
@@ -2465,11 +2466,8 @@ bool PltSection::isNeeded() const {
 void PltSection::addSymbols() {
   ctx.target->addPltHeaderSymbols(*this);
 
-  size_t off = headerSize;
-  for (size_t i = 0; i < entries.size(); ++i) {
-    ctx.target->addPltSymbols(*this, off);
-    off += ctx.target->pltEntrySize;
-  }
+  for (const Symbol *sym : entries)
+    ctx.target->addPltSymbols(*this, sym->getPltOffset(ctx));
 }
 
 IpltSection::IpltSection(Ctx &ctx)
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 3224c02c52e5f..4a9bbb3053735 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -50,8 +50,13 @@ class TargetInfo {
   // they are called. This function writes that code.
   virtual void writePltHeader(uint8_t *buf) const {}
 
+  virtual uint64_t getPltEntryOffset(uint32_t pltIdx,
+                                     uint64_t headerSize) const {
+    return headerSize + uint64_t{pltIdx} * pltEntrySize;
+  }
   virtual void writePlt(uint8_t *buf, const Symbol &sym,
                         uint64_t pltEntryAddr) const {}
+  virtual void finalizePlt(uint8_t *buf) const {}
   virtual void writeIplt(uint8_t *buf, const Symbol &sym,
                          uint64_t pltEntryAddr) const {
     // All but PPC32 and PPC64 use the same format for .plt and .iplt entries.
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 8cf88cd99cf8a..1a685bae1e3f6 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -602,7 +602,7 @@ static bool isRelroSection(Ctx &ctx, const OutputSection *sec) {
   // by default resolved lazily, so we usually cannot put it into RELRO.
   // However, if "-z now" is given, the lazy symbol resolution is
   // disabled, which enables us to put it into RELRO.
-  if (sec == ctx.in.gotPlt->getParent())
+  if (ctx.target->usesGotPlt && sec == ctx.in.gotPlt->getParent())
     return ctx.arg.zNow;
 
   if (ctx.in.relroPadding && sec == ctx.in.relroPadding->getParent())
@@ -844,10 +844,14 @@ template <class ELFT> void Writer<ELFT>::setReservedSymbolSections() {
   if (ctx.sym.globalOffsetTable) {
     // The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention usually
     // to the start of the .got or .got.plt section.
-    InputSection *sec = ctx.in.gotPlt.get();
-    if (!ctx.target->gotBaseSymInGotPlt)
+    InputSection *sec;
+    if (ctx.target->gotBaseSymInGotPlt) {
+      assert(ctx.target->usesGotPlt);
+      sec = ctx.in.gotPlt.get();
+    } else {
       sec = ctx.in.mipsGot ? cast<InputSection>(ctx.in.mipsGot.get())
                            : cast<InputSection>(ctx.in.got.get());
+    }
     ctx.sym.globalOffsetTable->section = sec;
   }
 
diff --git a/lld/test/ELF/Inputs/sparcv9-large-plt.py b/lld/test/ELF/Inputs/sparcv9-large-plt.py
new file mode 100644
index 0000000000000..84f74af5e96e4
--- /dev/null
+++ b/lld/test/ELF/Inputs/sparcv9-large-plt.py
@@ -0,0 +1,6 @@
+import sys
+
+count = int(sys.argv[1])
+for i in range(count):
+    print(f"call foo{i}")
+    print(" nop")
diff --git a/lld/test/ELF/sparcv9-large-plt.s b/lld/test/ELF/sparcv9-large-plt.s
new file mode 100644
index 0000000000000..97d1822d30ace
--- /dev/null
+++ b/lld/test/ELF/sparcv9-large-plt.s
@@ -0,0 +1,11 @@
+# REQUIRES: sparc
+# RUN: %python %S/Inputs/sparcv9-large-plt.py 32766 > %t.s
+# RUN: llvm-mc --position-independent -filetype=obj -triple=sparcv9 %t.s -o %t.o
+# RUN: ld.lld -shared %t.o -o %t.so
+# RUN: llvm-readelf -SW -r %t.so | FileCheck %s
+
+# CHECK-NOT: .got.plt
+# CHECK:     .plt PROGBITS [[#%x,PLT:]] {{[0-9a-f]+}} 100040
+# CHECK-NOT: .got.plt
+# CHECK:     [[#%x,PLT + 0x100030]] {{.*}} R_SPARC_JMP_SLOT {{.*}} foo32764
+# CHECK:     [[#%x,PLT + 0x100038]] {{.*}} R_SPARC_JMP_SLOT {{.*}} foo32765
diff --git a/lld/test/ELF/sparcv9-plt.s b/lld/test/ELF/sparcv9-plt.s
index b63c57a6574fa..4f1616aa20892 100644
--- a/lld/test/ELF/sparcv9-plt.s
+++ b/lld/test/ELF/sparcv9-plt.s
@@ -11,38 +11,38 @@
 ## so .plt is writable and DT_PLTGOT points at it, and .rela.plt names .plt in
 ## its sh_info.
 # CHECK: [ 5] .rela.plt RELA {{[0-9a-f]+}} {{[0-9a-f]+}} 000048 18 AI 1 7 8
-# CHECK: [ 7] .plt PROGBITS 0000000000300320 {{[0-9a-f]+}} 0000e0 00 WAX
-# CHECK: (PLTGOT) 0x300320
+# CHECK: [ 7] .plt PROGBITS 0000000000300400 {{[0-9a-f]+}} 0000e0 00 WAX 0 0 256
+# CHECK: (PLTGOT) 0x300400
 
 ## R_SPARC_JMP_SLOT applies to the PLT entry rather than to a .got.plt slot.
 # CHECK:      Relocation section '.rela.plt' {{.*}} contains 3 entries:
-# CHECK:      00000000003003a0 {{.*}} R_SPARC_JMP_SLOT {{.*}} weak + 0
-# CHECK-NEXT: 00000000003003c0 {{.*}} R_SPARC_JMP_SLOT {{.*}} bar + 0
-# CHECK-NEXT: 00000000003003e0 {{.*}} R_SPARC_JMP_SLOT {{.*}} foo + 0
+# CHECK:      0000000000300480 {{.*}} R_SPARC_JMP_SLOT {{.*}} weak + 0
+# CHECK-NEXT: 00000000003004a0 {{.*}} R_SPARC_JMP_SLOT {{.*}} bar + 0
+# CHECK-NEXT: 00000000003004c0 {{.*}} R_SPARC_JMP_SLOT {{.*}} foo + 0
 
 # CHECK:      0000000000000000 0 FUNC WEAK   DEFAULT UND weak
 # CHECK-NEXT: 0000000000000000 0 FUNC GLOBAL DEFAULT UND bar
-# CHECK-NEXT: 00000000003003e0 0 FUNC GLOBAL DEFAULT UND foo
+# CHECK-NEXT: 00000000003004c0 0 FUNC GLOBAL DEFAULT UND foo
 
 # DIS:      <_start>:
-# DIS-NEXT:   call 0x3003c0
+# DIS-NEXT:   call 0x3004a0
 # DIS-NEXT:   nop
-# DIS-NEXT:   call 0x3003a0
+# DIS-NEXT:   call 0x300480
 # DIS-NEXT:   nop
 
 ## The four reserved entries are left zeroed, as GNU ld leaves them. The
 ## dynamic linker writes the resolver code into .PLT0 and .PLT1 at startup.
-# DIS:          0000000000300320 <.plt>:
+# DIS:          0000000000300400 <.plt>:
 # DIS-COUNT-32:   unimp 0
 
 ## Each entry puts its own offset from .plt into %g1 and branches to .PLT1,
 ## which the dynamic linker redirects to the resolver.
-# DIS-NEXT:   3003a0: 03 00 00 80 sethi 128, %g1
-# DIS-NEXT:           30 6f ff e7 ba,a %xcc, 0x300340
-# DIS:        3003c0: 03 00 00 a0 sethi 160, %g1
-# DIS-NEXT:           30 6f ff df ba,a %xcc, 0x300340
-# DIS:        3003e0: 03 00 00 c0 sethi 192, %g1
-# DIS-NEXT:           30 6f ff d7 ba,a %xcc, 0x300340
+# DIS-NEXT:   300480: 03 00 00 80 sethi 128, %g1
+# DIS-NEXT:           30 6f ff e7 ba,a %xcc, 0x300420
+# DIS:        3004a0: 03 00 00 a0 sethi 160, %g1
+# DIS-NEXT:           30 6f ff df ba,a %xcc, 0x300420
+# DIS:        3004c0: 03 00 00 c0 sethi 192, %g1
+# DIS-NEXT:           30 6f ff d7 ba,a %xcc, 0x300420
 
 #--- a.s
 .globl _start



More information about the llvm-commits mailing list