[lld] [lld][ELF] Reject TLS relocations against non-STT_TLS symbols (PR #228418)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Oct 2 05:32:25 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-risc-v
Author: dong jianqiang (dongjianqiang2)
<details>
<summary>Changes</summary>
Per the gABI, thread-local storage relocations can only reference symbols with type STT_TLS. Previously lld silently resolved such relocations, producing wrong TP-relative offsets. Add a shared checkTlsSym invoked from every TLS relocation path (local-exec, initial-exec, general-dynamic, local-dynamic, TLSDESC, DTPREL and non-alloc sections) on all targets, reporting an error instead.
Section, undefined, and script-defined symbols are exempt: they carry no STT_TLS type in common assembler output (e.g. LoongArch/MIPS `.dtprelword .tdata+1`) and GNU ld accepts them.
Follow-up to #<!-- -->227629.
Testing: 15 test files covering all 12 TLS-capable targets (error cases for each relocation family plus the exemption tests); full lld/test/ELF passes locally with all targets enabled.
Assisted-by: Claude Code
---
Patch is 42.91 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/228418.diff
31 Files Affected:
- (modified) lld/ELF/Arch/AArch64.cpp (+6-1)
- (modified) lld/ELF/Arch/ARM.cpp (+2)
- (modified) lld/ELF/Arch/Hexagon.cpp (+10)
- (modified) lld/ELF/Arch/LoongArch.cpp (+16)
- (modified) lld/ELF/Arch/Mips.cpp (+9)
- (modified) lld/ELF/Arch/PPC.cpp (+6)
- (modified) lld/ELF/Arch/PPC64.cpp (+12)
- (modified) lld/ELF/Arch/SPARCV9.cpp (+2)
- (modified) lld/ELF/Arch/SystemZ.cpp (+6)
- (modified) lld/ELF/Arch/X86.cpp (+3)
- (modified) lld/ELF/Arch/X86_64.cpp (+4)
- (modified) lld/ELF/InputSection.cpp (+4)
- (modified) lld/ELF/RelocScan.h (+21)
- (modified) lld/ELF/Relocations.cpp (+27)
- (modified) lld/ELF/Relocations.h (+5)
- (modified) lld/test/ELF/aarch64-tls-le.s (+21)
- (added) lld/test/ELF/aarch64-tls-non-tls.s (+28)
- (added) lld/test/ELF/arm-tls-non-tls.s (+32)
- (added) lld/test/ELF/hexagon-tls-non-tls.s (+32)
- (added) lld/test/ELF/i386-tls-non-tls.s (+41)
- (added) lld/test/ELF/loongarch-tls-non-tls.s (+53)
- (added) lld/test/ELF/mips-tls-non-tls.s (+40)
- (added) lld/test/ELF/ppc-tls-non-tls.s (+35)
- (added) lld/test/ELF/ppc64-tls-non-tls.s (+41)
- (added) lld/test/ELF/riscv-tls-non-tls.s (+29)
- (added) lld/test/ELF/sparcv9-tls-non-tls.s (+23)
- (added) lld/test/ELF/systemz-tls-non-tls.s (+35)
- (added) lld/test/ELF/tls-non-tls-debug.s (+25)
- (added) lld/test/ELF/tls-non-tls-defsym.s (+12)
- (added) lld/test/ELF/tls-non-tls-weak.s (+14)
- (added) lld/test/ELF/tls-non-tls.s (+47)
``````````diff
diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 5d0c5dbd0065c..a2a2ba827045c 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -215,8 +215,10 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
// the switch to reach rs.process(). Types that need special handling
// (fast-path helpers, TLS) call a handler and use `continue`.
- auto handleTlsDescAuth = [&sym, &sec, type, offset,
+ auto handleTlsDescAuth = [this, &sym, &sec, type, offset,
addend](RelExpr tlsdescExpr) {
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ return;
if (sym.isUndefined() && !sym.isPreemptible) {
// Resolves to `addend`. Handle in
// relaxAuthTlsDescForNonPreemptibleUndefined
@@ -375,6 +377,8 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
rs.handleTlsDesc(R_TLSDESC, R_GOT, type, offset, addend, sym);
continue;
case R_AARCH64_TLSDESC_CALL:
+ // The symbol type is checked by the associated ADR/LD64/ADD relocations;
+ // the rewritten instruction does not materialize the symbol value.
if (!ctx.arg.shared)
sec.addReloc({R_TPREL, type, offset, addend, &sym});
continue;
@@ -390,6 +394,7 @@ void AArch64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
handleTlsDescAuth(R_TLSDESC);
continue;
case R_AARCH64_AUTH_TLSDESC_CALL:
+ // The symbol type is checked by the associated AUTH_TLSDESC relocations.
if (sym.isUndefined() && !sym.isPreemptible)
sec.addReloc({R_TPREL, type, offset, addend, &sym});
else
diff --git a/lld/ELF/Arch/ARM.cpp b/lld/ELF/Arch/ARM.cpp
index e1f73b991f1f3..23300dcb04c22 100644
--- a/lld/ELF/Arch/ARM.cpp
+++ b/lld/ELF/Arch/ARM.cpp
@@ -297,6 +297,8 @@ void ARM::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
rs.handleTlsGd(R_TLSGD_PC, R_NONE, R_NONE, type, offset, addend, sym);
continue;
case R_ARM_TLS_LDM32:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
ctx.needsTlsLd.store(true, std::memory_order_relaxed);
sec.addReloc({R_TLSLD_PC, type, offset, addend, &sym});
continue;
diff --git a/lld/ELF/Arch/Hexagon.cpp b/lld/ELF/Arch/Hexagon.cpp
index 985f9c365bd37..adf0c87a8cde7 100644
--- a/lld/ELF/Arch/Hexagon.cpp
+++ b/lld/ELF/Arch/Hexagon.cpp
@@ -162,7 +162,13 @@ void Hexagon::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
case R_HEX_32_6_X:
case R_HEX_HI16:
case R_HEX_LO16:
+ expr = R_ABS;
+ break;
+ // R_HEX_DTPREL_32 is classified R_ABS as relocation application is
+ // handled specially, so check the symbol type here.
case R_HEX_DTPREL_32:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
expr = R_ABS;
break;
@@ -187,6 +193,8 @@ void Hexagon::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
case R_HEX_GD_PLT_B22_PCREL:
case R_HEX_GD_PLT_B22_PCREL_X:
case R_HEX_GD_PLT_B32_PCREL_X:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
// GD PLT: call foo at GDPLT becomes call __tls_get_addr.
// Record R_PLT_PC on the TLS symbol; finalizeRelocScan (called
// single-threaded after scanning) will create __tls_get_addr and
@@ -242,6 +250,8 @@ void Hexagon::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
case R_HEX_GD_GOT_11_X:
case R_HEX_GD_GOT_16_X:
case R_HEX_GD_GOT_32_6_X:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
sym.setFlags(NEEDS_TLSGD);
ctx.in.gotPlt->hasGotPltOffRel.store(true, std::memory_order_relaxed);
sec.addReloc({R_TLSGD_GOTPLT, type, offset, addend, &sym});
diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp
index 224c31d5a2f1d..3c87dda3af00a 100644
--- a/lld/ELF/Arch/LoongArch.cpp
+++ b/lld/ELF/Arch/LoongArch.cpp
@@ -642,24 +642,34 @@ void LoongArch::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
// TLS GD/LD relocations (no GD/LD->IE/LE optimization):
case R_LARCH_TLS_LD_PC_HI20:
case R_LARCH_TLS_GD_PC_HI20:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
sym.setFlags(NEEDS_TLSGD);
sec.addReloc({RE_LOONGARCH_TLSGD_PAGE_PC, type, offset, addend, &sym});
continue;
case R_LARCH_TLS_LD_HI20:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
ctx.needsTlsLd.store(true, std::memory_order_relaxed);
sec.addReloc({R_TLSLD_GOT, type, offset, addend, &sym});
continue;
case R_LARCH_TLS_GD_HI20:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
sym.setFlags(NEEDS_TLSGD);
sec.addReloc({R_TLSGD_GOT, type, offset, addend, &sym});
continue;
case R_LARCH_TLS_LD_PCREL20_S2:
case R_LARCH_TLS_LD_PCADD_HI20:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
ctx.needsTlsLd.store(true, std::memory_order_relaxed);
sec.addReloc({R_TLSLD_PC, type, offset, addend, &sym});
continue;
case R_LARCH_TLS_GD_PCREL20_S2:
case R_LARCH_TLS_GD_PCADD_HI20:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
sym.setFlags(NEEDS_TLSGD);
sec.addReloc({R_TLSGD_PC, type, offset, addend, &sym});
continue;
@@ -679,6 +689,8 @@ void LoongArch::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
addend, sym);
continue;
case R_LARCH_TLS_DESC_CALL:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
if (!ctx.arg.shared)
sec.addReloc(
{sym.isPreemptible ? R_GOT : R_TPREL, type, offset, addend, &sym});
@@ -686,6 +698,8 @@ void LoongArch::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
// TLSDESC relocations (extreme code model, no optimization):
case R_LARCH_TLS_DESC64_PC_LO20:
case R_LARCH_TLS_DESC64_PC_HI12:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
sym.setFlags(NEEDS_TLSDESC);
sec.addReloc({RE_LOONGARCH_TLSDESC_PAGE_PC, type, offset, addend, &sym});
continue;
@@ -695,6 +709,8 @@ void LoongArch::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
case R_LARCH_TLS_DESC64_LO20:
case R_LARCH_TLS_DESC64_HI12:
case R_LARCH_TLS_DESC_PCADD_HI20:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
sym.setFlags(NEEDS_TLSDESC);
sec.addReloc({R_TLSDESC, type, offset, addend, &sym});
continue;
diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp
index ba751660df1fa..272ce5256da1c 100644
--- a/lld/ELF/Arch/Mips.cpp
+++ b/lld/ELF/Arch/Mips.cpp
@@ -729,14 +729,23 @@ void MIPS<ELFT>::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
}
if (expr == RE_MIPS_TLSLD) {
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
ctx.in.mipsGot->addTlsIndex(*sec.file);
sec.addReloc({expr, type, offset, addend, &sym});
} else if (expr == RE_MIPS_TLSGD) {
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
ctx.in.mipsGot->addDynTlsEntry(*sec.file, sym);
sec.addReloc({expr, type, offset, addend, &sym});
} else {
if (expr == R_TPREL && rs.checkTlsLe(offset, sym, type))
continue;
+ // GOTTPREL relocations are classified RE_MIPS_GOT_OFF, which is shared
+ // with non-TLS GOT relocations, so check the symbol type here.
+ if ((type == R_MIPS_TLS_GOTTPREL || type == R_MICROMIPS_TLS_GOTTPREL) &&
+ checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
rs.process(expr, type, offset, sym, addend);
}
}
diff --git a/lld/ELF/Arch/PPC.cpp b/lld/ELF/Arch/PPC.cpp
index e09ecb5c2cff6..1d6ba20083020 100644
--- a/lld/ELF/Arch/PPC.cpp
+++ b/lld/ELF/Arch/PPC.cpp
@@ -367,6 +367,8 @@ void PPC::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
rs.handleTlsIe(R_GOT_OFF, type, offset, addend, sym);
continue;
case R_PPC_TLS:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
if (!ctx.arg.shared && !sym.isPreemptible)
sec.addReloc({R_TPREL, type, offset, addend, &sym});
continue;
@@ -378,6 +380,8 @@ void PPC::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
continue;
case R_PPC_TLSGD:
case R_PPC_TLSLD:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
if (!ctx.arg.shared) {
sec.addReloc({sym.isPreemptible ? R_GOT_OFF : R_TPREL, type, offset,
addend, &sym});
@@ -394,6 +398,8 @@ void PPC::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
case R_PPC_DTPREL16_HI:
case R_PPC_DTPREL16_LO:
case R_PPC_DTPREL32:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
sec.addReloc({R_DTPREL, type, offset, addend, &sym});
continue;
diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp
index 17ee2f723e7b2..cb336d742abcf 100644
--- a/lld/ELF/Arch/PPC64.cpp
+++ b/lld/ELF/Arch/PPC64.cpp
@@ -1250,6 +1250,8 @@ void PPC64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
rs.handleTlsIe(R_GOT_PC, type, offset, addend, sym);
continue;
case R_PPC64_TLS:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
if (!ctx.arg.shared && !sym.isPreemptible)
sec.addReloc({R_TPREL, type, offset, addend, &sym});
continue;
@@ -1260,6 +1262,8 @@ void PPC64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
case R_PPC64_GOT_TLSGD16_HI:
case R_PPC64_GOT_TLSGD16_LO:
case R_PPC64_GOT_TLSGD_PCREL34: {
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
bool isPCRel = type == R_PPC64_GOT_TLSGD_PCREL34;
if (optimizeTlsGdLd) {
if (sym.isPreemptible) {
@@ -1283,6 +1287,8 @@ void PPC64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
// __tls_get_addr being defined.
case R_PPC64_TLSGD:
case R_PPC64_TLSLD: {
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
auto it1 = it;
++it1;
if (it1 == rels.end()) {
@@ -1310,6 +1316,8 @@ void PPC64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
case R_PPC64_GOT_TLSLD16_HI:
case R_PPC64_GOT_TLSLD16_LO:
case R_PPC64_GOT_TLSLD_PCREL34:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
if (optimizeTlsGdLd) {
sec.addReloc({R_TPREL, type, offset, addend, &sym});
} else {
@@ -1331,12 +1339,16 @@ void PPC64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
case R_PPC64_DTPREL16_LO_DS:
case R_PPC64_DTPREL64:
case R_PPC64_DTPREL34:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
sec.addReloc({R_DTPREL, type, offset, addend, &sym});
continue;
case R_PPC64_GOT_DTPREL16_HA:
case R_PPC64_GOT_DTPREL16_LO_DS:
case R_PPC64_GOT_DTPREL16_DS:
case R_PPC64_GOT_DTPREL16_HI:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
sym.setFlags(NEEDS_GOT_DTPREL);
sec.addReloc({R_TLSLD_GOT_OFF, type, offset, addend, &sym});
continue;
diff --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index 7797990571def..3877ccebc7511 100644
--- a/lld/ELF/Arch/SPARCV9.cpp
+++ b/lld/ELF/Arch/SPARCV9.cpp
@@ -199,6 +199,8 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
continue;
case R_SPARC_TLS_IE_LD:
case R_SPARC_TLS_IE_LDX:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
if (!ctx.arg.shared && !sym.isPreemptible)
sec.addReloc({R_TPREL, type, offset, addend, &sym});
continue;
diff --git a/lld/ELF/Arch/SystemZ.cpp b/lld/ELF/Arch/SystemZ.cpp
index b76baf464a5e7..11dbb2352ead6 100644
--- a/lld/ELF/Arch/SystemZ.cpp
+++ b/lld/ELF/Arch/SystemZ.cpp
@@ -314,10 +314,14 @@ void SystemZ::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
case R_390_TLS_GOTIE20:
case R_390_TLS_GOTIE32:
case R_390_TLS_GOTIE64:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
sym.setFlags(NEEDS_TLSIE);
sec.addReloc({R_GOT_OFF, type, offset, addend, &sym});
continue;
case R_390_TLS_IEENT:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
sym.setFlags(NEEDS_TLSIE);
sec.addReloc({R_GOT_PC, type, offset, addend, &sym});
continue;
@@ -345,6 +349,8 @@ void SystemZ::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
// TLS DTPREL relocations:
case R_390_TLS_LDO32:
case R_390_TLS_LDO64:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
if (ctx.arg.shared)
sec.addReloc({R_DTPREL, type, offset, addend, &sym});
else
diff --git a/lld/ELF/Arch/X86.cpp b/lld/ELF/Arch/X86.cpp
index 5b95ccdf50e46..652fc5e527f0c 100644
--- a/lld/ELF/Arch/X86.cpp
+++ b/lld/ELF/Arch/X86.cpp
@@ -269,6 +269,8 @@ void X86::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
++it;
continue;
case R_386_TLS_LDO_32:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
sec.addReloc(
{ctx.arg.shared ? R_DTPREL : R_TPREL, type, offset, addend, &sym});
continue;
@@ -277,6 +279,7 @@ void X86::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
rs.handleTlsDesc(R_TLSDESC_GOTPLT, R_GOTPLT, type, offset, addend, sym);
continue;
case R_386_TLS_DESC_CALL:
+ // The symbol type is checked by the associated GOTDESC relocation.
// For executables, TLSDESC is optimized to IE or LE. Use R_TPREL as the
// rewrites for this relocation are identical.
if (!ctx.arg.shared)
diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp
index c1d1db1d0133c..df4c1141213c6 100644
--- a/lld/ELF/Arch/X86_64.cpp
+++ b/lld/ELF/Arch/X86_64.cpp
@@ -737,10 +737,14 @@ void X86_64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
continue;
case R_X86_64_DTPOFF32:
case R_X86_64_DTPOFF64:
+ if (checkTlsSym(ctx, sec, offset, sym, type))
+ continue;
sec.addReloc(
{ctx.arg.shared ? R_DTPREL : R_TPREL, type, offset, addend, &sym});
continue;
case R_X86_64_TLSDESC_CALL:
+ // The symbol type is checked by the associated GOTPC32_TLSDESC
+ // relocation.
// For executables, TLSDESC is optimized to IE or LE. Use R_TPREL as the
// rewrites for this relocation are identical.
if (!ctx.arg.shared)
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index 13baaa5b8c2ad..c6d3fc878719e 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -10,6 +10,7 @@
#include "Config.h"
#include "InputFiles.h"
#include "OutputSections.h"
+#include "RelocScan.h"
#include "Relocations.h"
#include "SymbolTable.h"
#include "Symbols.h"
@@ -1080,6 +1081,9 @@ void InputSection::relocateNonAlloc(Ctx &ctx, uint8_t *buf,
RelExpr expr = target.getRelExpr(type, sym, bufLoc);
if (expr == R_NONE)
continue;
+ if (LLVM_UNLIKELY(isTlsExpr(expr) &&
+ checkTlsSym(ctx, *this, offset, sym, type)))
+ continue;
auto *ds = dyn_cast<Defined>(&sym);
if (emachine == EM_RISCV && type == R_RISCV_SET_ULEB128) {
diff --git a/lld/ELF/RelocScan.h b/lld/ELF/RelocScan.h
index ca55577e9ee71..70f209bdffd14 100644
--- a/lld/ELF/RelocScan.h
+++ b/lld/ELF/RelocScan.h
@@ -45,6 +45,19 @@ template <RelExpr... Exprs> bool oneof(RelExpr expr) {
return (uint64_t(1) << expr) & buildMask(Exprs...);
}
+// TLS relocations can only reference symbols with type STT_TLS (gABI).
+// A target classifying a TLS relocation with a non-TLS expr must call
+// checkTlsSym explicitly.
+inline bool isTlsExpr(RelExpr expr) {
+ // RE_LOONGARCH_GOT* are also used for non-TLS GOT relocations and
+ // therefore excluded.
+ return oneof<R_TPREL, R_TPREL_NEG, R_DTPREL, R_TLSDESC, R_TLSDESC_PC,
+ R_TLSDESC_GOTPLT, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC,
+ R_TLSLD_GOT, R_TLSLD_GOTPLT, R_TLSLD_GOT_OFF, R_TLSLD_PC,
+ RE_AARCH64_TLSDESC_PAGE, RE_MIPS_TLSGD, RE_MIPS_TLSLD,
+ RE_LOONGARCH_TLSGD_PAGE_PC, RE_LOONGARCH_TLSDESC_PAGE_PC>(expr);
+}
+
// This class encapsulates states needed to scan relocations for one
// InputSectionBase.
class RelocScan {
@@ -107,6 +120,8 @@ class RelocScan {
template <bool enableIeToLe = true>
void handleTlsIe(RelExpr ieExpr, RelType type, uint64_t offset,
int64_t addend, Symbol &sym) {
+ if (checkTlsSym(ctx, *sec, offset, sym, type))
+ return;
if (enableIeToLe && !ctx.arg.shared && !sym.isPreemptible) {
// Optimize to Local Exec.
sec->addReloc({R_TPREL, type, offset, addend, &sym});
@@ -128,6 +143,8 @@ class RelocScan {
// call should be skipped (i.e., caller should ++it).
bool handleTlsLd(RelExpr sharedExpr, RelType type, uint64_t offset,
int64_t addend, Symbol &sym) {
+ if (checkTlsSym(ctx, *sec, offset, sym, type))
+ return true;
if (ctx.arg.shared) {
ctx.needsTlsLd.store(true, std::memory_order_relaxed);
sec->addReloc({sharedExpr, type, offset, addend, &sym});
@@ -143,6 +160,8 @@ class RelocScan {
// ieExpr/leExpr to disable GD-to-IE/LE optimization (e.g. ARM, RISC-V).
bool handleTlsGd(RelExpr sharedExpr, RelExpr ieExpr, RelExpr leExpr,
RelType type, uint64_t offset, int64_t addend, Symbol &sym) {
+ if (checkTlsSym(ctx, *sec, offset, sym, type))
+ return true;
if (!ctx.arg.shared && ieExpr != R_NONE) {
if (sym.isPreemptible) {
// Optimize to Initial Exec.
@@ -162,6 +181,8 @@ class RelocScan {
// Handle TLSDESC relocation.
void handleTlsDesc(RelExpr sharedExpr, RelExpr ieExpr, RelType type,
uint64_t offset, int64_t addend, Symbol &sym) {
+ if (checkTlsSym(ctx, *sec, offset, sym, type))
+ return;
if (ctx.arg.shared) {
sym.setFlags(NEEDS_TLSDESC);
sec->addReloc({sharedExpr, type, offset, addend, &sym});
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index db5ba7d023b7f..814f608e464f5 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -693,7 +693,29 @@ bool RelocScan::maybeReportUndefined(Undefined &sym, uint64_t offset) {
return elf::maybeReportUndefined(ctx, sym, *sec, offset);
}
+bool elf::checkTlsSym(Ctx &ctx, InputSectionBase &sec, uint64_t offset,
+ Symbol &sym, RelType type) {
+ // TLS relocations can only reference symbols with type STT_TLS (gABI).
+ // Symbols whose type cannot be validated against the gABI invariant are
+ // exempt:
+ // * script-defined symbols have no ELF type,
+ // * section symbols and undefined symbols are accepted by GNU ld, and some
+ // assemblers (LoongArch, MIPS) emit them for TLS relocations, e.g.
+ // `.dtprelword .tdata+1` in debug info.
+ // Defined non-TLS symbols are rejected; this is stricter than GNU ld, which
+ // only warns.
+ if (sym.isTls() || sym.scriptDefined || sym.isSection() || sym.isUndefined())
+ return false;
+ auto diag = Err(ctx);
+ diag << "relocation " << type << " against " << &sym
+ << " cannot be used with a non-STT_TLS symbol";
+ printLocation(diag, sec, sym, offset);
+ return true;
+}
+
bool RelocScan::checkTlsLe(uint64_t offset, Symbol &sym, RelType type) {
+ if (checkTlsSym(ctx, *sec, offset, sym, type))
+ return true;
if (!ctx.arg.shared)
return false;
auto diag = Err(ctx);
@@ -933,6 +955,11 @@ bool RelocScan::isStaticLinkTimeConstant(RelExpr e, RelType type,
// space for the extra PT_LOAD even if we end up not using it.
void RelocScan::process(RelExpr expr, RelType type, uint64_t offset,
Symbol &sym, int64_t addend) const {
+ // Some TLS relocations are routed here directly, e.g. R_DTPREL.
+ if (LLVM_UNLIKELY(isTlsExpr(expr) &&
+ checkTlsSym(ctx, *sec, offset, sym, type)))
+ return;
+
// If non-ifunc non-preemptible, change PLT to direct call and optimize GOT
// indirection.
const bool isIfunc = sym.isGnuIFunc();
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Reloca...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/228418
More information about the llvm-commits
mailing list