Skip to content

Enough LLD changes to bootstrap OpenBSD on sparc64#207609

Open
catap wants to merge 6 commits into
llvm:mainfrom
catap:openbsd-sparc64
Open

Enough LLD changes to bootstrap OpenBSD on sparc64#207609
catap wants to merge 6 commits into
llvm:mainfrom
catap:openbsd-sparc64

Conversation

@catap

@catap catap commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

This PR consumes #137919, adds my bits from https://github.com/catap/OpenBSD-src/tree/sparc64 which I had used to actually build OpenBSD by using LLVM toolchain.

It also contains a bit of tests.

I do not tried to build OpenBSD by exactly this branch, but it looks close enough and I had resolved only one conflicts which was introduced by 3be67c7

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-lld-elf

Author: Kirill A. Korinsky (catap)

Changes

This PR consumes #137919, adds my bits from https://github.com/catap/OpenBSD-src/tree/sparc64 which I had used to actually build OpenBSD by using LLVM toolchain.

It also contains a bit of tests.

I do not tried to build OpenBSD by exactly this branch, but it looks close enough and I had resolved only one conflicts which was introduced by 3be67c7


Patch is 31.03 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/207609.diff

18 Files Affected:

  • (modified) lld/ELF/Arch/SPARCV9.cpp (+262-34)
  • (modified) lld/ELF/Driver.cpp (+4-3)
  • (modified) lld/ELF/InputSection.cpp (+1)
  • (modified) lld/ELF/Relocations.cpp (+20-13)
  • (modified) lld/ELF/Relocations.h (+1)
  • (modified) lld/ELF/Symbols.cpp (+6)
  • (modified) lld/ELF/Symbols.h (+1)
  • (modified) lld/ELF/SyntheticSections.cpp (+27-6)
  • (modified) lld/ELF/Target.cpp (+6)
  • (modified) lld/ELF/Target.h (+6-1)
  • (modified) lld/ELF/Writer.cpp (+7-3)
  • (added) lld/test/ELF/sparcv9-got-header.s (+11)
  • (added) lld/test/ELF/sparcv9-gotdata-relax.s (+37)
  • (added) lld/test/ELF/sparcv9-plt.s (+23)
  • (added) lld/test/ELF/sparcv9-rela.s (+14)
  • (added) lld/test/ELF/sparcv9-reloc-additional.s (+43)
  • (added) lld/test/ELF/sparcv9-tls-dynamic.s (+38)
  • (added) lld/test/ELF/sparcv9-ua64-dynrel.s (+29)
diff --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index 405be69a8f572..1847fb295ab41 100644
--- a/lld/ELF/Arch/SPARCV9.cpp
+++ b/lld/ELF/Arch/SPARCV9.cpp
@@ -6,7 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "InputFiles.h"
 #include "RelocScan.h"
+#include "SymbolTable.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
@@ -25,6 +27,8 @@ class SPARCV9 final : public TargetInfo {
   SPARCV9(Ctx &);
   RelExpr getRelExpr(RelType type, const Symbol &s,
                      const uint8_t *loc) const override;
+  RelType getDynRel(RelType type) const override;
+  void writeGotHeader(uint8_t *buf) const override;
   void writePlt(uint8_t *buf, const Symbol &sym,
                 uint64_t pltEntryAddr) const override;
   template <class ELFT, class RelTy>
@@ -34,6 +38,12 @@ class SPARCV9 final : public TargetInfo {
   }
   void relocate(uint8_t *loc, const Relocation &rel,
                 uint64_t val) const override;
+  void finalizeRelocScan() override;
+  RelExpr adjustGotOffExpr(RelType type, const Symbol &sym, int64_t addend,
+                           const uint8_t *loc) const override;
+
+private:
+  void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const;
 };
 } // namespace
 
@@ -42,9 +52,15 @@ SPARCV9::SPARCV9(Ctx &ctx) : TargetInfo(ctx) {
   gotRel = R_SPARC_GLOB_DAT;
   pltRel = R_SPARC_JMP_SLOT;
   relativeRel = R_SPARC_RELATIVE;
+  iRelativeRel = R_SPARC_IRELATIVE;
   symbolicRel = R_SPARC_64;
+  tlsGotRel = R_SPARC_TLS_TPOFF64;
+  tlsModuleIndexRel = R_SPARC_TLS_DTPMOD64;
+  tlsOffsetRel = R_SPARC_TLS_DTPOFF64;
+  gotHeaderEntriesNum = 1;
   pltEntrySize = 32;
   pltHeaderSize = 4 * pltEntrySize;
+  usesGotPlt = false;
 
   defaultCommonPageSize = 8192;
   defaultMaxPageSize = 0x100000;
@@ -72,6 +88,12 @@ RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
   }
 }
 
+RelType SPARCV9::getDynRel(RelType type) const {
+  if (type == symbolicRel || type == R_SPARC_UA64)
+    return type;
+  return R_SPARC_NONE;
+}
+
 template <class ELFT, class RelTy>
 void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
   RelocScan rs(ctx, &sec);
