[lld] 5e87f81 - [ELF] Add target-specific relocation scanning for PPC32 (#181517)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Feb 14 17:13:40 PST 2026
Author: Fangrui Song
Date: 2026-02-15T01:13:35Z
New Revision: 5e87f8147d680f340948d428f44b93a239a22464
URL: https://github.com/llvm/llvm-project/commit/5e87f8147d680f340948d428f44b93a239a22464
DIFF: https://github.com/llvm/llvm-project/commit/5e87f8147d680f340948d428f44b93a239a22464.diff
LOG: [ELF] Add target-specific relocation scanning for PPC32 (#181517)
Implement PPC::scanSectionImpl, following the pattern established for
x86 (#178846) and PPC64 (#181496). This merges the getRelExpr and TLS
handling for SHF_ALLOC sections into the target-specific scanner,
enabling devirtualization and eliminating abstraction overhead.
- Inline relocation classification into scanSectionImpl with a switch
on relocation type, replacing the generic rs.scan() path.
- Use processR_PC/processR_PLT_PC for common PC-relative and PLT
relocations.
- Handle R_PPC_PLTREL24 inline with addend masking via processAux,
removing the EM_PPC special case from process().
- Handle TLS GD/LD/IE directly, eliminating handleTlsRelocation,
getTlsGdRelaxSkip, and adjustTlsExpr overrides. Use handleTlsIe
for TLS IE, and handleTlsGd for R_PPC_GOT_TLSGD16.
- Use R_DTPREL unconditionally for DTPREL relocations, removing
R_RELAX_TLS_LD_TO_LE_ABS (PPC32 was the only user).
- Move TLS relaxation dispatch from relocateAlloc into relocate,
removing the relocateAlloc override.
- Simplify getRelExpr to only handle relocations needed by
relocateNonAlloc and .eh_frame.
Added:
Modified:
lld/ELF/Arch/PPC.cpp
lld/ELF/InputSection.cpp
lld/ELF/Relocations.cpp
lld/ELF/Relocations.h
Removed:
################################################################################
diff --git a/lld/ELF/Arch/PPC.cpp b/lld/ELF/Arch/PPC.cpp
index 5972698b34a2c..d517125d16a3d 100644
--- a/lld/ELF/Arch/PPC.cpp
+++ b/lld/ELF/Arch/PPC.cpp
@@ -6,7 +6,9 @@
//
//===----------------------------------------------------------------------===//
+#include "InputFiles.h"
#include "OutputSections.h"
+#include "RelocScan.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
@@ -40,6 +42,9 @@ class PPC final : public TargetInfo {
void writeIplt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
+ template <class ELFT, class RelTy>
+ void scanSectionImpl(InputSectionBase &, Relocs<RelTy>);
+ void scanSection(InputSectionBase &) override;
bool needsThunk(RelExpr expr, RelType relocType, const InputFile *file,
uint64_t branchAddr, const Symbol &s,
int64_t a) const override;
@@ -47,10 +52,6 @@ class PPC final : public TargetInfo {
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
void relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
- RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override;
- int getTlsGdRelaxSkip(RelType type) const override;
- void relocateAlloc(InputSection &sec, uint8_t *buf) const override;
-
private:
void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
@@ -219,53 +220,19 @@ bool PPC::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
llvm_unreachable("unsupported relocation type used in branch");
}
+// Only needed to support relocations used by relocateNonAlloc and
+// preprocessRelocs.
RelExpr PPC::getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const {
switch (type) {
case R_PPC_NONE:
return R_NONE;
- case R_PPC_ADDR16_HA:
- case R_PPC_ADDR16_HI:
- case R_PPC_ADDR16_LO:
- case R_PPC_ADDR24:
case R_PPC_ADDR32:
return R_ABS;
- case R_PPC_DTPREL16:
- case R_PPC_DTPREL16_HA:
- case R_PPC_DTPREL16_HI:
- case R_PPC_DTPREL16_LO:
case R_PPC_DTPREL32:
return R_DTPREL;
- case R_PPC_REL14:
case R_PPC_REL32:
- case R_PPC_REL16_LO:
- case R_PPC_REL16_HI:
- case R_PPC_REL16_HA:
return R_PC;
- case R_PPC_GOT16:
- return R_GOT_OFF;
- case R_PPC_LOCAL24PC:
- case R_PPC_REL24:
- return R_PLT_PC;
- case R_PPC_PLTREL24:
- return RE_PPC32_PLTREL;
- case R_PPC_GOT_TLSGD16:
- return R_TLSGD_GOT;
- case R_PPC_GOT_TLSLD16:
- return R_TLSLD_GOT;
- case R_PPC_GOT_TPREL16:
- return R_GOT_OFF;
- case R_PPC_TLS:
- return R_TLSIE_HINT;
- case R_PPC_TLSGD:
- return R_TLSDESC_CALL;
- case R_PPC_TLSLD:
- return R_TLSLD_HINT;
- case R_PPC_TPREL16:
- case R_PPC_TPREL16_HA:
- case R_PPC_TPREL16_LO:
- case R_PPC_TPREL16_HI:
- return R_TPREL;
default:
Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v
<< ") against symbol " << &s;
@@ -299,6 +266,134 @@ int64_t PPC::getImplicitAddend(const uint8_t *buf, RelType type) const {
}
}
+template <class ELFT, class RelTy>
+void PPC::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
+ RelocScan rs(ctx, &sec);
+ sec.relocations.reserve(rels.size());
+ for (auto it = rels.begin(); it != rels.end(); ++it) {
+ const RelTy &rel = *it;
+ uint32_t symIdx = rel.getSymbol(false);
+ Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIdx);
+ uint64_t offset = rel.r_offset;
+ RelType type = rel.getType(false);
+ if (sym.isUndefined() && symIdx != 0 &&
+ rs.maybeReportUndefined(cast<Undefined>(sym), offset))
+ continue;
+ int64_t addend = rs.getAddend<ELFT>(rel, type);
+ RelExpr expr;
+ // Relocation types that only need a RelExpr set `expr` and break out of
+ // the switch to reach rs.process(). Types that need special handling
+ // (fast-path helpers, TLS) call a handler and use `continue`.
+ switch (type) {
+ case R_PPC_NONE:
+ continue;
+ // Absolute relocations:
+ case R_PPC_ADDR16_HA:
+ case R_PPC_ADDR16_HI:
+ case R_PPC_ADDR16_LO:
+ case R_PPC_ADDR24:
+ case R_PPC_ADDR32:
+ expr = R_ABS;
+ break;
+
+ // PC-relative relocations:
+ case R_PPC_REL14:
+ case R_PPC_REL32:
+ case R_PPC_REL16_LO:
+ case R_PPC_REL16_HI:
+ case R_PPC_REL16_HA:
+ rs.processR_PC(type, offset, addend, sym);
+ continue;
+
+ // GOT-generating relocation:
+ case R_PPC_GOT16:
+ expr = R_GOT_OFF;
+ break;
+
+ // PLT-generating relocations:
+ case R_PPC_LOCAL24PC:
+ case R_PPC_REL24:
+ rs.processR_PLT_PC(type, offset, addend, sym);
+ continue;
+ case R_PPC_PLTREL24:
+ ctx.in.got->hasGotOffRel.store(true, std::memory_order_relaxed);
+ if (LLVM_UNLIKELY(sym.isGnuIFunc())) {
+ rs.process(RE_PPC32_PLTREL, type, offset, sym, addend);
+ } else if (sym.isPreemptible) {
+ sym.setFlags(NEEDS_PLT);
+ sec.addReloc({RE_PPC32_PLTREL, type, offset, addend, &sym});
+ } else {
+ // The 0x8000 bit of r_addend selects call stub type; mask it for direct
+ // calls.
+ addend &= ~0x8000;
+ rs.processAux(R_PC, type, offset, sym, addend);
+ }
+ continue;
+
+ // TLS relocations:
+
+ // TLS LE:
+ case R_PPC_TPREL16:
+ case R_PPC_TPREL16_HA:
+ case R_PPC_TPREL16_LO:
+ case R_PPC_TPREL16_HI:
+ if (rs.checkTlsLe(offset, sym, type))
+ continue;
+ expr = R_TPREL;
+ break;
+
+ // TLS IE:
+ case R_PPC_GOT_TPREL16:
+ rs.handleTlsIe(R_GOT_OFF, type, offset, addend, sym);
+ continue;
+ case R_PPC_TLS:
+ if (!ctx.arg.shared && !sym.isPreemptible)
+ sec.addReloc({R_TPREL, type, offset, addend, &sym});
+ continue;
+
+ // TLS GD:
+ case R_PPC_GOT_TLSGD16:
+ rs.handleTlsGd(R_TLSGD_GOT, R_GOT_OFF, R_TPREL, type, offset, addend,
+ sym);
+ continue;
+ case R_PPC_TLSGD:
+ case R_PPC_TLSLD:
+ if (!ctx.arg.shared) {
+ sec.addReloc({sym.isPreemptible ? R_GOT_OFF : R_TPREL, type, offset,
+ addend, &sym});
+ ++it; // Skip REL24
+ }
+ continue;
+
+ // TLS LD:
+ case R_PPC_GOT_TLSLD16:
+ rs.handleTlsLd(R_TLSLD_GOT, type, offset, addend, sym);
+ continue;
+ case R_PPC_DTPREL16:
+ case R_PPC_DTPREL16_HA:
+ case R_PPC_DTPREL16_HI:
+ case R_PPC_DTPREL16_LO:
+ case R_PPC_DTPREL32:
+ sec.addReloc({R_DTPREL, type, offset, addend, &sym});
+ continue;
+
+ default:
+ Err(ctx) << getErrorLoc(ctx, sec.content().data() + offset)
+ << "unknown relocation (" << type.v << ") against symbol "
+ << &sym;
+ continue;
+ }
+ rs.process(expr, type, offset, sym, addend);
+ }
+}
+
+void PPC::scanSection(InputSectionBase &sec) {
+ if (ctx.arg.isLE)
+ elf::scanSection1<PPC, ELF32LE>(*this, sec);
+ else
+ elf::scanSection1<PPC, ELF32BE>(*this, sec);
+}
+
static std::pair<RelType, uint64_t> fromDTPREL(RelType type, uint64_t val) {
uint64_t dtpBiasedVal = val - 0x8000;
switch (type) {
@@ -326,13 +421,36 @@ void PPC::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
write16(ctx, loc, val);
break;
case R_PPC_GOT16:
- case R_PPC_GOT_TLSGD16:
- case R_PPC_GOT_TLSLD16:
- case R_PPC_GOT_TPREL16:
case R_PPC_TPREL16:
checkInt(ctx, loc, val, 16, rel);
write16(ctx, loc, val);
break;
+ case R_PPC_GOT_TLSGD16:
+ if (rel.expr == R_TPREL)
+ relaxTlsGdToLe(loc, rel, val);
+ else if (rel.expr == R_GOT_OFF)
+ relaxTlsGdToIe(loc, rel, val);
+ else {
+ checkInt(ctx, loc, val, 16, rel);
+ write16(ctx, loc, val);
+ }
+ break;
+ case R_PPC_GOT_TLSLD16:
+ if (rel.expr == R_TPREL)
+ relaxTlsLdToLe(loc, rel, val);
+ else {
+ checkInt(ctx, loc, val, 16, rel);
+ write16(ctx, loc, val);
+ }
+ break;
+ case R_PPC_GOT_TPREL16:
+ if (rel.expr == R_TPREL)
+ relaxTlsIeToLe(loc, rel, val);
+ else {
+ checkInt(ctx, loc, val, 16, rel);
+ write16(ctx, loc, val);
+ }
+ break;
case R_PPC_ADDR16_HA:
case R_PPC_DTPREL16_HA:
case R_PPC_GOT_TLSGD16_HA:
@@ -381,33 +499,25 @@ void PPC::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
write32(ctx, loc, (read32(ctx, loc) & ~mask) | (val & mask));
break;
}
+ case R_PPC_TLSGD:
+ if (rel.expr == R_TPREL)
+ relaxTlsGdToLe(loc, rel, val);
+ else if (rel.expr == R_GOT_OFF)
+ relaxTlsGdToIe(loc, rel, val);
+ break;
+ case R_PPC_TLSLD:
+ if (rel.expr == R_TPREL)
+ relaxTlsLdToLe(loc, rel, val);
+ break;
+ case R_PPC_TLS:
+ if (rel.expr == R_TPREL)
+ relaxTlsIeToLe(loc, rel, val);
+ break;
default:
llvm_unreachable("unknown relocation");
}
}
-RelExpr PPC::adjustTlsExpr(RelType type, RelExpr expr) const {
- if (expr == R_RELAX_TLS_GD_TO_IE)
- return R_RELAX_TLS_GD_TO_IE_GOT_OFF;
- if (expr == R_RELAX_TLS_LD_TO_LE)
- return R_RELAX_TLS_LD_TO_LE_ABS;
- return expr;
-}
-
-int PPC::getTlsGdRelaxSkip(RelType type) const {
- // A __tls_get_addr call instruction is marked with 2 relocations:
- //
- // R_PPC_TLSGD / R_PPC_TLSLD: marker relocation
- // R_PPC_REL24: __tls_get_addr
- //
- // After the relaxation we no longer call __tls_get_addr and should skip both
- // relocations to not create a false dependence on __tls_get_addr being
- // defined.
- if (type == R_PPC_TLSGD || type == R_PPC_TLSLD)
- return 2;
- return 1;
-}
-
void PPC::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
switch (rel.type) {
@@ -456,12 +566,6 @@ void PPC::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
// bl __tls_get_addr(x at tlsld) --> addi r3, r3, 4096
write32(ctx, loc, 0x38631000);
break;
- case R_PPC_DTPREL16:
- case R_PPC_DTPREL16_HA:
- case R_PPC_DTPREL16_HI:
- case R_PPC_DTPREL16_LO:
- relocate(loc, rel, val);
- break;
default:
llvm_unreachable("unsupported relocation for TLS LD to LE relaxation");
}
@@ -496,30 +600,4 @@ void PPC::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
}
}
-void PPC::relocateAlloc(InputSection &sec, uint8_t *buf) const {
- uint64_t secAddr = sec.getOutputSection()->addr + sec.outSecOff;
- for (const Relocation &rel : sec.relocs()) {
- uint8_t *loc = buf + rel.offset;
- const uint64_t val =
- SignExtend64(sec.getRelocTargetVA(ctx, rel, secAddr + rel.offset), 32);
- switch (rel.expr) {
- case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
- relaxTlsGdToIe(loc, rel, val);
- break;
- case R_RELAX_TLS_GD_TO_LE:
- relaxTlsGdToLe(loc, rel, val);
- break;
- case R_RELAX_TLS_LD_TO_LE_ABS:
- relaxTlsLdToLe(loc, rel, val);
- break;
- case R_RELAX_TLS_IE_TO_LE:
- relaxTlsIeToLe(loc, rel, val);
- break;
- default:
- relocate(loc, rel, val);
- break;
- }
- }
-}
-
void elf::setPPCTargetInfo(Ctx &ctx) { ctx.target.reset(new PPC(ctx)); }
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index 767293ad75641..c71bb6c2c201c 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -806,7 +806,6 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
switch (r.expr) {
case R_ABS:
case R_DTPREL:
- case R_RELAX_TLS_LD_TO_LE_ABS:
case R_RELAX_GOT_PC_NOPIC:
case RE_AARCH64_AUTH:
case RE_RISCV_ADD:
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index ce1332ffe7104..1827e4800cd7f 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -926,10 +926,6 @@ void RelocScan::process(RelExpr expr, RelType type, uint64_t offset,
const bool isIfunc = sym.isGnuIFunc();
if (!sym.isPreemptible && !isIfunc) {
if (expr != R_GOT_PC) {
- // 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.
if (!(ctx.arg.emachine == EM_HEXAGON &&
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index 6d31fb20a7d5a..7ba94a4bef849 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -71,7 +71,6 @@ enum RelExpr {
R_RELAX_TLS_GD_TO_LE_NEG,
R_RELAX_TLS_IE_TO_LE,
R_RELAX_TLS_LD_TO_LE,
- R_RELAX_TLS_LD_TO_LE_ABS,
R_SIZE,
R_TPREL,
R_TPREL_NEG,
More information about the llvm-commits
mailing list