Index: lld/ELF/Arch/SPARCV9.cpp
--- lld/ELF/Arch/SPARCV9.cpp.orig
+++ lld/ELF/Arch/SPARCV9.cpp
@@ -6,11 +6,16 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "OutputSections.h"
 #include "RelocScan.h"
+#include "SymbolTable.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/Support/Endian.h"
+#include <algorithm>
+#include <cassert>
 
 using namespace llvm;
 using namespace llvm::object;
@@ -20,21 +25,45 @@ 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 &);
   RelExpr getRelExpr(RelType type, const Symbol &s,
                      const uint8_t *loc) const override;
+  RelType getDynRel(RelType type) const override;
+  void prepareDynamicReloc(RelType type, const Symbol &sym,
+                           InputSectionBase &sec) 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);
   void scanSection(InputSectionBase &sec, unsigned shard) override {
     elf::scanSection1<SPARCV9, ELF64BE>(*this, sec, shard);
   }
+  void finalizeRelocScan() override;
   void relocate(uint8_t *loc, const Relocation &rel,
                 uint64_t val) const override;
+  RelExpr adjustGotOffExpr(RelType type, const Symbol &sym, int64_t addend,
+                           const uint8_t *loc) const override;
+
+private:
+  uint64_t getLargePltPointerOffset(uint32_t pltIdx) const;
+  void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const;
+
+  DenseMap<OutputSection *, Defined *> dynamicSectionSymbols;
 };
 } // namespace
 