@@ -89,21 +111,33 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
     RelExpr expr;
     switch (type) {
     case R_SPARC_NONE:
+    case R_SPARC_TLS_GD_ADD:
+    case R_SPARC_TLS_LDM_ADD:
+    case R_SPARC_TLS_LDO_ADD:
+    case R_SPARC_TLS_IE_LD:
+    case R_SPARC_TLS_IE_LDX:
+    case R_SPARC_TLS_IE_ADD:
       continue;
 
     // Absolute relocations:
+    case R_SPARC_8:
+    case R_SPARC_16:
     case R_SPARC_32:
+    case R_SPARC_HI22:
+    case R_SPARC_13:
+    case R_SPARC_LO10:
     case R_SPARC_UA32:
     case R_SPARC_64:
-    case R_SPARC_UA64:
-    case R_SPARC_H44:
-    case R_SPARC_M44:
-    case R_SPARC_L44:
     case R_SPARC_HH22:
     case R_SPARC_HM10:
     case R_SPARC_LM22:
-    case R_SPARC_HI22:
-    case R_SPARC_LO10:
+    case R_SPARC_HIX22:
+    case R_SPARC_LOX10:
+    case R_SPARC_H44:
+    case R_SPARC_M44:
+    case R_SPARC_L44:
+    case R_SPARC_UA64:
+    case R_SPARC_UA16:
       expr = R_ABS;
       break;
 
@@ -111,20 +145,38 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
     case R_SPARC_WPLT30:
       rs.processR_PLT_PC(type, offset, addend, sym);
       continue;
+    case R_SPARC_TLS_GD_CALL:
+    case R_SPARC_TLS_LDM_CALL:
+      sec.addReloc({R_PLT_PC, type, offset, addend, &sym});
+      continue;
 
     // PC-relative relocations:
-    case R_SPARC_PC10:
-    case R_SPARC_PC22:
+    case R_SPARC_DISP8:
+    case R_SPARC_DISP16:
     case R_SPARC_DISP32:
     case R_SPARC_WDISP30:
+    case R_SPARC_WDISP22:
+    case R_SPARC_PC10:
+    case R_SPARC_PC22:
+    case R_SPARC_WDISP16:
+    case R_SPARC_WDISP19:
+    case R_SPARC_DISP64:
       rs.processR_PC(type, offset, addend, sym);
       continue;
 
     // GOT relocations:
     case R_SPARC_GOT10:
+    case R_SPARC_GOT13:
     case R_SPARC_GOT22:
+    case R_SPARC_GOTDATA_OP_HIX22:
+    case R_SPARC_GOTDATA_OP_LOX10:
+    case R_SPARC_GOTDATA_OP:
       expr = R_GOT_OFF;
       break;
+    case R_SPARC_GOTDATA_HIX22:
+    case R_SPARC_GOTDATA_LOX10:
+      expr = R_GOTREL;
+      break;
 
     // TLS LE relocations:
     case R_SPARC_TLS_LE_HIX22:
@@ -133,6 +185,25 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
         continue;
       expr = R_TPREL;
       break;
+    case R_SPARC_TLS_GD_HI22:
+    case R_SPARC_TLS_GD_LO10:
+      sym.setFlags(NEEDS_TLSGD);
+      sec.addReloc({R_TLSGD_GOT, type, offset, addend, &sym});
+      continue;
+    case R_SPARC_TLS_LDM_HI22:
+    case R_SPARC_TLS_LDM_LO10:
+      ctx.needsTlsLd.store(true, std::memory_order_relaxed);
+      sec.addReloc({R_TLSLD_GOT, type, offset, addend, &sym});
+      continue;
+    case R_SPARC_TLS_LDO_HIX22:
+    case R_SPARC_TLS_LDO_LOX10:
+      expr = R_DTPREL;
+      break;
+    case R_SPARC_TLS_IE_HI22:
+    case R_SPARC_TLS_IE_LO10:
+      sym.setFlags(NEEDS_TLSIE);
+      sec.addReloc({R_GOT, type, offset, addend, &sym});
+      continue;
 
     default:
       Err(ctx) << getErrorLoc(ctx, sec.content().data() + offset)
@@ -146,71 +217,139 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
 
 void SPARCV9::relocate(uint8_t *loc, const Relocation &rel,
                        uint64_t val) const {
+  switch (rel.expr) {
+  case R_RELAX_GOT_OFF:
+    return relaxGot(loc, rel, val);
+  default:
+    break;
+  }
+
   switch (rel.type) {
+  case R_SPARC_8:
+    // V-byte8
+    checkUInt(ctx, loc, val, 8, rel);
+    *loc = val;
+    break;
+  case R_SPARC_16:
+  case R_SPARC_UA16:
+    // V-half16
+    checkUInt(ctx, loc, val, 16, rel);
+    write16be(loc, val);
+    break;
   case R_SPARC_32:
   case R_SPARC_UA32:
     // V-word32
     checkUInt(ctx, loc, val, 32, rel);
     write32be(loc, val);
     break;
+  case R_SPARC_DISP8:
+    // V-byte8
+    checkIntUInt(ctx, loc, val, 8, rel);
+    *loc = val;
+    break;
+  case R_SPARC_DISP16:
+    // V-half16
+    checkIntUInt(ctx, loc, val, 16, rel);
+    write16be(loc, val);
+    break;
   case R_SPARC_DISP32:
     // V-disp32
-    checkInt(ctx, loc, val, 32, rel);
+    checkIntUInt(ctx, loc, val, 32, rel);
     write32be(loc, val);
     break;
   case R_SPARC_WDISP30:
   case R_SPARC_WPLT30:
+  case R_SPARC_TLS_GD_CALL:
+  case R_SPARC_TLS_LDM_CALL:
     // V-disp30
-    checkInt(ctx, loc, val, 32, rel);
+    checkIntUInt(ctx, loc, val, 32, rel);
     write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff));
     break;
+  case R_SPARC_WDISP22:
+    // V-disp22
+    checkIntUInt(ctx, loc, val, 24, rel);
+    write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 2) & 0x003fffff));
+    break;
+  case R_SPARC_HI22:
+    // V-imm22
+    checkUInt(ctx, loc, val, 32, rel);
+    write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
+    break;
   case R_SPARC_22:
     // V-imm22
     checkUInt(ctx, loc, val, 22, rel);
     write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff));
     break;
+  case R_SPARC_13:
+  case R_SPARC_GOT13:
+    // V-simm13
+    checkIntUInt(ctx, loc, val, 13, rel);
+    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x00001fff));
+    break;
+  case R_SPARC_LO10:
+  case R_SPARC_GOT10:
+  case R_SPARC_PC10:
+  case R_SPARC_TLS_GD_LO10:
+  case R_SPARC_TLS_LDM_LO10:
+  case R_SPARC_TLS_IE_LO10:
+    // T-simm13
+    write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff));
+    break;
   case R_SPARC_GOT22:
-  case R_SPARC_PC22:
   case R_SPARC_LM22:
+  case R_SPARC_TLS_GD_HI22:
+  case R_SPARC_TLS_LDM_HI22:
+  case R_SPARC_TLS_LDO_HIX22:
+  case R_SPARC_TLS_IE_HI22:
     // T-imm22
     write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
     break;
-  case R_SPARC_HI22:
-    // V-imm22
-    checkUInt(ctx, loc, val >> 10, 22, rel);
+  case R_SPARC_PC22:
+    // V-disp22
+    checkIntUInt(ctx, loc, val, 32, rel);
     write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
     break;
-  case R_SPARC_WDISP19:
-    // V-disp19
-    checkInt(ctx, loc, val, 21, rel);
-    write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff));
-    break;
-  case R_SPARC_GOT10:
-  case R_SPARC_PC10:
-    // T-simm10
-    write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff));
-    break;
-  case R_SPARC_LO10:
-    // T-simm13
-    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff));
-    break;
   case R_SPARC_64:
+  case R_SPARC_DISP64:
   case R_SPARC_UA64:
     // V-xword64
     write64be(loc, val);
     break;
   case R_SPARC_HH22:
     // V-imm22
-    checkUInt(ctx, loc, val >> 42, 22, rel);
     write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 42) & 0x003fffff));
     break;
   case R_SPARC_HM10:
     // T-simm13
-    write32be(loc, (read32be(loc) & ~0x00001fff) | ((val >> 32) & 0x000003ff));
+    write32be(loc, (read32be(loc) & ~0x000003ff) | ((val >> 32) & 0x000003ff));
+    break;
+  case R_SPARC_WDISP16:
+    // V-d2/disp14
+    checkIntUInt(ctx, loc, val, 18, rel);
+    write32be(loc, (read32be(loc) & ~0x0303fff) |
+                       (((val >> 2) & 0xc000) << 6) |
+                       ((val >> 2) & 0x00003fff));
+    break;
+  case R_SPARC_WDISP19:
+    // V-disp19
+    checkIntUInt(ctx, loc, val, 21, rel);
+    write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff));
+    break;
+  case R_SPARC_HIX22:
+    // V-imm22
+    checkUInt(ctx, loc, ~val, 32, rel);
+    write32be(loc, (read32be(loc) & ~0x003fffff) | ((~val >> 10) & 0x003fffff));
+    break;
+  case R_SPARC_LOX10:
+  case R_SPARC_TLS_LE_LOX10:
+  case R_SPARC_GOTDATA_LOX10:
+  case R_SPARC_GOTDATA_OP_LOX10:
+    // T-simm13
+    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | 0x1c00);
     break;
   case R_SPARC_H44:
     // V-imm22
-    checkUInt(ctx, loc, val >> 22, 22, rel);
+    checkUInt(ctx, loc, val, 44, rel);
     write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 22) & 0x003fffff));
     break;
   case R_SPARC_M44:
@@ -219,21 +358,110 @@ void SPARCV9::relocate(uint8_t *loc, const Relocation &rel,
     break;
   case R_SPARC_L44:
     // T-imm13
-    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x00000fff));
+    write32be(loc, (read32be(loc) & ~0x00000fff) | (val & 0x00000fff));
+    break;
+  case R_SPARC_TLS_GD_ADD:
+  case R_SPARC_TLS_LDM_ADD:
+  case R_SPARC_TLS_LDO_ADD:
+  case R_SPARC_TLS_IE_LD:
+  case R_SPARC_TLS_IE_LDX:
+  case R_SPARC_TLS_IE_ADD:
     break;
   case R_SPARC_TLS_LE_HIX22:
     // T-imm22
     write32be(loc, (read32be(loc) & ~0x003fffff) | ((~val >> 10) & 0x003fffff));
     break;
-  case R_SPARC_TLS_LE_LOX10:
+  case R_SPARC_TLS_LDO_LOX10:
     // T-simm13
