[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