@@ -43,9 +72,16 @@ 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;
@@ -57,15 +93,74 @@ SPARCV9::SPARCV9(Ctx &ctx) : TargetInfo(ctx) {
 RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
                             const uint8_t *loc) const {
   switch (type) {
+  case R_SPARC_NONE:
+    return R_NONE;
+  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_HH22:
+  case R_SPARC_HM10:
+  case R_SPARC_LM22:
+  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:
     return R_ABS;
+  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:
     return R_PC;
-  case R_SPARC_NONE:
-    return R_NONE;
+  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:
+    return R_GOT_OFF;
+  case R_SPARC_WPLT30:
+  case R_SPARC_TLS_GD_CALL:
+  case R_SPARC_TLS_LDM_CALL:
+    return R_PLT_PC;
+  case R_SPARC_TLS_GD_HI22:
+  case R_SPARC_TLS_GD_LO10:
+    return R_TLSGD_GOT;
+  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:
+    return R_NONE; // TODO: Relax TLS relocations.
+  case R_SPARC_TLS_LDM_HI22:
+  case R_SPARC_TLS_LDM_LO10:
+    return R_TLSLD_GOT;
+  case R_SPARC_TLS_LDO_HIX22:
+  case R_SPARC_TLS_LDO_LOX10:
+    return R_DTPREL;
+  case R_SPARC_TLS_IE_HI22:
+  case R_SPARC_TLS_IE_LO10:
+    return R_GOT;
+  case R_SPARC_TLS_LE_HIX22:
+  case R_SPARC_TLS_LE_LOX10:
+    return R_TPREL;
+  case R_SPARC_GOTDATA_HIX22:
+  case R_SPARC_GOTDATA_LOX10:
+    return R_GOTREL;
   default:
     Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v
              << ") against symbol " << &s;
@@ -91,9 +186,21 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, R
     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_13:
+    case R_SPARC_UA16:
+    case R_SPARC_HIX22:
+    case R_SPARC_LOX10:
     case R_SPARC_32:
     case R_SPARC_UA32:
     case R_SPARC_64:
@@ -114,7 +221,21 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, R
       rs.processR_PLT_PC(type, offset, addend, sym);
       continue;
 
+    case R_SPARC_TLS_GD_CALL:
+    case R_SPARC_TLS_LDM_CALL:
+      if (sym.isTls())
+        sec.addReloc({R_PLT_PC, type, offset, addend, &sym});
+      else
+        rs.processR_PLT_PC(type, offset, addend, sym);
+      continue;
+
     // PC-relative relocations:
+    case R_SPARC_DISP8:
+    case R_SPARC_DISP16:
+    case R_SPARC_DISP64:
+    case R_SPARC_WDISP16:
+    case R_SPARC_WDISP19:
+    case R_SPARC_WDISP22:
     case R_SPARC_PC10:
     case R_SPARC_PC22:
     case R_SPARC_DISP32:
@@ -123,11 +244,39 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, R
       continue;
 
     // GOT relocations:
+    case R_SPARC_GOT13:
+    case R_SPARC_GOTDATA_OP_HIX22:
+    case R_SPARC_GOTDATA_OP_LOX10:
+    case R_SPARC_GOTDATA_OP:
     case R_SPARC_GOT10:
     case R_SPARC_GOT22:
       expr = R_GOT_OFF;
       break;
 
+    case R_SPARC_GOTDATA_HIX22:
+    case R_SPARC_GOTDATA_LOX10:
+      ctx.in.got->hasGotOffRel.store(true, std::memory_order_relaxed);
+      expr = R_GOTREL;
+      break;
+
+    case R_SPARC_TLS_GD_HI22:
+    case R_SPARC_TLS_GD_LO10:
+      rs.handleTlsGd(R_TLSGD_GOT, R_NONE, R_NONE, 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:
+      sec.addReloc({R_DTPREL, type, offset, addend, &sym});
+      continue;
+    case R_SPARC_TLS_IE_HI22:
+    case R_SPARC_TLS_IE_LO10:
+      rs.handleTlsIe<false>(R_GOT, type, offset, addend, sym);
+      continue;
+
     // TLS LE relocations:
     case R_SPARC_TLS_LE_HIX22:
     case R_SPARC_TLS_LE_LOX10:
@@ -146,73 +295,250 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, R
   }
 }
 
+RelType SPARCV9::getDynRel(RelType type) const {
+  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;
+}
+
+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::prepareDynamicReloc(RelType type, const Symbol &sym,
+                                  InputSectionBase &sec) {
+  if (auto *eh = dyn_cast<EhInputSection>(&sec)) {
+    SyntheticSection *parent = eh->getParent();
+    parent->flags |= sec.flags & SHF_WRITE;
+    parent->getParent()->flags |= sec.flags & SHF_WRITE;
+  }
+
+  RelType aligned = R_SPARC_NONE, unaligned = R_SPARC_NONE;
+  uint64_t alignment = 1;
+  if (!getSparcAbsRelocPair(type, aligned, unaligned, alignment) ||
+      sym.isPreemptible)
+    return;
+
+  OutputSection *osec = sym.getOutputSection();
+  if (!osec)
+    return;
+
+  if (dynamicSectionSymbols.contains(osec))
+    return;
+
+  Defined *sectionSym =
+      makeDefined(ctx, ctx.internalFile, "", STB_LOCAL, STV_DEFAULT,
+                  STT_SECTION, 0, 0, osec);
+  ctx.in.dynSymTab->addLocalSectionSymbol(sectionSym);
+  dynamicSectionSymbols[osec] = sectionSym;
+}
+
+void SPARCV9::finalizeDynamicReloc(DynamicReloc &rel) const {
+  // scanEhSection records an offset relative to the merged .eh_frame.
+  if (auto *eh = dyn_cast<EhInputSection>(rel.inputSec)) {
+    rel.inputSec = eh->getParent();
+    rel.r_offset = rel.inputSec->getVA(rel.offsetInSec);
+  }
+
+  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))
+    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;
+  }
+
+  OutputSection *osec = rel.sym->getOutputSection();
+  auto it = dynamicSectionSymbols.find(osec);
+  assert(it != dynamicSectionSymbols.end());
+  rel.addend = rel.sym->getVA(ctx, rel.addend);
+  rel.sym = it->second;
+}
+
 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: // Only T-imm22 on 32-bit, despite binutils behavior.