-    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | 0x1C00);
+    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff));
+    break;
+  case R_SPARC_GOTDATA_HIX22:
+    // V-imm22
+    checkUInt(ctx, loc, ((int64_t)val < 0 ? ~val : val), 32, rel);
+    write32be(loc, (read32be(loc) & ~0x003fffff) |
+                       ((((int64_t)val < 0 ? ~val : val) >> 10) & 0x003fffff));
+    break;
+  case R_SPARC_GOTDATA_OP_HIX22:
+    // T-imm22
+    write32be(loc, (read32be(loc) & ~0x003fffff) |
+                       ((((int64_t)val < 0 ? ~val : val) >> 10) & 0x003fffff));
+    break;
+  case R_SPARC_GOTDATA_OP:
     break;
   default:
     llvm_unreachable("unknown relocation");
   }
 }
 
+RelExpr SPARCV9::adjustGotOffExpr(RelType type, const Symbol &sym,
+                                  int64_t addend, const uint8_t *loc) const {
+  switch (type) {
+  case R_SPARC_GOTDATA_OP_HIX22:
+  case R_SPARC_GOTDATA_OP_LOX10:
+  case R_SPARC_GOTDATA_OP:
+    if (sym.isLocal())
+      return R_RELAX_GOT_OFF;
+    [[fallthrough]];
+  default:
+    return R_GOT_OFF;
+  }
+}
+
+void SPARCV9::relaxGot(uint8_t *loc, const Relocation &rel,
+                       uint64_t val) const {
+  switch (rel.type) {
+  case R_SPARC_GOTDATA_OP_HIX22:
+    // T-imm22
+    write32be(loc, (read32be(loc) & ~0x003fffff) |
+                       ((((int64_t)val < 0 ? ~val : val) >> 10) & 0x003fffff));
+    break;
+  case R_SPARC_GOTDATA_OP_LOX10:
+    // T-imm13
+    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) |
+                       ((int64_t)val < 0 ? 0x1c00 : 0));
+    break;
+  case R_SPARC_GOTDATA_OP:
+    // ldx [%rs1 + %rs2], %rd -> add %rs1, %rs2, %rd
+    write32be(loc, (read32be(loc) & 0x3e07c01f) | 0x80000000);
+    break;
+  default:
+    llvm_unreachable("unknown relocation");
+  }
+}
+
+void SPARCV9::writeGotHeader(uint8_t *buf) const {
+  write64be(buf, ctx.in.dynamic->getVA());
+}
+
+static bool isTlsCall(RelType type) {
+  return type == R_SPARC_TLS_GD_CALL || type == R_SPARC_TLS_LDM_CALL;
+}
+
+void SPARCV9::finalizeRelocScan() {
+  Symbol *tga = nullptr;
+
+  for (ELFFileBase *file : ctx.objectFiles) {
+    for (InputSectionBase *s : file->getSections()) {
+      auto *isec = dyn_cast_or_null<InputSection>(s);
+      if (!isec || !isec->isLive())
+        continue;
+      for (Relocation &rel : isec->relocs()) {
+        if (rel.expr != R_PLT_PC || !isTlsCall(rel.type))
+          continue;
+        if (!tga) {
+          tga = ctx.symtab->addSymbol(Undefined{ctx.internalFile,
+                                                "__tls_get_addr", STB_GLOBAL,
+                                                STV_DEFAULT, STT_FUNC});
+          tga->isUsedInRegularObj = true;
+          tga->isPreemptible = true;
+          tga->setFlags(NEEDS_PLT | USED);
+        }
+        rel.sym = tga;
+      }
+    }
+  }
+}
+
 void SPARCV9::writePlt(uint8_t *buf, const Symbol & /*sym*/,
                        uint64_t pltEntryAddr) const {
   const uint8_t pltData[] = {
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 7ec7dfcae6bca..a2552a1ae1ab7 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1318,9 +1318,10 @@ static SmallVector<StringRef, 0> getSymbolOrderingFile(Ctx &ctx,
 
 static bool getIsRela(Ctx &ctx, opt::InputArgList &args) {
   // The psABI specifies the default relocation entry format.
-  bool rela = is_contained({EM_AARCH64, EM_AMDGPU, EM_HEXAGON, EM_LOONGARCH,
-                            EM_PPC, EM_PPC64, EM_RISCV, EM_S390, EM_X86_64},
-                           ctx.arg.emachine);
+  bool rela =
+      is_contained({EM_AARCH64, EM_AMDGPU, EM_HEXAGON, EM_LOONGARCH, EM_PPC,
+                    EM_PPC64, EM_RISCV, EM_S390, EM_SPARCV9, EM_X86_64},
+                   ctx.arg.emachine);
   // If -z rel or -z rela is specified, use the last option.
   for (auto *arg : args.filtered(OPT_z)) {
     StringRef s(arg->getValue());
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index c78800787d27e..c26425bbc4a7b 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -833,6 +833,7 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
   case R_GOTPLTONLY_PC:
     return ctx.in.gotPlt->getVA() + a - p;
   case R_GOTREL:
+  case R_RELAX_GOT_OFF:
     return r.sym->getVA(ctx, a) - ctx.in.got->getVA();
   case R_GOTPLTREL:
     return r.sym->getVA(ctx, a) - ctx.in.gotPlt->getVA();
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 0d45236e6d11e..cd8d3634e9d33 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -145,7 +145,7 @@ bool lld::elf::needsGot(RelExpr expr) {
 static bool isRelExpr(RelExpr expr) {
   return oneof<R_PC, R_GOTREL, R_GOTPLTREL, RE_ARM_PCA, RE_MIPS_GOTREL,
                RE_PPC64_CALL, RE_AARCH64_PAGE_PC, R_RELAX_GOT_PC,
-               RE_RISCV_PC_INDIRECT, RE_LOONGARCH_PAGE_PC,
+               R_RELAX_GOT_OFF, RE_RISCV_PC_INDIRECT, RE_LOONGARCH_PAGE_PC,
                RE_LOONGARCH_PC_INDIRECT>(expr);
 }
 
@@ -747,14 +747,16 @@ static void addRelativeReloc(Ctx &ctx, InputSectionBase &isec,
 template <class PltSection, class GotPltSection>
 static void addPltEntry(Ctx &ctx, PltSection &plt, GotPltSection &gotPlt,
                         RelocationBaseSection &rel, RelType type, Symbol &sym) {
+  RelExpr expr = sym.isPreemptible ? R_ADDEND : R_ABS;
   plt.addEntry(sym);
-  gotPlt.addEntry(sym);
-  if (sym.isPreemptible)
-    rel.addReloc(
-        {type, &gotPlt, sym.getGotPltOffset(ctx), true, sym, 0, R_ADDEND});
-  else
-    rel.addReloc(
-        {type, &gotPlt, sym.getGotPltOffset(ctx), false, sym, 0, R_ABS});
+  if (ctx.target->usesGotPlt) {
+    gotPlt.addEntry(sym);
+    rel.addReloc({type, &gotPlt, sym.getGotPltOffset(ctx), sym.isPreemptible,
+                  sym, 0, expr});
+  } else {
+    rel.addReloc({type, &plt, sym.getPltOffset(ctx), sym.isPreemptible, sym, 0,
+                  expr});
+  }
 }
 
 void elf::addGotEntry(Ctx &ctx, Symbol &sym) {
@@ -920,14 +922,19 @@ void RelocScan::process(RelExpr expr, RelType type, uint64_t offset,
   // indirection.
   const bool isIfunc = sym.isGnuIFunc();
   if (!sym.isPreemptible && !isIfunc) {
-    if (expr != R_GOT_PC) {
+    if (expr != R_GOT_PC && expr != R_GOT_OFF) {
       expr = fromPlt(expr);
     } else if (!isAbsoluteOrTls(sym)) {
-      expr = ctx.target->adjustGotPcExpr(type, addend,
-                                         sec->content().data() + offset);
-      // If the target adjusted the expression to R_RELAX_GOT_PC, we may end up
+      if (expr == R_GOT_PC)
+        expr = ctx.target->adjustGotPcExpr(type, addend,
+                                           sec->content().data() + offset);
+      else
+        expr = ctx.target->adjustGotOffExpr(type, sym, addend,
+                                            sec->content().data() + offset);
+
+      // If the target adjusted the expression to R_RELAX_GOT_*, we may end up
       // needing the GOT if we can't relax everything.
-      if (expr == R_RELAX_GOT_PC)
+      if (expr == R_RELAX_GOT_PC || expr == R_RELAX_GOT_OFF)
         ctx.in.got->hasGotOffRel.store(true, std::memory_order_relaxed);
     }
   }
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index abc62bf01a421..210ef5efb4a07 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -61,6 +61,7 @@ enum RelExpr {
   R_PLT_GOTPLT,
   R_PLT_GOTREL,
   R_RELAX_HINT,
+  R_RELAX_GOT_OFF,
   R_RELAX_GOT_PC,
   R_RELAX_GOT_PC_NOPIC,
   R_RELAX_TLS_GD_TO_IE,
diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index 7bad4ceccec33..67e07b840cb82 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -181,6 +181,12 @@ uint64_t Symbol::getGotPltOffset(Ctx &ctx) const {
          ctx.target->gotEntrySize;
 }
 
+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;
+}
+
 uint64_t Symbol::getPltVA(Ctx &ctx) const {
   uint64_t outVA = isInIplt ? ctx.in.iplt->getVA() +
                                   getPltIdx(ctx) * ctx.target->ipltEntrySize
diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h
index b7d0b13ae476c..0893776882dc6 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -198,6 +198,7 @@ class Symbol {
   uint64_t getGotVA(Ctx &) const;
   uint64_t getGotPltOffset(Ctx &) const;
   uint64_t getGotPltVA(Ctx &) const;
+  uint64_t getPltOffset(Ctx &) const;
   uint64_t getPltVA(Ctx &) const;
   uint64_t getSize() const;
   OutputSection *getOutputSection() const;
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index b9a42590dc8e7..aa5fabe9f9cbc 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1523,14 +1523,31 @@ void RelocationBaseSection::finalizeContents() {
   else
     getParent()->link = 0;
 
-  if (ctx.in.relaPlt.get() == this && ctx.in.gotPlt->getParent()) {
-    getParent()->flags |= ELF::SHF_INFO_LINK;
-    getParent()->info = ctx.in.gotPlt->getParent()->sectionIndex;
+  if (ctx.in.relaPlt.get() == this) {
+    if (ctx.target->usesGotPlt && ctx.in.gotPlt->getParent()) {
+      getParent()->flags |= ELF::SHF_INFO_LINK;
+      getParent()->info = ctx.in.gotPlt->getParent()->sectionIndex;
+    } else if (ctx.in.plt->getParent()) {
+      getParent()->flags |= ELF::SHF_INFO_LINK;
+      getParent()->info = ctx.in.plt->getParent()->sectionIndex;
+    }
   }
 }
 
 void DynamicReloc::finalize(Ctx &ctx, SymbolTableBaseSect...
[truncated]

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-lld

Author: Kirill A. Korinsky (catap)

Changes

This PR consumes #137919, adds my bits from https://github.com/catap/OpenBSD-src/tree/sparc64 which I had used to actually build OpenBSD by using LLVM toolchain.

It also contains a bit of tests.

I do not tried to build OpenBSD by exactly this branch, but it looks close enough and I had resolved only one conflicts which was introduced by 3be67c7


Patch is 31.03 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/207609.diff

18 Files Affected:

  • (modified) lld/ELF/Arch/SPARCV9.cpp (+262-34)
  • (modified) lld/ELF/Driver.cpp (+4-3)
  • (modified) lld/ELF/InputSection.cpp (+1)
  • (modified) lld/ELF/Relocations.cpp (+20-13)
  • (modified) lld/ELF/Relocations.h (+1)
  • (modified) lld/ELF/Symbols.cpp (+6)
  • (modified) lld/ELF/Symbols.h (+1)
  • (modified) lld/ELF/SyntheticSections.cpp (+27-6)
  • (modified) lld/ELF/Target.cpp (+6)
  • (modified) lld/ELF/Target.h (+6-1)
  • (modified) lld/ELF/Writer.cpp (+7-3)
  • (added) lld/test/ELF/sparcv9-got-header.s (+11)
  • (added) lld/test/ELF/sparcv9-gotdata-relax.s (+37)
  • (added) lld/test/ELF/sparcv9-plt.s (+23)
  • (added) lld/test/ELF/sparcv9-rela.s (+14)
  • (added) lld/test/ELF/sparcv9-reloc-additional.s (+43)
  • (added) lld/test/ELF/sparcv9-tls-dynamic.s (+38)
  • (added) lld/test/ELF/sparcv9-ua64-dynrel.s (+29)
diff --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index 405be69a8f572..1847fb295ab41 100644
--- a/lld/ELF/Arch/SPARCV9.cpp
+++ b/lld/ELF/Arch/SPARCV9.cpp
@@ -6,7 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "InputFiles.h"
 #include "RelocScan.h"
+#include "SymbolTable.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
@@ -25,6 +27,8 @@ class SPARCV9 final : public TargetInfo {
   SPARCV9(Ctx &);
   RelExpr getRelExpr(RelType type, const Symbol &s,
                      const uint8_t *loc) const override;
+  RelType getDynRel(RelType type) const override;
+  void writeGotHeader(uint8_t *buf) const override;
   void writePlt(uint8_t *buf, const Symbol &sym,
                 uint64_t pltEntryAddr) const override;
   template <class ELFT, class RelTy>
@@ -34,6 +38,12 @@ class SPARCV9 final : public TargetInfo {
   }
   void relocate(uint8_t *loc, const Relocation &rel,
                 uint64_t val) const override;
+  void finalizeRelocScan() override;
+  RelExpr adjustGotOffExpr(RelType type, const Symbol &sym, int64_t addend,
+                           const uint8_t *loc) const override;
+
+private:
+  void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const;
 };
 } // namespace
 
@@ -42,9 +52,15 @@ SPARCV9::SPARCV9(Ctx &ctx) : TargetInfo(ctx) {
   gotRel = R_SPARC_GLOB_DAT;
   pltRel = R_SPARC_JMP_SLOT;
   relativeRel = R_SPARC_RELATIVE;
+  iRelativeRel = R_SPARC_IRELATIVE;
   symbolicRel = R_SPARC_64;
+  tlsGotRel = R_SPARC_TLS_TPOFF64;
+  tlsModuleIndexRel = R_SPARC_TLS_DTPMOD64;
+  tlsOffsetRel = R_SPARC_TLS_DTPOFF64;
+  gotHeaderEntriesNum = 1;
   pltEntrySize = 32;
   pltHeaderSize = 4 * pltEntrySize;
+  usesGotPlt = false;
 
   defaultCommonPageSize = 8192;
   defaultMaxPageSize = 0x100000;
@@ -72,6 +88,12 @@ RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
   }
 }
 
+RelType SPARCV9::getDynRel(RelType type) const {
+  if (type == symbolicRel || type == R_SPARC_UA64)
+    return type;
+  return R_SPARC_NONE;
+}
+
 template <class ELFT, class RelTy>
 void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
   RelocScan rs(ctx, &sec);
@@ -89,21 +111,33 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
     RelExpr expr;
     switch (type) {
     case R_SPARC_NONE:
+    case R_SPARC_TLS_GD_ADD:
+    case R_SPARC_TLS_LDM_ADD:
+    case R_SPARC_TLS_LDO_ADD:
+    case R_SPARC_TLS_IE_LD:
+    case R_SPARC_TLS_IE_LDX:
+    case R_SPARC_TLS_IE_ADD:
       continue;
 
     // Absolute relocations:
+    case R_SPARC_8:
+    case R_SPARC_16:
     case R_SPARC_32:
+    case R_SPARC_HI22:
+    case R_SPARC_13:
+    case R_SPARC_LO10:
     case R_SPARC_UA32:
     case R_SPARC_64:
-    case R_SPARC_UA64:
-    case R_SPARC_H44:
-    case R_SPARC_M44:
-    case R_SPARC_L44:
     case R_SPARC_HH22:
     case R_SPARC_HM10:
     case R_SPARC_LM22:
-    case R_SPARC_HI22:
-    case R_SPARC_LO10:
+    case R_SPARC_HIX22:
+    case R_SPARC_LOX10:
+    case R_SPARC_H44:
+    case R_SPARC_M44:
+    case R_SPARC_L44:
+    case R_SPARC_UA64:
+    case R_SPARC_UA16:
       expr = R_ABS;
       break;
 
@@ -111,20 +145,38 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
     case R_SPARC_WPLT30:
       rs.processR_PLT_PC(type, offset, addend, sym);
       continue;
+    case R_SPARC_TLS_GD_CALL:
+    case R_SPARC_TLS_LDM_CALL:
+      sec.addReloc({R_PLT_PC, type, offset, addend, &sym});
+      continue;
 
     // PC-relative relocations:
-    case R_SPARC_PC10:
-    case R_SPARC_PC22:
+    case R_SPARC_DISP8:
+    case R_SPARC_DISP16:
     case R_SPARC_DISP32:
     case R_SPARC_WDISP30:
+    case R_SPARC_WDISP22:
+    case R_SPARC_PC10:
+    case R_SPARC_PC22:
+    case R_SPARC_WDISP16:
+    case R_SPARC_WDISP19:
+    case R_SPARC_DISP64:
       rs.processR_PC(type, offset, addend, sym);
       continue;
 
     // GOT relocations:
     case R_SPARC_GOT10:
+    case R_SPARC_GOT13:
     case R_SPARC_GOT22:
+    case R_SPARC_GOTDATA_OP_HIX22:
+    case R_SPARC_GOTDATA_OP_LOX10:
+    case R_SPARC_GOTDATA_OP:
       expr = R_GOT_OFF;
       break;
+    case R_SPARC_GOTDATA_HIX22:
+    case R_SPARC_GOTDATA_LOX10:
+      expr = R_GOTREL;
+      break;
 
     // TLS LE relocations:
     case R_SPARC_TLS_LE_HIX22:
@@ -133,6 +185,25 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
         continue;
       expr = R_TPREL;
       break;
+    case R_SPARC_TLS_GD_HI22:
+    case R_SPARC_TLS_GD_LO10:
+      sym.setFlags(NEEDS_TLSGD);
+      sec.addReloc({R_TLSGD_GOT, type, offset, addend, &sym});
+      continue;
+    case R_SPARC_TLS_LDM_HI22:
+    case R_SPARC_TLS_LDM_LO10:
+      ctx.needsTlsLd.store(true, std::memory_order_relaxed);
+      sec.addReloc({R_TLSLD_GOT, type, offset, addend, &sym});
+      continue;
+    case R_SPARC_TLS_LDO_HIX22:
+    case R_SPARC_TLS_LDO_LOX10:
+      expr = R_DTPREL;
+      break;
+    case R_SPARC_TLS_IE_HI22:
+    case R_SPARC_TLS_IE_LO10:
+      sym.setFlags(NEEDS_TLSIE);
+      sec.addReloc({R_GOT, type, offset, addend, &sym});
+      continue;
 
     default:
       Err(ctx) << getErrorLoc(ctx, sec.content().data() + offset)
@@ -146,71 +217,139 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
 
 void SPARCV9::relocate(uint8_t *loc, const Relocation &rel,
                        uint64_t val) const {
+  switch (rel.expr) {
+  case R_RELAX_GOT_OFF:
+    return relaxGot(loc, rel, val);
+  default:
+    break;
+  }
+
   switch (rel.type) {
+  case R_SPARC_8:
+    // V-byte8
+    checkUInt(ctx, loc, val, 8, rel);
+    *loc = val;
+    break;
+  case R_SPARC_16:
+  case R_SPARC_UA16:
+    // V-half16
+    checkUInt(ctx, loc, val, 16, rel);
+    write16be(loc, val);
+    break;
   case R_SPARC_32:
   case R_SPARC_UA32:
     // V-word32
     checkUInt(ctx, loc, val, 32, rel);
     write32be(loc, val);
     break;
+  case R_SPARC_DISP8:
+    // V-byte8
+    checkIntUInt(ctx, loc, val, 8, rel);
+    *loc = val;
+    break;
+  case R_SPARC_DISP16:
+    // V-half16
+    checkIntUInt(ctx, loc, val, 16, rel);
+    write16be(loc, val);
+    break;
   case R_SPARC_DISP32:
     // V-disp32
-    checkInt(ctx, loc, val, 32, rel);
+    checkIntUInt(ctx, loc, val, 32, rel);
     write32be(loc, val);
     break;
   case R_SPARC_WDISP30:
   case R_SPARC_WPLT30:
+  case R_SPARC_TLS_GD_CALL:
+  case R_SPARC_TLS_LDM_CALL:
     // V-disp30
-    checkInt(ctx, loc, val, 32, rel);
+    checkIntUInt(ctx, loc, val, 32, rel);
     write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff));
     break;
+  case R_SPARC_WDISP22:
+    // V-disp22
+    checkIntUInt(ctx, loc, val, 24, rel);
+    write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 2) & 0x003fffff));
+    break;
+  case R_SPARC_HI22:
+    // V-imm22
+    checkUInt(ctx, loc, val, 32, rel);
+    write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
+    break;
   case R_SPARC_22:
     // V-imm22
     checkUInt(ctx, loc, val, 22, rel);
     write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff));
     break;
+  case R_SPARC_13:
+  case R_SPARC_GOT13:
+    // V-simm13
+    checkIntUInt(ctx, loc, val, 13, rel);
+    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x00001fff));
+    break;
+  case R_SPARC_LO10:
+  case R_SPARC_GOT10:
+  case R_SPARC_PC10:
+  case R_SPARC_TLS_GD_LO10:
+  case R_SPARC_TLS_LDM_LO10:
+  case R_SPARC_TLS_IE_LO10:
+    // T-simm13
+    write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff));
+    break;
   case R_SPARC_GOT22:
-  case R_SPARC_PC22:
   case R_SPARC_LM22:
+  case R_SPARC_TLS_GD_HI22:
+  case R_SPARC_TLS_LDM_HI22:
+  case R_SPARC_TLS_LDO_HIX22:
+  case R_SPARC_TLS_IE_HI22:
     // T-imm22
     write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
     break;
-  case R_SPARC_HI22:
-    // V-imm22
-    checkUInt(ctx, loc, val >> 10, 22, rel);
+  case R_SPARC_PC22:
+    // V-disp22
+    checkIntUInt(ctx, loc, val, 32, rel);
     write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
     break;
-  case R_SPARC_WDISP19:
-    // V-disp19
-    checkInt(ctx, loc, val, 21, rel);
-    write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff));
-    break;
-  case R_SPARC_GOT10:
-  case R_SPARC_PC10:
-    // T-simm10
-    write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff));
-    break;
-  case R_SPARC_LO10:
-    // T-simm13
-    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff));
-    break;
   case R_SPARC_64:
+  case R_SPARC_DISP64:
   case R_SPARC_UA64:
     // V-xword64
     write64be(loc, val);
     break;
   case R_SPARC_HH22:
     // V-imm22
-    checkUInt(ctx, loc, val >> 42, 22, rel);
     write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 42) & 0x003fffff));
     break;
   case R_SPARC_HM10:
     // T-simm13
-    write32be(loc, (read32be(loc) & ~0x00001fff) | ((val >> 32) & 0x000003ff));
+    write32be(loc, (read32be(loc) & ~0x000003ff) | ((val >> 32) & 0x000003ff));
+    break;
+  case R_SPARC_WDISP16:
+    // V-d2/disp14
+    checkIntUInt(ctx, loc, val, 18, rel);
+    write32be(loc, (read32be(loc) & ~0x0303fff) |
+                       (((val >> 2) & 0xc000) << 6) |
+                       ((val >> 2) & 0x00003fff));
+    break;
+  case R_SPARC_WDISP19:
+    // V-disp19
+    checkIntUInt(ctx, loc, val, 21, rel);
+    write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff));
+    break;
+  case R_SPARC_HIX22:
+    // V-imm22
+    checkUInt(ctx, loc, ~val, 32, rel);
+    write32be(loc, (read32be(loc) & ~0x003fffff) | ((~val >> 10) & 0x003fffff));
+    break;
+  case R_SPARC_LOX10:
+  case R_SPARC_TLS_LE_LOX10:
+  case R_SPARC_GOTDATA_LOX10:
+  case R_SPARC_GOTDATA_OP_LOX10:
+    // T-simm13
+    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | 0x1c00);
     break;
   case R_SPARC_H44:
     // V-imm22
