[lld] [LLD][SPARC] Implement enough functionality to run non-trivial 64-bit programs (PR #137919)
Alex Rønne Petersen via llvm-commits
llvm-commits at lists.llvm.org
Sat May 10 04:43:23 PDT 2025
https://github.com/alexrp updated https://github.com/llvm/llvm-project/pull/137919
>From 24f71d45ff2231623daf9d77256f57ca9dc3a7c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= <alex at alexrp.com>
Date: Wed, 30 Apr 2025 05:21:11 +0200
Subject: [PATCH] [LLD][SPARC] Implement enough functionality to run
non-trivial 64-bit programs.
This allows the Zig test suite to link and run for sparc64-linux, both with
glibc and without any libc. This patch incorporates portions of the abandoned
https://reviews.llvm.org/D102985 which I came across while working on this.
Co-authored-by: LemonBoy <thatlemon at gmail.com>
---
lld/ELF/Arch/SPARCV9.cpp | 273 +++++++++++++++++++++++++++++-----
lld/ELF/Driver.cpp | 7 +-
lld/ELF/InputSection.cpp | 1 +
lld/ELF/Relocations.cpp | 55 ++++---
lld/ELF/Relocations.h | 5 +-
lld/ELF/Symbols.cpp | 6 +
lld/ELF/Symbols.h | 1 +
lld/ELF/SyntheticSections.cpp | 22 ++-
lld/ELF/Target.cpp | 6 +
lld/ELF/Target.h | 7 +-
lld/ELF/Writer.cpp | 22 +--
11 files changed, 327 insertions(+), 78 deletions(-)
diff --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index b594b6677f3ad..5e9b27928641c 100644
--- a/lld/ELF/Arch/SPARCV9.cpp
+++ b/lld/ELF/Arch/SPARCV9.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include "OutputSections.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
@@ -24,10 +25,17 @@ 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;
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:
+ void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const;
};
} // namespace
@@ -36,9 +44,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;
@@ -48,35 +63,73 @@ 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_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:
return R_ABS;
- 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_DISP64:
return R_PC;
case R_SPARC_GOT10:
- return R_GOT_OFF;
+ 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_NONE:
- return R_NONE;
+ 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;
@@ -84,73 +137,148 @@ RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
}
}
+RelType SPARCV9::getDynRel(RelType type) const {
+ if (type == symbolicRel)
+ return type;
+ return R_SPARC_NONE;
+}
+
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_22:
- // V-imm22
- checkUInt(ctx, loc, val, 22, rel);
- write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff));
+ case R_SPARC_WDISP22:
+ // V-disp22
+ checkIntUInt(ctx, loc, val, 24, rel);
+ write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 2) & 0x003fffff));
break;
- case R_SPARC_GOT22:
- case R_SPARC_PC22:
- case R_SPARC_LM22:
- // T-imm22
+ 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_HI22:
+ case R_SPARC_22:
// V-imm22
- checkUInt(ctx, loc, val >> 10, 22, rel);
- write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
+ checkUInt(ctx, loc, val, 22, rel);
+ write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff));
break;
- case R_SPARC_WDISP19:
- // V-disp19
- checkInt(ctx, loc, val, 21, rel);
- write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff));
+ 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-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:
@@ -159,21 +287,90 @@ 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:
+ 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");
}
}
+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
+ write32(ctx, buf, ctx.mainPart->dynamic->getVA());
+}
+
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 e8acdbefa32bb..b113e7e5d58ab 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1247,9 +1247,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 52c472bb89caf..1120439733824 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -806,6 +806,7 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
return ctx.in.gotPlt->getVA() + a - p;
case R_GOTREL:
case RE_PPC64_RELAX_TOC:
+ 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 277acb26987bc..be2c5f2a62ac3 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -884,12 +884,21 @@ 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) {
- plt.addEntry(sym);
- gotPlt.addEntry(sym);
- rel.addReloc({type, &gotPlt, sym.getGotPltOffset(ctx),
- sym.isPreemptible ? DynamicReloc::AgainstSymbol
- : DynamicReloc::AddendOnlyWithTargetVA,
- sym, 0, R_ABS});
+ DynamicReloc::Kind reloc = sym.isPreemptible
+ ? DynamicReloc::AgainstSymbol
+ : DynamicReloc::AddendOnlyWithTargetVA;
+
+ if (ctx.target->usesGotPlt) {
+ plt.addEntry(sym);
+ gotPlt.addEntry(sym);
+ // The relocation is applied to the .got.plt entry.
+ rel.addReloc(
+ {type, &gotPlt, sym.getGotPltOffset(ctx), reloc, sym, 0, R_ABS});
+ } else {
+ plt.addEntry(sym);
+ // The relocation is applied to the .plt entry.
+ rel.addReloc({type, &plt, sym.getPltOffset(ctx), reloc, sym, 0, R_ABS});
+ }
}
void elf::addGotEntry(Ctx &ctx, Symbol &sym) {
@@ -1053,25 +1062,32 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
// indirection.
const bool isIfunc = sym.isGnuIFunc();
if (!sym.isPreemptible && (!isIfunc || ctx.arg.zIfuncNoplt)) {
- if (expr != R_GOT_PC) {
+ if (expr != R_GOT_PC && expr != R_GOT_OFF) {
// The 0x8000 bit of r_addend of R_PPC_PLTREL24 is used to choose call
// stub type. It should be ignored if optimized to R_PC.
if (ctx.arg.emachine == EM_PPC && expr == RE_PPC32_PLTREL)
addend &= ~0x8000;
// R_HEX_GD_PLT_B22_PCREL (call a at GDPLT) is transformed into
// call __tls_get_addr even if the symbol is non-preemptible.
+ // Same deal for R_SPARC_TLS_LDM_CALL (call x at TLSPLT).
if (!(ctx.arg.emachine == EM_HEXAGON &&
(type == R_HEX_GD_PLT_B22_PCREL ||
type == R_HEX_GD_PLT_B22_PCREL_X ||
- type == R_HEX_GD_PLT_B32_PCREL_X)))
+ type == R_HEX_GD_PLT_B32_PCREL_X)) &&
+ !(ctx.arg.emachine == EM_SPARCV9 && type == R_SPARC_TLS_LDM_CALL))
expr = fromPlt(expr);
} else if (!isAbsoluteValue(sym) ||
(type == R_PPC64_PCREL_OPT && ctx.arg.emachine == EM_PPC64)) {
- 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 if (expr == R_GOT_OFF)
+ 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);
}
}
@@ -1382,12 +1398,13 @@ unsigned RelocationScanner::handleTlsRelocation(RelExpr expr, RelType type,
// ARM, Hexagon, LoongArch and RISC-V do not support GD/LD to IE/LE
// optimizations.
+ // SPARC support for GD/LD to IE/LE optimizations is not yet implemented.
// RISC-V supports TLSDESC to IE/LE optimizations.
// For PPC64, if the file has missing R_PPC64_TLSGD/R_PPC64_TLSLD, disable
// optimization as well.
bool execOptimize =
!ctx.arg.shared && ctx.arg.emachine != EM_ARM &&
- ctx.arg.emachine != EM_HEXAGON &&
+ ctx.arg.emachine != EM_HEXAGON && ctx.arg.emachine != EM_SPARCV9 &&
(ctx.arg.emachine != EM_LOONGARCH || execOptimizeInLoongArch) &&
!(isRISCV && expr != R_TLSDESC_PC && expr != R_TLSDESC_CALL) &&
!sec->file->ppc64DisableTLSRelax;
@@ -2492,11 +2509,11 @@ bool ThunkCreator::createThunks(uint32_t pass,
return addressesChanged;
}
-// The following aid in the conversion of call x at GDPLT to call __tls_get_addr
-// hexagonNeedsTLSSymbol scans for relocations would require a call to
-// __tls_get_addr.
-// hexagonTLSSymbolUpdate rebinds the relocation to __tls_get_addr.
-bool elf::hexagonNeedsTLSSymbol(ArrayRef<OutputSection *> outputSections) {
+// The following aid in the conversion of call x at GDPLT (Hexagon) and
+// call x at TLSPLT (SPARC) to call __tls_get_addr. needsTLSSymbol scans
+// for relocations that would require a call to __tls_get_addr.
+// tlsSymbolUpdate rebinds the relocation to __tls_get_addr.
+bool elf::needsTLSSymbol(ArrayRef<OutputSection *> outputSections) {
bool needTlsSymbol = false;
forEachInputSectionDescription(
outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
@@ -2510,7 +2527,7 @@ bool elf::hexagonNeedsTLSSymbol(ArrayRef<OutputSection *> outputSections) {
return needTlsSymbol;
}
-void elf::hexagonTLSSymbolUpdate(Ctx &ctx) {
+void elf::tlsSymbolUpdate(Ctx &ctx) {
Symbol *sym = ctx.symtab->find("__tls_get_addr");
if (!sym)
return;
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index d2a77bc953109..a166af8f8c1d9 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -58,6 +58,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,
@@ -160,8 +161,8 @@ void reportUndefinedSymbols(Ctx &);
void postScanRelocations(Ctx &ctx);
void addGotEntry(Ctx &ctx, Symbol &sym);
-void hexagonTLSSymbolUpdate(Ctx &ctx);
-bool hexagonNeedsTLSSymbol(ArrayRef<OutputSection *> outputSections);
+void tlsSymbolUpdate(Ctx &ctx);
+bool needsTLSSymbol(ArrayRef<OutputSection *> outputSections);
class ThunkSection;
class Thunk;
diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index fe7ba370c9f5d..e64237a250a7a 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -174,6 +174,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 64f2f6eaa8d09..9a3e4da3ba565 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -204,6 +204,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 2531227cb99b7..64f6f435d8a6e 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1499,6 +1499,7 @@ DynamicSection<ELFT>::computeContents() {
addInt(DT_RISCV_VARIANT_CC, 0);
[[fallthrough]];
default:
+ assert(ctx.target->usesGotPlt);
addInSec(DT_PLTGOT, *ctx.in.gotPlt);
break;
}
@@ -1727,9 +1728,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;
+ }
}
}
@@ -2613,8 +2619,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) {
@@ -4933,10 +4941,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.cpp b/lld/ELF/Target.cpp
index c90ef8505aadd..26634ca8e9e98 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -150,6 +150,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;
+}
+
void TargetInfo::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
const unsigned bits = ctx.arg.is64 ? 64 : 32;
uint64_t secAddr = sec.getOutputSection()->addr;
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index fd1e5d33c438a..cbfb2c9c192e1 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -120,7 +120,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;
@@ -146,6 +147,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
@@ -170,6 +173,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/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index e2aebff20e174..cbe63038ba556 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -613,7 +613,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;
}
@@ -1514,9 +1518,9 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
};
finalizeOrderDependentContent();
- // Converts call x at GDPLT to call __tls_get_addr
- if (ctx.arg.emachine == EM_HEXAGON)
- hexagonTLSSymbolUpdate(ctx);
+ // Converts call x at GDPLT/x at TLSPLT to call __tls_get_addr.
+ if (ctx.arg.emachine == EM_HEXAGON || ctx.arg.emachine == EM_SPARCV9)
+ tlsSymbolUpdate(ctx);
if (ctx.arg.randomizeSectionPadding)
randomizeSectionPadding(ctx);
@@ -1995,10 +1999,10 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
sec->addrExpr = [=] { return i->second; };
}
- // With the ctx.outputSections available check for GDPLT relocations
+ // With the ctx.outputSections available check for GDPLT/TLSPLT relocations
// and add __tls_get_addr symbol if needed.
- if (ctx.arg.emachine == EM_HEXAGON &&
- hexagonNeedsTLSSymbol(ctx.outputSections)) {
+ if ((ctx.arg.emachine == EM_HEXAGON || ctx.arg.emachine == EM_SPARCV9) &&
+ needsTLSSymbol(ctx.outputSections)) {
Symbol *sym =
ctx.symtab->addSymbol(Undefined{ctx.internalFile, "__tls_get_addr",
STB_GLOBAL, STV_DEFAULT, STT_NOTYPE});
More information about the llvm-commits
mailing list