+    // 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_GOT22:
-  case R_SPARC_PC22:
-  case R_SPARC_LM22:
-    // T-imm22
-    write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
+  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_HI22:
-    // V-imm22
-    checkUInt(ctx, loc, val >> 10, 22, 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_LO10:
   case R_SPARC_GOT10:
   case R_SPARC_PC10:
-    // T-simm10
+  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_LO10:
+  case R_SPARC_TLS_LDO_LOX10:
     // T-simm13
     write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff));
     break;
+  case R_SPARC_GOT22:
+  case R_SPARC_LM22:
+  case R_SPARC_TLS_GD_HI22:
+  case R_SPARC_TLS_LDM_HI22:
+  case R_SPARC_TLS_LDO_HIX22: // Not V-simm22, despite binutils behavior.
+  case R_SPARC_TLS_IE_HI22:
+    // T-(s)imm22
+    write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
+    break;
+  case R_SPARC_PC22:
+    // V-disp22
+    checkIntUInt(ctx, loc, val, 32, rel);
+    write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
+    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:
+    // 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:
@@ -221,23 +547,134 @@ void SPARCV9::relocate(uint8_t *loc, const Relocation 
     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_LE_HIX22:
+  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:
+    // None
+    break;
+  case R_SPARC_TLS_LE_HIX22: // Not V-imm2, despite binutils behavior.
     // T-imm22
     write32be(loc, (read32be(loc) & ~0x003fffff) | ((~val >> 10) & 0x003fffff));
     break;
-  case R_SPARC_TLS_LE_LOX10:
-    // T-simm13
-    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | 0x1C00);
+  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: // Not V-imm22, despite binutils behavior.
+                                 // Non-relaxed case.
+    // T-imm22
+    write32be(loc, (read32be(loc) & ~0x003fffff) |
+                       ((((int64_t)val < 0 ? ~val : val) >> 10) & 0x003fffff));
+    break;
+  case R_SPARC_GOTDATA_LOX10:
+  case R_SPARC_GOTDATA_OP_LOX10: // Non-relaxed case.
+    // T-imm13
+    write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) |
+                       ((int64_t)val < 0 ? 0x1c00 : 0));
+    break;
+  case R_SPARC_GOTDATA_OP: // Non-relaxed case.
+    // word32
+    // Nothing needs to be done in the non-relaxed case.
+    break;
   default:
     llvm_unreachable("unknown relocation");
   }
 }
 
-void SPARCV9::writePlt(uint8_t *buf, const Symbol & /*sym*/,
+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: // Not V-imm22, despite binutils behavior.
+    // 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:
+    // word32
+    // 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 {
+  // _GLOBAL_OFFSET_TABLE_[0] = _DYNAMIC when a dynamic section exists.
+  if (ctx.in.dynamic)
+    write64(ctx, buf, ctx.in.dynamic->getVA());
+}
+
+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);      // mov %o7, %g5
+    write32be(buf + 4, 0x40000002);  // call .+8
+    write32be(buf + 8, 0x01000000);  // nop
+    write32be(buf + 12,
+              0xc25be000 | ((ptrOff - (off + 4)) & 0x1fff));
+    write32be(buf + 16, 0x83c3c001); // jmpl %o7+%g1, %g1
+    write32be(buf + 20, 0x9e100005); // mov %g5, %o7
+    return;
+  }
+
   const uint8_t pltData[] = {
       0x03, 0x00, 0x00, 0x00, // sethi   (. - .PLT0), %g1
       0x30, 0x68, 0x00, 0x00, // ba,a    %xcc, .PLT1
@@ -250,9 +687,41 @@ void SPARCV9::writePlt(uint8_t *buf, const Symbol & /*
   };
   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 SPARCV9::finalizeRelocScan() {
+  Symbol *tga = nullptr;
+  for (ELFFileBase *f : ctx.objectFiles) {
+    for (InputSectionBase *s : f->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 || !rel.sym->isTls() ||
+            (rel.type != R_SPARC_TLS_GD_CALL &&
+             rel.type != R_SPARC_TLS_LDM_CALL))
+          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 elf::setSPARCV9TargetInfo(Ctx &ctx) { ctx.target.reset(new SPARCV9(ctx)); }