-    checkUInt(ctx, loc, val >> 22, 22, rel);
+    checkUInt(ctx, loc, val, 44, rel);
     write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 22) & 0x003fffff));
     break;
   case R_SPARC_M44:
@@ -219,21 +358,110 @@ void SPARCV9::relocate(uint8_t *loc, const Relocation &rel,
     break;
   case R_SPARC_L44:
     // T-imm13
-    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x00000fff));
+    write32be(loc, (read32be(loc) & ~0x00000fff) | (val & 0x00000fff));
+    break;
+  case R_SPARC_TLS_GD_ADD:
+  case R_SPARC_TLS_LDM_ADD:
+  case R_SPARC_TLS_LDO_ADD:
+  case R_SPARC_TLS_IE_LD:
+  case R_SPARC_TLS_IE_LDX:
+  case R_SPARC_TLS_IE_ADD:
     break;
   case R_SPARC_TLS_LE_HIX22:
     // T-imm22
     write32be(loc, (read32be(loc) & ~0x003fffff) | ((~val >> 10) & 0x003fffff));
     break;
-  case R_SPARC_TLS_LE_LOX10:
+  case R_SPARC_TLS_LDO_LOX10:
     // T-simm13
-    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | 0x1C00);
+    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff));
+    break;
+  case R_SPARC_GOTDATA_HIX22:
+    // V-imm22
+    checkUInt(ctx, loc, ((int64_t)val < 0 ? ~val : val), 32, rel);
+    write32be(loc, (read32be(loc) & ~0x003fffff) |
+                       ((((int64_t)val < 0 ? ~val : val) >> 10) & 0x003fffff));
+    break;
+  case R_SPARC_GOTDATA_OP_HIX22:
+    // T-imm22
+    write32be(loc, (read32be(loc) & ~0x003fffff) |
+                       ((((int64_t)val < 0 ? ~val : val) >> 10) & 0x003fffff));
+    break;
+  case R_SPARC_GOTDATA_OP:
     break;
   default:
     llvm_unreachable("unknown relocation");
   }
 }
 
