[lld] Enough LLD changes to bootstrap OpenBSD on sparc64 (PR #207609)
Kirill A. Korinsky via llvm-commits
llvm-commits at lists.llvm.org
Sun Jul 5 14:19:39 PDT 2026
https://github.com/catap updated https://github.com/llvm/llvm-project/pull/207609
>From 6b37ff2835ebe08df790b2ba5512ca9b8f91b9f6 Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill at korins.ky>
Date: Sun, 5 Jul 2026 21:27:10 +0200
Subject: [PATCH 1/7] [lld][ELF][SPARC] Use RELA relocations for SPARCV9
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: LemonBoy <thatlemon at gmail.com>
Co-authored-by: Alex Rønne Petersen <alex at alexrp.com>
---
lld/ELF/Driver.cpp | 7 ++++---
lld/test/ELF/sparcv9-rela.s | 14 ++++++++++++++
2 files changed, 18 insertions(+), 3 deletions(-)
create mode 100644 lld/test/ELF/sparcv9-rela.s
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/test/ELF/sparcv9-rela.s b/lld/test/ELF/sparcv9-rela.s
new file mode 100644
index 0000000000000..2cebe2acffbed
--- /dev/null
+++ b/lld/test/ELF/sparcv9-rela.s
@@ -0,0 +1,14 @@
+# 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
+
+# CHECK: Relocation section '.rela.dyn'
+# CHECK: R_SPARC_RELATIVE
+
+.data
+.quad local
+
+.hidden local
+local:
+ .quad 0
>From 86e2a5f08f9695efaaec8531001a6ef8281c5924 Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill at korins.ky>
Date: Sun, 5 Jul 2026 21:34:29 +0200
Subject: [PATCH 2/7] [lld][ELF][SPARC] Handle more SPARCV9 relocations
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: LemonBoy <thatlemon at gmail.com>
Co-authored-by: Alex Rønne Petersen <alex at alexrp.com>
---
lld/ELF/Arch/SPARCV9.cpp | 138 +++++++++++++++++-------
lld/test/ELF/sparcv9-reloc-additional.s | 41 +++++++
2 files changed, 143 insertions(+), 36 deletions(-)
create mode 100644 lld/test/ELF/sparcv9-reloc-additional.s
diff --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index 405be69a8f572..36f309e07e4a2 100644
--- a/lld/ELF/Arch/SPARCV9.cpp
+++ b/lld/ELF/Arch/SPARCV9.cpp
@@ -25,6 +25,7 @@ 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 writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
template <class ELFT, class RelTy>
@@ -42,6 +43,7 @@ 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;
pltEntrySize = 32;
pltHeaderSize = 4 * pltEntrySize;
@@ -72,6 +74,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);
@@ -92,18 +100,24 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
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;
@@ -113,15 +127,22 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
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:
expr = R_GOT_OFF;
break;
@@ -147,70 +168,119 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
void SPARCV9::relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
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:
// 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:
+ // T-simm13
+ write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff));
+ break;
case R_SPARC_GOT22:
- case R_SPARC_PC22:
case R_SPARC_LM22:
// 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:
+ // 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,16 +289,12 @@ 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_LE_HIX22:
// 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);
- break;
default:
llvm_unreachable("unknown relocation");
}
diff --git a/lld/test/ELF/sparcv9-reloc-additional.s b/lld/test/ELF/sparcv9-reloc-additional.s
new file mode 100644
index 0000000000000..75d490f18d1c6
--- /dev/null
+++ b/lld/test/ELF/sparcv9-reloc-additional.s
@@ -0,0 +1,41 @@
+# REQUIRES: sparc
+# RUN: llvm-mc -filetype=obj -triple=sparcv9 %s -o %t.o
+# RUN: ld.lld %t.o --defsym=abs8=0x7f --defsym=abs16=0x1234 \
+# RUN: --defsym=abs32=0x12345678 --defsym=abs64=0x123456789abcdef \
+# RUN: --defsym=small=0x123 --defsym=neg1=0xffffffffffffffff \
+# RUN: -o %t
+# RUN: llvm-objdump -s --section=.data %t | FileCheck --check-prefix=HEX %s
+# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s
+
+## R_SPARC_8, R_SPARC_16, R_SPARC_UA16, R_SPARC_32, R_SPARC_UA32,
+## R_SPARC_64, and R_SPARC_UA64.
+# HEX: Contents of section .data:
+# HEX-NEXT: {{[0-9a-f]+}} 7f123412 34123456 78123456 78012345
+# HEX-NEXT: {{[0-9a-f]+}} 6789abcd ef
+.data
+ .byte abs8
+ .half abs16
+ .uahalf abs16
+ .word abs32
+ .uaword abs32
+ .xword abs64
+
+## R_SPARC_13, R_SPARC_HIX22, R_SPARC_LOX10, R_SPARC_WDISP16,
+## R_SPARC_WDISP22, and R_SPARC_DISP64.
+# CHECK-LABEL: section .text:
+# CHECK: mov 0x123, %g1
+# CHECK: sethi 0x0, %g2
+# CHECK-NEXT: xor %g2, -0x1, %g2
+# CHECK: ba,a
+# CHECK: brnz
+.text
+.globl _start
+_start:
+ or %g0, small, %g1
+ sethi %hix(neg1), %g2
+ xor %g2, %lox(neg1), %g2
+ ba,a target
+ brnz %g1, target
+ .xword target - .
+target:
+ nop
>From ed47ff52ac29271bb25eff7a52d2313fd5abedde Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill at korins.ky>
Date: Sun, 5 Jul 2026 21:40:31 +0200
Subject: [PATCH 3/7] [lld][ELF][SPARC] Support SPARCV9 TLS relocations
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: LemonBoy <thatlemon at gmail.com>
Co-authored-by: Alex Rønne Petersen <alex at alexrp.com>
---
lld/ELF/Arch/SPARCV9.cpp | 84 ++++++++++++++++++++++++++++++
lld/test/ELF/sparcv9-tls-dynamic.s | 39 ++++++++++++++
2 files changed, 123 insertions(+)
create mode 100644 lld/test/ELF/sparcv9-tls-dynamic.s
diff --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index 36f309e07e4a2..c673bb0135367 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"
@@ -35,6 +37,7 @@ class SPARCV9 final : public TargetInfo {
}
void relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
+ void finalizeRelocScan() override;
};
} // namespace
@@ -45,6 +48,9 @@ SPARCV9::SPARCV9(Ctx &ctx) : TargetInfo(ctx) {
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;
pltEntrySize = 32;
pltHeaderSize = 4 * pltEntrySize;
@@ -97,6 +103,12 @@ 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:
@@ -125,6 +137,10 @@ 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_DISP8:
@@ -154,6 +170,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)
@@ -202,6 +237,8 @@ void SPARCV9::relocate(uint8_t *loc, const Relocation &rel,
break;
case R_SPARC_WDISP30:
case R_SPARC_WPLT30:
+ case R_SPARC_TLS_GD_CALL:
+ case R_SPARC_TLS_LDM_CALL:
// V-disp30
checkIntUInt(ctx, loc, val, 32, rel);
write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff));
@@ -230,11 +267,18 @@ void SPARCV9::relocate(uint8_t *loc, const Relocation &rel,
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_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;
@@ -291,15 +335,55 @@ void SPARCV9::relocate(uint8_t *loc, const Relocation &rel,
// T-imm13
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_LDO_LOX10:
+ // T-simm13
+ write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff));
+ break;
default:
llvm_unreachable("unknown relocation");
}
}
+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/test/ELF/sparcv9-tls-dynamic.s b/lld/test/ELF/sparcv9-tls-dynamic.s
new file mode 100644
index 0000000000000..7f6525702f4d0
--- /dev/null
+++ b/lld/test/ELF/sparcv9-tls-dynamic.s
@@ -0,0 +1,39 @@
+# 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
+
+# CHECK: R_SPARC_TLS_DTPMOD64 0
+# CHECK: R_SPARC_TLS_DTPMOD64 {{.*}} a
+# CHECK: R_SPARC_TLS_DTPOFF64 {{.*}} a
+# CHECK: R_SPARC_TLS_TPOFF64 {{.*}} b
+# CHECK: R_SPARC_JMP_SLOT {{.*}} __tls_get_addr
+
+.text
+.globl _start
+_start:
+ sethi %tgd_hi22(a), %i1
+ add %i1, %tgd_lo10(a), %i1
+ add %i0, %i1, %o0, %tgd_add(a)
+ call __tls_get_addr, %tgd_call(a)
+ nop
+
+ sethi %tldm_hi22(a), %i2
+ add %i2, %tldm_lo10(a), %i2
+ add %i0, %i2, %o0, %tldm_add(a)
+ call __tls_get_addr, %tldm_call(a)
+ nop
+
+ sethi %tie_hi22(b), %i3
+ add %i3, %tie_lo10(b), %i3
+ ldx [%i0 + %i3], %i3, %tie_ldx(b)
+ add %i3, %g7, %i3, %tie_add(b)
+
+.section .tbss,"awT", at nobits
+.globl a
+a:
+ .zero 8
+
+.globl b
+b:
+ .zero 8
>From 7edaf932c27f517f55acc25aad540542ea03643e Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill at korins.ky>
Date: Sun, 5 Jul 2026 21:44:10 +0200
Subject: [PATCH 4/7] [lld][ELF][SPARC] Relax SPARC GOTDATA_OP relocations
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: LemonBoy <thatlemon at gmail.com>
Co-authored-by: Alex Rønne Petersen <alex at alexrp.com>
---
lld/ELF/Arch/SPARCV9.cpp | 70 ++++++++++++++++++++++++++++
lld/ELF/InputSection.cpp | 1 +
lld/ELF/Relocations.cpp | 17 ++++---
lld/ELF/Relocations.h | 1 +
lld/ELF/Target.cpp | 6 +++
lld/ELF/Target.h | 2 +
lld/test/ELF/sparcv9-gotdata-relax.s | 37 +++++++++++++++
7 files changed, 128 insertions(+), 6 deletions(-)
create mode 100644 lld/test/ELF/sparcv9-gotdata-relax.s
diff --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index c673bb0135367..fb57b73d8c1bc 100644
--- a/lld/ELF/Arch/SPARCV9.cpp
+++ b/lld/ELF/Arch/SPARCV9.cpp
@@ -38,6 +38,11 @@ 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
@@ -160,8 +165,15 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
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:
@@ -202,6 +214,13 @@ 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
@@ -319,6 +338,8 @@ void SPARCV9::relocate(uint8_t *loc, const Relocation &rel,
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;
@@ -350,6 +371,55 @@ void SPARCV9::relocate(uint8_t *loc, const Relocation &rel,
// T-simm13
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");
}
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..fb48f56e5963e 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);
}
@@ -920,14 +920,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/Target.cpp b/lld/ELF/Target.cpp
index 89e4dbeed3109..6ad4904d2546a 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -149,6 +149,12 @@ RelExpr TargetInfo::adjustGotPcExpr(RelType type, int64_t addend,
return R_GOT_PC;
}
+RelExpr TargetInfo::adjustGotOffExpr(RelType type, const Symbol &sym,
+ int64_t addend,
+ const uint8_t *data) const {
+ return R_GOT_OFF;
+}
+
static void relocateImpl(const TargetInfo &target, InputSectionBase &sec,
uint64_t secAddr, uint8_t *buf) {
auto &ctx = target.ctx;
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 199f76e3fe851..70a63f4f0829a 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -195,6 +195,8 @@ class TargetInfo {
virtual RelExpr adjustTlsExpr(RelType type, RelExpr expr) const;
virtual RelExpr adjustGotPcExpr(RelType type, int64_t addend,
const uint8_t *loc) const;
+ virtual RelExpr adjustGotOffExpr(RelType type, const Symbol &sym,
+ int64_t addend, const uint8_t *loc) const;
protected:
// On FreeBSD x86_64 the first page cannot be mmaped.
diff --git a/lld/test/ELF/sparcv9-gotdata-relax.s b/lld/test/ELF/sparcv9-gotdata-relax.s
new file mode 100644
index 0000000000000..cf6826ceaff34
--- /dev/null
+++ b/lld/test/ELF/sparcv9-gotdata-relax.s
@@ -0,0 +1,37 @@
+# REQUIRES: sparc
+# RUN: llvm-mc -filetype=obj -triple=sparcv9 %s -o %t.o
+# RUN: ld.lld -shared %t.o -o %t.so
+# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck %s
+# RUN: llvm-readelf -r %t.so | FileCheck --check-prefix=REL %s
+
+# CHECK-LABEL: <local_ref>:
+# CHECK: add %l7, %l1, %l2
+# CHECK-LABEL: <extern_ref>:
+# CHECK: ldx [%l7+%l1], %l2
+
+# REL: R_SPARC_GLOB_DAT {{.*}} extern
+# REL-NOT: local
+
+.text
+.globl local_ref
+.type local_ref, at function
+local_ref:
+ sethi %gdop_hix22(local), %l1
+ or %l1, %gdop_lox10(local), %l1
+ ldx [%l7 + %l1], %l2, %gdop(local)
+ retl
+ nop
+
+.globl extern_ref
+.type extern_ref, at function
+extern_ref:
+ sethi %gdop_hix22(extern), %l1
+ or %l1, %gdop_lox10(extern), %l1
+ ldx [%l7 + %l1], %l2, %gdop(extern)
+ retl
+ nop
+
+.data
+.hidden local
+local:
+ .xword 0
>From fe45a6ab3fa201532d90d757de2fcd48277ee0f1 Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill at korins.ky>
Date: Sun, 5 Jul 2026 21:47:07 +0200
Subject: [PATCH 5/7] [lld][ELF][SPARC] Add SPARC GOT header entry
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: LemonBoy <thatlemon at gmail.com>
Co-authored-by: Alex Rønne Petersen <alex at alexrp.com>
---
lld/ELF/Arch/SPARCV9.cpp | 6 ++++++
lld/test/ELF/sparcv9-got-header.s | 11 +++++++++++
2 files changed, 17 insertions(+)
create mode 100644 lld/test/ELF/sparcv9-got-header.s
diff --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index fb57b73d8c1bc..bfb773178e6c2 100644
--- a/lld/ELF/Arch/SPARCV9.cpp
+++ b/lld/ELF/Arch/SPARCV9.cpp
@@ -28,6 +28,7 @@ class SPARCV9 final : public TargetInfo {
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>
@@ -56,6 +57,7 @@ SPARCV9::SPARCV9(Ctx &ctx) : TargetInfo(ctx) {
tlsGotRel = R_SPARC_TLS_TPOFF64;
tlsModuleIndexRel = R_SPARC_TLS_DTPMOD64;
tlsOffsetRel = R_SPARC_TLS_DTPOFF64;
+ gotHeaderEntriesNum = 1;
pltEntrySize = 32;
pltHeaderSize = 4 * pltEntrySize;
@@ -425,6 +427,10 @@ void SPARCV9::relaxGot(uint8_t *loc, const Relocation &rel,
}
}
+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;
}
diff --git a/lld/test/ELF/sparcv9-got-header.s b/lld/test/ELF/sparcv9-got-header.s
new file mode 100644
index 0000000000000..d4630104e5fba
--- /dev/null
+++ b/lld/test/ELF/sparcv9-got-header.s
@@ -0,0 +1,11 @@
+# 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 -S -x .got %t.so | FileCheck %s
+
+# CHECK: .got PROGBITS {{[0-9a-f]+}} {{[0-9a-f]+}} 000008
+# CHECK-LABEL: Hex dump of section '.got':
+# CHECK-NEXT: 0x{{[0-9a-f]+}} {{[0-9a-f ]*[1-9a-f][0-9a-f ]*}}
+
+.data
+.xword _GLOBAL_OFFSET_TABLE_
>From 2dac2cc0649bc922f8cce40c22af3a95ceaff768 Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill at korins.ky>
Date: Sun, 5 Jul 2026 21:47:16 +0200
Subject: [PATCH 6/7] [lld][ELF][SPARC] Apply SPARC PLT relocations to .plt
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: LemonBoy <thatlemon at gmail.com>
Co-authored-by: Alex Rønne Petersen <alex at alexrp.com>
---
lld/ELF/Arch/SPARCV9.cpp | 1 +
lld/ELF/Relocations.cpp | 14 ++++++++------
lld/ELF/Symbols.cpp | 6 ++++++
lld/ELF/Symbols.h | 1 +
lld/ELF/SyntheticSections.cpp | 21 +++++++++++++++------
lld/ELF/Target.h | 5 ++++-
lld/ELF/Writer.cpp | 10 +++++++---
lld/test/ELF/sparcv9-plt.s | 23 +++++++++++++++++++++++
8 files changed, 65 insertions(+), 16 deletions(-)
create mode 100644 lld/test/ELF/sparcv9-plt.s
diff --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index bfb773178e6c2..25ab1279c8a01 100644
--- a/lld/ELF/Arch/SPARCV9.cpp
+++ b/lld/ELF/Arch/SPARCV9.cpp
@@ -60,6 +60,7 @@ SPARCV9::SPARCV9(Ctx &ctx) : TargetInfo(ctx) {
gotHeaderEntriesNum = 1;
pltEntrySize = 32;
pltHeaderSize = 4 * pltEntrySize;
+ usesGotPlt = false;
defaultCommonPageSize = 8192;
defaultMaxPageSize = 0x100000;
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index fb48f56e5963e..c0c11b0b81fb4 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -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
+ if (ctx.target->usesGotPlt) {
+ gotPlt.addEntry(sym);
+ rel.addReloc({type, &gotPlt, sym.getGotPltOffset(ctx), sym.isPreemptible,
+ sym, 0, expr});
+ } else {
rel.addReloc(
- {type, &gotPlt, sym.getGotPltOffset(ctx), false, sym, 0, R_ABS});
+ {type, &plt, sym.getPltOffset(ctx), sym.isPreemptible, sym, 0, expr});
+ }
}
void elf::addGotEntry(Ctx &ctx, Symbol &sym) {
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..bc1c2c042618a 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1523,9 +1523,14 @@ 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;
+ }
}
}
@@ -2402,8 +2407,10 @@ 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) {
@@ -4529,10 +4536,12 @@ template <class ELFT> void elf::createSyntheticSections(Ctx &ctx) {
// _GLOBAL_OFFSET_TABLE_ is defined relative to either .got.plt or .got. Treat
// it as a relocation and ensure the referenced section is created.
if (ctx.sym.globalOffsetTable && ctx.arg.emachine != EM_MIPS) {
- if (ctx.target->gotBaseSymInGotPlt)
+ if (ctx.target->gotBaseSymInGotPlt) {
+ assert(ctx.target->usesGotPlt);
ctx.in.gotPlt->hasGotPltOffRel = true;
- else
+ } else {
ctx.in.got->hasGotOffRel = true;
+ }
}
// We always need to add rel[a].plt to output if it has entries.
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 70a63f4f0829a..73ab71abbdca3 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -144,7 +144,8 @@ class TargetInfo {
uint64_t getImageBase() const;
- // True if _GLOBAL_OFFSET_TABLE_ is relative to .got.plt, false if .got.
+ // True if _GLOBAL_OFFSET_TABLE_ is relative to .got.plt, false if .got. If
+ // true, usesGotPlt must also be true.
bool gotBaseSymInGotPlt = false;
static constexpr RelType noneRel = 0;
@@ -171,6 +172,8 @@ class TargetInfo {
// On PPC ELF V2 abi, the first entry in the .got is the .TOC.
unsigned gotHeaderEntriesNum = 0;
+ bool usesGotPlt = true;
+
// On PPC ELF V2 abi, the dynamic section needs DT_PPC64_OPT (DT_LOPROC + 3)
// to be set to 0x2 if there can be multiple TOC's. Although we do not emit
// multiple TOC's, there can be a mix of TOC and NOTOC addressing which
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 49d3f903b3743..37338f53cf9b6 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -609,7 +609,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())
@@ -835,10 +835,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/sparcv9-plt.s b/lld/test/ELF/sparcv9-plt.s
new file mode 100644
index 0000000000000..3b18e24206d9c
--- /dev/null
+++ b/lld/test/ELF/sparcv9-plt.s
@@ -0,0 +1,23 @@
+# REQUIRES: sparc
+# RUN: llvm-mc --position-independent -filetype=obj -triple=sparcv9 %s -o %t.o
+# RUN: ld.lld -shared %t.o -o %t.so
+# RUN: llvm-readelf -SW -r %t.so | FileCheck --check-prefix=READELF %s
+# RUN: llvm-readobj --section-headers %t.so | FileCheck --check-prefix=SEC %s
+
+# READELF-NOT: .got.plt
+# READELF: .plt PROGBITS {{[0-9a-f]+}} {{[0-9a-f]+}} {{[0-9a-f]+}} 00 WAX 0 0 256
+# READELF-NOT: .got.plt
+# READELF: Relocation section '.rela.plt'
+# READELF: R_SPARC_JMP_SLOT {{.*}} foo
+
+# SEC: Name: .rela.plt
+# SEC: Flags [
+# SEC-NEXT: SHF_ALLOC
+# SEC-NEXT: SHF_INFO_LINK
+# SEC-NEXT: ]
+
+.text
+.globl _start
+_start:
+ call foo
+ nop
>From d122eb687eb95c0cf99c27cc00e9965a84a3dd40 Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill at korins.ky>
Date: Sun, 5 Jul 2026 21:47:17 +0200
Subject: [PATCH 7/7] [lld][ELF][SPARC] Convert aligned SPARC UA64 dynamic
relocs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: LemonBoy <thatlemon at gmail.com>
Co-authored-by: Alex Rønne Petersen <alex at alexrp.com>
---
lld/ELF/SyntheticSections.cpp | 12 ++++++++++++
lld/test/ELF/sparcv9-ua64-dynrel.s | 29 +++++++++++++++++++++++++++++
2 files changed, 41 insertions(+)
create mode 100644 lld/test/ELF/sparcv9-ua64-dynrel.s
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index bc1c2c042618a..aa5fabe9f9cbc 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1536,6 +1536,18 @@ void RelocationBaseSection::finalizeContents() {
void DynamicReloc::finalize(Ctx &ctx, SymbolTableBaseSection *symt) {
r_offset = getOffset();
+ if (ctx.arg.emachine == EM_SPARCV9 && type == R_SPARC_UA64 &&
+ needsDynSymIndex() && !sym->isPreemptible) {
+ if (r_offset % 8 != 0) {
+ Err(ctx) << "R_SPARC_UA64 relocation at offset " << r_offset
+ << " against non-preemptible symbol " << sym
+ << " is not 8-byte aligned";
+ } else {
+ type = ctx.target->relativeRel;
+ isAgainstSymbol = false;
+ expr = R_ABS;
+ }
+ }
r_sym = getSymIndex(symt);
addend = computeAddend(ctx);
isFinal = true; // Catch errors
diff --git a/lld/test/ELF/sparcv9-ua64-dynrel.s b/lld/test/ELF/sparcv9-ua64-dynrel.s
new file mode 100644
index 0000000000000..9fcab99365bcc
--- /dev/null
+++ b/lld/test/ELF/sparcv9-ua64-dynrel.s
@@ -0,0 +1,29 @@
+# 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-NOT: R_SPARC_UA64
+
+# ERR: R_SPARC_UA64 relocation at offset {{[0-9]+}} against non-preemptible symbol local is not 8-byte aligned
+
+.data
+.p2align 3
+aligned:
+ .xword 0
+ .reloc aligned, R_SPARC_UA64, local
+
+.ifdef ERR
+ .byte 0
+unaligned:
+ .xword 0
+ .reloc unaligned, R_SPARC_UA64, local
+.endif
+
+.hidden local
+local:
+ .xword 0
More information about the llvm-commits
mailing list