+RelExpr SPARCV9::adjustGotOffExpr(RelType type, const Symbol &sym,
+                                  int64_t addend, const uint8_t *loc) const {
+  switch (type) {
+  case R_SPARC_GOTDATA_OP_HIX22:
+  case R_SPARC_GOTDATA_OP_LOX10:
+  case R_SPARC_GOTDATA_OP:
+    if (sym.isLocal())
+      return R_RELAX_GOT_OFF;
+    [[fallthrough]];
+  default:
+    return R_GOT_OFF;
+  }
+}
+
+void SPARCV9::relaxGot(uint8_t *loc, const Relocation &rel,
+                       uint64_t val) const {
+  switch (rel.type) {
+  case R_SPARC_GOTDATA_OP_HIX22:
+    // T-imm22
+    write32be(loc, (read32be(loc) & ~0x003fffff) |
+                       ((((int64_t)val < 0 ? ~val : val) >> 10) & 0x003fffff));
+    break;
+  case R_SPARC_GOTDATA_OP_LOX10:
+    // T-imm13
+    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) |
+                       ((int64_t)val < 0 ? 0x1c00 : 0));
+    break;
+  case R_SPARC_GOTDATA_OP:
+    // ldx [%rs1 + %rs2], %rd -> add %rs1, %rs2, %rd
+    write32be(loc, (read32be(loc) & 0x3e07c01f) | 0x80000000);
+    break;
+  default:
+    llvm_unreachable("unknown relocation");
+  }
+}
+
+void SPARCV9::writeGotHeader(uint8_t *buf) const {
+  write64be(buf, ctx.in.dynamic->getVA());
+}
+
+static bool isTlsCall(RelType type) {
+  return type == R_SPARC_TLS_GD_CALL || type == R_SPARC_TLS_LDM_CALL;
+}
+
+void SPARCV9::finalizeRelocScan() {
+  Symbol *tga = nullptr;
+
+  for (ELFFileBase *file : ctx.objectFiles) {
+    for (InputSectionBase *s : file->getSections()) {
+      auto *isec = dyn_cast_or_null<InputSection>(s);
+      if (!isec || !isec->isLive())
+        continue;
+      for (Relocation &rel : isec->relocs()) {
+        if (rel.expr != R_PLT_PC || !isTlsCall(rel.type))
+          continue;
+        if (!tga) {
+          tga = ctx.symtab->addSymbol(Undefined{ctx.internalFile,
+                                                "__tls_get_addr", STB_GLOBAL,
+                                                STV_DEFAULT, STT_FUNC});
+          tga->isUsedInRegularObj = true;
+          tga->isPreemptible = true;
+          tga->setFlags(NEEDS_PLT | USED);
+        }
+        rel.sym = tga;
+      }
+    }
+  }
+}
+
 void SPARCV9::writePlt(uint8_t *buf, const Symbol & /*sym*/,
                        uint64_t pltEntryAddr) const {
   const uint8_t pltData[] = {
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 7ec7dfcae6bca..a2552a1ae1ab7 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1318,9 +1318,10 @@ static SmallVector<StringRef, 0> getSymbolOrderingFile(Ctx &ctx,
 
 static bool getIsRela(Ctx &ctx, opt::InputArgList &args) {
   // The psABI specifies the default relocation entry format.
-  bool rela = is_contained({EM_AARCH64, EM_AMDGPU, EM_HEXAGON, EM_LOONGARCH,
-                            EM_PPC, EM_PPC64, EM_RISCV, EM_S390, EM_X86_64},
-                           ctx.arg.emachine);
+  bool rela =
+      is_contained({EM_AARCH64, EM_AMDGPU, EM_HEXAGON, EM_LOONGARCH, EM_PPC,
+                    EM_PPC64, EM_RISCV, EM_S390, EM_SPARCV9, EM_X86_64},
+                   ctx.arg.emachine);
   // If -z rel or -z rela is specified, use the last option.
   for (auto *arg : args.filtered(OPT_z)) {
     StringRef s(arg->getValue());
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index c78800787d27e..c26425bbc4a7b 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -833,6 +833,7 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
   case R_GOTPLTONLY_PC:
     return ctx.in.gotPlt->getVA() + a - p;
   case R_GOTREL:
+  case R_RELAX_GOT_OFF:
     return r.sym->getVA(ctx, a) - ctx.in.got->getVA();
   case R_GOTPLTREL:
     return r.sym->getVA(ctx, a) - ctx.in.gotPlt->getVA();
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 0d45236e6d11e..cd8d3634e9d33 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -145,7 +145,7 @@ bool lld::elf::needsGot(RelExpr expr) {
 static bool isRelExpr(RelExpr expr) {
   return oneof<R_PC, R_GOTREL, R_GOTPLTREL, RE_ARM_PCA, RE_MIPS_GOTREL,
                RE_PPC64_CALL, RE_AARCH64_PAGE_PC, R_RELAX_GOT_PC,
-               RE_RISCV_PC_INDIRECT, RE_LOONGARCH_PAGE_PC,
+               R_RELAX_GOT_OFF, RE_RISCV_PC_INDIRECT, RE_LOONGARCH_PAGE_PC,
                RE_LOONGARCH_PC_INDIRECT>(expr);
 }
 
@@ -747,14 +747,16 @@ static void addRelativeReloc(Ctx &ctx, InputSectionBase &isec,
 template <class PltSection, class GotPltSection>
 static void addPltEntry(Ctx &ctx, PltSection &plt, GotPltSection &gotPlt,
                         RelocationBaseSection &rel, RelType type, Symbol &sym) {
+  RelExpr expr = sym.isPreemptible ? R_ADDEND : R_ABS;
   plt.addEntry(sym);
-  gotPlt.addEntry(sym);
-  if (sym.isPreemptible)
-    rel.addReloc(
-        {type, &gotPlt, sym.getGotPltOffset(ctx), true, sym, 0, R_ADDEND});
-  else
-    rel.addReloc(
-        {type, &gotPlt, sym.getGotPltOffset(ctx), false, sym, 0, R_ABS});
+  if (ctx.target->usesGotPlt) {
+    gotPlt.addEntry(sym);
+    rel.addReloc({type, &gotPlt, sym.getGotPltOffset(ctx), sym.isPreemptible,
+                  sym, 0, expr});
+  } else {
+    rel.addReloc({type, &plt, sym.getPltOffset(ctx), sym.isPreemptible, sym, 0,
+                  expr});
+  }
 }
 
 void elf::addGotEntry(Ctx &ctx, Symbol &sym) {
@@ -920,14 +922,19 @@ void RelocScan::process(RelExpr expr, RelType type, uint64_t offset,
   // indirection.
   const bool isIfunc = sym.isGnuIFunc();
   if (!sym.isPreemptible && !isIfunc) {
-    if (expr != R_GOT_PC) {
+    if (expr != R_GOT_PC && expr != R_GOT_OFF) {
       expr = fromPlt(expr);
     } else if (!isAbsoluteOrTls(sym)) {
-      expr = ctx.target->adjustGotPcExpr(type, addend,
-                                         sec->content().data() + offset);
-      // If the target adjusted the expression to R_RELAX_GOT_PC, we may end up
+      if (expr == R_GOT_PC)
+        expr = ctx.target->adjustGotPcExpr(type, addend,
+                                           sec->content().data() + offset);
+      else
+        expr = ctx.target->adjustGotOffExpr(type, sym, addend,
+                                            sec->content().data() + offset);
+
+      // If the target adjusted the expression to R_RELAX_GOT_*, we may end up
       // needing the GOT if we can't relax everything.
-      if (expr == R_RELAX_GOT_PC)
+      if (expr == R_RELAX_GOT_PC || expr == R_RELAX_GOT_OFF)
         ctx.in.got->hasGotOffRel.store(true, std::memory_order_relaxed);
     }
   }
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index abc62bf01a421..210ef5efb4a07 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -61,6 +61,7 @@ enum RelExpr {
   R_PLT_GOTPLT,
   R_PLT_GOTREL,
   R_RELAX_HINT,
+  R_RELAX_GOT_OFF,
   R_RELAX_GOT_PC,
   R_RELAX_GOT_PC_NOPIC,
   R_RELAX_TLS_GD_TO_IE,
diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index 7bad4ceccec33..67e07b840cb82 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -181,6 +181,12 @@ uint64_t Symbol::getGotPltOffset(Ctx &ctx) const {
          ctx.target->gotEntrySize;
 }
 
+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;
+}
+
 uint64_t Symbol::getPltVA(Ctx &ctx) const {
   uint64_t outVA = isInIplt ? ctx.in.iplt->getVA() +
                                   getPltIdx(ctx) * ctx.target->ipltEntrySize
diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h
index b7d0b13ae476c..0893776882dc6 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -198,6 +198,7 @@ class Symbol {
   uint64_t getGotVA(Ctx &) const;
   uint64_t getGotPltOffset(Ctx &) const;
   uint64_t getGotPltVA(Ctx &) const;
+  uint64_t getPltOffset(Ctx &) const;
   uint64_t getPltVA(Ctx &) const;
   uint64_t getSize() const;
   OutputSection *getOutputSection() const;
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index b9a42590dc8e7..aa5fabe9f9cbc 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1523,14 +1523,31 @@ void RelocationBaseSection::finalizeContents() {
   else
     getParent()->link = 0;
 
-  if (ctx.in.relaPlt.get() == this && ctx.in.gotPlt->getParent()) {
-    getParent()->flags |= ELF::SHF_INFO_LINK;
-    getParent()->info = ctx.in.gotPlt->getParent()->sectionIndex;
+  if (ctx.in.relaPlt.get() == this) {
+    if (ctx.target->usesGotPlt && ctx.in.gotPlt->getParent()) {
+      getParent()->flags |= ELF::SHF_INFO_LINK;
+      getParent()->info = ctx.in.gotPlt->getParent()->sectionIndex;
+    } else if (ctx.in.plt->getParent()) {
+      getParent()->flags |= ELF::SHF_INFO_LINK;
+      getParent()->info = ctx.in.plt->getParent()->sectionIndex;
+    }
   }
 }
 
 void DynamicReloc::finalize(Ctx &ctx, SymbolTableBaseSect...
[truncated]

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown

✅ With the latest revision this PR passed the C/C++ code formatter.

@jrtc27

jrtc27 commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

The reason why I was in favour of splitting this up is that some of the changes are straightforward and can be landed quickly, but others are more invasive (or, skimming this patch, look incorrect, e.g. the GOT relaxation code looks odd to me, but I'd need to remind myself of the details first). Landing the simple stuff first cuts down on the size of the diff that keeps being reviewed/amended/etc.

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown

🪟 Windows x64 Test Results

  • 3164 tests passed
  • 66 tests skipped

✅ The build succeeded and all tests passed.

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown

🐧 Linux x64 Test Results

  • 4099 tests passed
  • 114 tests skipped

✅ The build succeeded and all tests passed.

@catap

catap commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

@jrtc27 probably, buthere a lot of changes and all of them, let say accumulated near a few places of code. Extracting them one-by-one will make rebase quite painful.

@catap
catap force-pushed the openbsd-sparc64 branch 3 times, most recently from daf0bff to d122eb6 Compare July 5, 2026 21:19
@catap

catap commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

@jrtc27 I can try to create smaller commits if you think it can make review easy

@catap
catap force-pushed the openbsd-sparc64 branch from d122eb6 to 56609ed Compare July 6, 2026 00:17
@catap

catap commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Here it is. I hope I haven't broke anything. We can discuss it and I'll cherry-pick agreed commits and rebase that branch.

Not sure that I can make them smaller.

@jrtc27

jrtc27 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

It's not about the number of commits in the PR, it's about this being a single PR. In LLVM, 1 PR is 1 commit when merged (and the commit message is taken from the PR description), so anything that should be a separate commit that undergoes its own independent review needs to go in its own PR. See https://llvm.org/docs/GitHub.html.

@brad0
brad0 requested a review from MaskRay July 6, 2026 06:30
@glaubitz

glaubitz commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

It's not about the number of commits in the PR, it's about this being a single PR. In LLVM, 1 PR is 1 commit when merged (and the commit message is taken from the PR description), so anything that should be a separate commit that undergoes its own independent review needs to go in its own PR. See https://llvm.org/docs/GitHub.html.

But is it really a rule that must be strictly enforced? I think it makes more sense to split up the changes and still land them with one commit.

@catap
catap force-pushed the openbsd-sparc64 branch from 56609ed to d6d422d Compare July 6, 2026 09:54
@catap

catap commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

If it is going to be merged, I don't mind open 18 PR one by one, but I think it should be discussed and agreed before.

@MaskRay MaskRay left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind combining multiple sparc64-related changes into a single commit, provided we ensure thorough test coverage.

When I added the initial ppc32 and riscv ports in 2019, I spent a lot of time curating a small but comprehensive set of tests. You should consolidate the abs-* tests into one file, and do the same for the plt-* tests. There are other test improvements that should be done - which is going to painful if you split this into multiple patches.

Comment thread lld/test/ELF/sparcv9-ua64-dynrel.s Outdated
# CHECK: R_SPARC_RELATIVE
# CHECK-NOT: R_SPARC_UA64

# ERR: R_SPARC_UA64 relocation at offset {{[0-9]+}} against non-preemptible symbol local is not 8-byte aligned

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All error message should include error: . See newly added negative tests

(ctx.arg.andFeatures & GNU_PROPERTY_X86_FEATURE_1_IBT))
name = ".plt.sec";

// The PLT needs to be writable on SPARC as the dynamic linker will

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am surprised OpenBSD allows this. Will you introduce a Secure PLT ABI?

@catap
catap force-pushed the openbsd-sparc64 branch from d6d422d to 985a757 Compare July 7, 2026 21:48
@catap

catap commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

@MaskRay sorry, I used full diff to make this PR which includes OpenBSD specific things. A new version addressed all your remarks, and I have drop that PLT-related changes.

@catap
catap force-pushed the openbsd-sparc64 branch 2 times, most recently from 292bacd to 4ca1593 Compare July 8, 2026 21:37
@catap
catap requested a review from MaskRay July 8, 2026 22:34
@brad0

brad0 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

@brad0 base builds and boots, regression is passed.

Ports in progress right now, but so far a few openbsd specific issues.

I'll write here when it's done and share the link to a brnach with all changes that I did.

It doesn't have to be literally perfect, but try to find / shake out what you can find from ports build.

@catap

catap commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

@brad0 so far the major issue in ports is lang/gcc/15, which I think I had addressed locally and I need some time to rebuild everything and test.

After that beast is over, I'll re-reviwe all my local changes to verify which can be upstreamed and which is openbsd specific.

Meanwhile, I had backported / extracted as separated and small PR a few bugfixes already, I think it is out of scope from this PR because it is LLD orinted:

@catap
catap force-pushed the openbsd-sparc64 branch from a3fa382 to 20b081b Compare July 16, 2026 21:53
@catap

catap commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

@brad0 here rebase and push with a few more bits:

To be clear: this PR plus:

Is re-implemetation to current LLVM the code from https://github.com/catap/OpenBSD-src/tree/sparc64 without OpenBSD specific bits and which is llvm-22 based.

I tested only OpenBSD specific branch and it actually allows me to build whole system with clang lld default toolchain, and a kernel which boots on sparc64 and it was retested on one more sparc64 machine.

Resutled system builds gcc-15 and roughly half of ports collection is built by ingoing bulk.

@catap

catap commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Ok, I think I had spoted one more missed bit. Here it is.

@catap
catap force-pushed the openbsd-sparc64 branch 2 times, most recently from ff57c13 to 0409073 Compare July 17, 2026 11:36
Comment thread lld/test/ELF/eh-frame-dyn-reloc.s Outdated
@@ -0,0 +1,33 @@
# REQUIRES: x86

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This .eh_frame fix is generic and should be extracted

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted as #210967

Comment thread lld/ELF/SyntheticSections.cpp Outdated
EhFrameSection &eh = *ctx.in.ehFrame;
sec->parent = &eh;
eh.addralign = std::max(eh.addralign, sec->addralign);
eh.flags |= sec->flags & SHF_WRITE;

@MaskRay MaskRay Jul 18, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really backward. Can you change compilers to not use absolute FDE/personality encodings, allowing .eh_frame to be readonly (like every other arch)?
I don't think we want to take this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, we currently do this for CHERI too, because we seal our return capabilities and therefore can't just do pointer arithmetic on them to get to a landing pad like you normally would with relative offsets. That is a bit ugly for various reasons though, not just requiring it to be relro rather than read-only, so we've talked about having a separate table of such capabilities that gets indexed by .eh_frame. But that's not what we do today. And to be honest, even if the input on non-CHERI is unnecessary, given it's little burden to support and is valid, should we not?

Comment thread lld/ELF/SyntheticSections.cpp Outdated
symbols.push_back({b, strTabSec.addString(b->getName(), false)});
}

void SymbolTableBaseSection::addLocalSectionSymbol(Symbol *b) {

@MaskRay MaskRay Jul 18, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want to take support for the legacy local dynamic symbol (this and ctx.target->prepareDynamicReloc).

Every other 64-bit arch (aarch64,x86-64,riscv64, etc) doesn't allow sub-pointer-width absolute reloc that would have to become dynamic. You should fix your code instead.

Also, we really don't need one synthesized STT_SECTION dynamic symbol per output section needing dynamic relocs ( DenseMap<OutputSection *, Defined *> dynamicSectionSymbols;), just one shared STT_SECTION dynamic symbol.

@catap

catap commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@MaskRay dropped pointed commit.

@catap
catap requested a review from MaskRay July 21, 2026 12:42
@catap
catap force-pushed the openbsd-sparc64 branch 2 times, most recently from e9ab538 to 3307d97 Compare July 26, 2026 21:50
@catap

catap commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

Rebased and added one more piece which is heavy depeneded to this branch: support large PLTs.

This issue was discovered by ongoing bulk build of openbsd ports.

Comment thread lld/test/ELF/sparcv9-got-header.s Outdated
@@ -0,0 +1,11 @@
# REQUIRES: sparc

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unneeded as a separate file. I am folding this into sparcv9-reloc-got.s

Comment thread lld/test/ELF/sparcv9-reloc-additional.s Outdated
@@ -0,0 +1,67 @@
# REQUIRES: sparc

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop after rebase. I've split the tests into sparcv9-reloc-{,pc,range}.s

@catap

catap commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

@MaskRay here it is.

@catap
catap force-pushed the openbsd-sparc64 branch from 3307d97 to de7cb20 Compare July 27, 2026 02:27
Comment thread lld/test/ELF/sparcv9-rela.s Outdated
@@ -0,0 +1,14 @@
# REQUIRES: sparc

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is redundant - other tests check .rela.dyn

@catap
catap force-pushed the openbsd-sparc64 branch from de7cb20 to fa19c98 Compare July 27, 2026 02:42
Comment thread lld/ELF/Relocations.cpp Outdated
// If the target adjusted the expression to an optimizable form, we may
// end up needing the GOT if we can't optimize everything.
if (expr == R_RELAX_GOT_PC || expr == R_RELAX_GOT_PC_NOPIC)
if (expr == R_GOT_PC)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take over GOTDATA relocations. We can implement GOTDATA_OP without touching the generic code.

catap and others added 6 commits July 27, 2026 12:08
Co-authored-by: LemonBoy <thatlemon@gmail.com>
Co-authored-by: Alex Rønne Petersen <alex@alexrp.com>
Co-authored-by: LemonBoy <thatlemon@gmail.com>
Co-authored-by: LemonBoy <thatlemon@gmail.com>

Co-authored-by: Alex Rønne Petersen <alex@alexrp.com>
Co-authored-by: LemonBoy <thatlemon@gmail.com>
Co-authored-by: Alex Rønne Petersen <alex@alexrp.com>
Co-authored-by: LemonBoy <thatlemon@gmail.com>

Co-authored-by: Alex Rønne Petersen <alex@alexrp.com>
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.
@catap
catap force-pushed the openbsd-sparc64 branch from fa19c98 to 149c4b8 Compare July 27, 2026 10:10
@catap

catap commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

@MaskRay here it is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants