[lld] 8f4f515 - [lld][Hexagon] Fix TLS GD PLT to only create PLT entry for __tls_get_addr (#180297)

via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 30 07:35:22 PDT 2026


Author: Brian Cain
Date: 2026-03-30T09:35:17-05:00
New Revision: 8f4f515898e2664ef8b4c1988b444cfd041f3fa5

URL: https://github.com/llvm/llvm-project/commit/8f4f515898e2664ef8b4c1988b444cfd041f3fa5
DIFF: https://github.com/llvm/llvm-project/commit/8f4f515898e2664ef8b4c1988b444cfd041f3fa5.diff

LOG: [lld][Hexagon] Fix TLS GD PLT to only create PLT entry for __tls_get_addr (#180297)

Previously, R_HEX_GD_PLT_* relocations would create PLT entries for TLS
symbols like 'foo' in addition to __tls_get_addr.

This fix skips NEEDS_PLT on TLS symbols with R_HEX_GD_PLT_*, creates
__tls_get_addr symbol earlier with NEEDS_PLT, changes
hexagonTLSSymbolUpdate to only rebind relocations.

Also a test for the edge case where a GD_PLT relocation directly
references __tls_get_addr which previously caused a crash due to
duplicate PLT entry creation.

---------

Co-authored-by: Fangrui Song <i at maskray.me>

Added: 
    lld/test/ELF/hexagon-tls-gd-plt-direct.s

Modified: 
    lld/ELF/Arch/Hexagon.cpp
    lld/ELF/Relocations.cpp
    lld/ELF/Relocations.h
    lld/ELF/Target.h
    lld/ELF/Writer.cpp
    lld/test/ELF/hexagon-thunk-range-gdplt.s
    lld/test/ELF/hexagon-tls-gd-nonpreemptible.s
    lld/test/ELF/hexagon-tls-gd-xform.s

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Arch/Hexagon.cpp b/lld/ELF/Arch/Hexagon.cpp
index 435ee8a4d4f8e..45b5188845b10 100644
--- a/lld/ELF/Arch/Hexagon.cpp
+++ b/lld/ELF/Arch/Hexagon.cpp
@@ -9,6 +9,7 @@
 #include "InputFiles.h"
 #include "OutputSections.h"
 #include "RelocScan.h"
+#include "SymbolTable.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
@@ -43,6 +44,7 @@ class Hexagon final : public TargetInfo {
   void scanSection(InputSectionBase &sec) override {
     elf::scanSection1<Hexagon, ELF32LE>(*this, sec);
   }
+  void finalizeRelocScan() override;
   bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
                   uint64_t branchAddr, const Symbol &s,
                   int64_t a) const override;
@@ -182,7 +184,11 @@ 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:
-      sym.setFlags(NEEDS_PLT);
+      // 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
+      // rebind these relocations.  We cannot access the symbol table here
+      // because scanSectionImpl runs in parallel.
       sec.addReloc({R_PLT_PC, type, offset, addend, &sym});
       continue;
 
@@ -664,4 +670,43 @@ void elf::mergeHexagonAttributesSections(Ctx &ctx) {
                            mergeAttributesSection(ctx, sections));
 }
 
+static bool isGDPLT(RelType type) {
+  switch (type) {
+  case R_HEX_GD_PLT_B22_PCREL:
+  case R_HEX_GD_PLT_B22_PCREL_X:
+  case R_HEX_GD_PLT_B32_PCREL_X:
+    return true;
+  default:
+    return false;
+  }
+}
+
+void Hexagon::finalizeRelocScan() {
+  Symbol *tga = nullptr;
+
+  // Scan for R_HEX_GD_PLT_* relocations (recorded as R_PLT_PC by
+  // scanSectionImpl) and rebind them to __tls_get_addr.
+  for (ELFFileBase *f : ctx.objectFiles) {
+    for (InputSectionBase *s : f->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 || !isGDPLT(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->used = true;
+          tga->isPreemptible = true;
+          tga->setFlags(NEEDS_PLT);
+        }
+        rel.sym = tga;
+      }
+    }
+  }
+}
+
 void elf::setHexagonTargetInfo(Ctx &ctx) { ctx.target.reset(new Hexagon(ctx)); }

diff  --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index ee6b14f2798ed..0000f1b4d9059 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -1406,6 +1406,8 @@ void elf::postScanRelocations(Ctx &ctx) {
     }
   };
 
+  ctx.target->finalizeRelocScan();
+
   GotSection *got = ctx.in.got.get();
   if (ctx.needsTlsLd.load(std::memory_order_relaxed) && got->addTlsIndex()) {
     if (ctx.arg.shared)
@@ -1991,46 +1993,6 @@ 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) {
-  bool needTlsSymbol = false;
-  forEachInputSectionDescription(
-      outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
-        for (InputSection *isec : isd->sections)
-          for (Relocation &rel : isec->relocs())
-            if (rel.sym->type == llvm::ELF::STT_TLS && rel.expr == R_PLT_PC) {
-              needTlsSymbol = true;
-              return;
-            }
-      });
-  return needTlsSymbol;
-}
-
-void elf::hexagonTLSSymbolUpdate(Ctx &ctx) {
-  Symbol *sym = ctx.symtab->find("__tls_get_addr");
-  if (!sym)
-    return;
-  bool needEntry = true;
-  forEachInputSectionDescription(
-      ctx.outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
-        for (InputSection *isec : isd->sections)
-          for (Relocation &rel : isec->relocs())
-            if (rel.sym->type == llvm::ELF::STT_TLS && rel.expr == R_PLT_PC) {
-              if (needEntry) {
-                if (sym->auxIdx == 0)
-                  sym->allocateAux(ctx);
-                addPltEntry(ctx, *ctx.in.plt, *ctx.in.gotPlt, *ctx.in.relaPlt,
-                            ctx.target->pltRel, *sym);
-                needEntry = false;
-              }
-              rel.sym = sym;
-            }
-      });
-}
-
 static bool matchesRefTo(const NoCrossRefCommand &cmd, StringRef osec) {
   if (cmd.toFirst)
     return cmd.outputSections[0] == osec;

diff  --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index 8de6b7dc2a6f7..abc62bf01a421 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -160,9 +160,6 @@ bool maybeReportUndefined(Ctx &, Undefined &sym, InputSectionBase &sec,
 void postScanRelocations(Ctx &ctx);
 void addGotEntry(Ctx &ctx, Symbol &sym);
 
-void hexagonTLSSymbolUpdate(Ctx &ctx);
-bool hexagonNeedsTLSSymbol(ArrayRef<OutputSection *> outputSections);
-
 bool isAbsolute(const Symbol &sym);
 
 class ThunkSection;

diff  --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 8fa8cd13f1b67..1904892fdd2cf 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -99,6 +99,12 @@ class TargetInfo {
   template <class ELFT, class RelTy>
   void scanSectionImpl(InputSectionBase &, Relocs<RelTy>);
 
+  // Called after parallel relocation scanning is complete but before
+  // postScanRelocations processes symbol flags. Targets may override this to
+  // perform single-threaded fixups that cannot run during parallel scanning
+  // (e.g. symbol table modifications).
+  virtual void finalizeRelocScan() {}
+
   virtual void relocate(uint8_t *loc, const Relocation &rel,
                         uint64_t val) const = 0;
   void relocateNoSym(uint8_t *loc, RelType type, uint64_t val) const {

diff  --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index b8ec53e78f6e4..6b0382090f78f 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1538,10 +1538,6 @@ 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);
-
   if (ctx.arg.randomizeSectionPadding)
     randomizeSectionPadding(ctx);
 
@@ -2042,17 +2038,6 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
       sec->addrExpr = [=] { return i->second; };
   }
 
-  // With the ctx.outputSections available check for GDPLT relocations
-  // and add __tls_get_addr symbol if needed.
-  if (ctx.arg.emachine == EM_HEXAGON &&
-      hexagonNeedsTLSSymbol(ctx.outputSections)) {
-    Symbol *sym =
-        ctx.symtab->addSymbol(Undefined{ctx.internalFile, "__tls_get_addr",
-                                        STB_GLOBAL, STV_DEFAULT, STT_NOTYPE});
-    sym->isPreemptible = true;
-    ctx.partitions[0].dynSymTab->addSymbol(sym);
-  }
-
   // This is a bit of a hack. A value of 0 means undef, so we set it
   // to 1 to make __ehdr_start defined. The section number is not
   // particularly relevant.

diff  --git a/lld/test/ELF/hexagon-thunk-range-gdplt.s b/lld/test/ELF/hexagon-thunk-range-gdplt.s
index 77fd0e5754568..f646e81026ce9 100644
--- a/lld/test/ELF/hexagon-thunk-range-gdplt.s
+++ b/lld/test/ELF/hexagon-thunk-range-gdplt.s
@@ -48,27 +48,29 @@ tls_var_distant:
 
 # CHECK: Disassembly of section .text:
 # CHECK:     <_start>:
-# CHECK-NEXT:   102d4:  { immext(#0x420100)
-# CHECK-NEXT:      r2 = add(pc,##0x420130) }
+# CHECK-NEXT:   102b0:  { immext(#0x420100)
+# CHECK-NEXT:      r2 = add(pc,##0x420104) }
 # CHECK-NEXT:    { immext(#0xfffeffc0)
 # CHECK-NEXT:      r0 = add(r2,##-0x10018) }
-# CHECK-NEXT:    { call 0x410360 <__tls_get_addr at plt> }
+# CHECK-NEXT:    { call 0x410310 <__tls_get_addr at plt> }
 # CHECK-NEXT:    { immext(#0xfffeffc0)
 # CHECK-NEXT:      r0 = add(r2,##-0x10010) }
-# CHECK-NEXT:    { call 0x410360 <__tls_get_addr at plt> }
+# CHECK-NEXT:    { call 0x410310 <__tls_get_addr at plt> }
 # CHECK-NEXT:    { jumpr r31 }
 
 # CHECK:     <more_code>:
-# CHECK-NEXT:   4102f8:  { immext(#0xfffeffc0)
+# CHECK-NEXT:   4102d4:  { immext(#0xfffeffc0)
 # CHECK-NEXT:      r0 = add(r2,##-0x10008) }
-# CHECK-NEXT:    { call 0x410360 <__tls_get_addr at plt> }
+# CHECK-NEXT:    { call 0x410310 <__tls_get_addr at plt> }
 # CHECK-NEXT:    { jumpr r31 }
 
-## Verify PLT entries are created for TLS
+## Verify PLT entry is created for __tls_get_addr
+## TLS symbols (tls_var_close, tls_var_far, tls_var_distant) should NOT have
+## PLT entries - only __tls_get_addr needs one.
 # CHECK: Disassembly of section .plt:
 # CHECK:      <.plt>:
-# CHECK-NEXT:   410310:  { immext(#0x200c0)
-# CHECK-NEXT:      r28 = add(pc,##0x200f4) }
+# CHECK-NEXT:   4102f0:  { immext(#0x200c0)
+# CHECK-NEXT:      r28 = add(pc,##0x200c4) }
 # CHECK-NEXT:    { r14 -= add(r28,#0x10)
 # CHECK-NEXT:      r15 = memw(r28+#0x8)
 # CHECK-NEXT:      r28 = memw(r28+#0x4) }
@@ -76,20 +78,8 @@ tls_var_distant:
 # CHECK-NEXT:      jumpr r28 }
 # CHECK-NEXT:    { trap0(#0xdb) }
 
-# CHECK:      <tls_var_far at plt>:
-# CHECK-NEXT:   410340:  { immext(#0x200c0)
-# CHECK-NEXT:      r14 = add(pc,##0x200d8) }
-# CHECK-NEXT:    { r28 = memw(r14+#0x0) }
-# CHECK-NEXT:    { jumpr r28 }
-
-# CHECK:      <tls_var_distant at plt>:
-# CHECK-NEXT:   410350:  { immext(#0x200c0)
-# CHECK-NEXT:      r14 = add(pc,##0x200cc) }
-# CHECK-NEXT:    { r28 = memw(r14+#0x0) }
-# CHECK-NEXT:    { jumpr r28 }
-
 # CHECK:      <__tls_get_addr at plt>:
-# CHECK-NEXT:   410360: { immext(#0x200c0)
-# CHECK-NEXT:      r14 = add(pc,##0x200c0) }
+# CHECK-NEXT:   410310: { immext(#0x20080)
+# CHECK-NEXT:      r14 = add(pc,##0x200b4) }
 # CHECK-NEXT:    { r28 = memw(r14+#0x0) }
 # CHECK-NEXT:    { jumpr r28 }

diff  --git a/lld/test/ELF/hexagon-tls-gd-nonpreemptible.s b/lld/test/ELF/hexagon-tls-gd-nonpreemptible.s
index ff5e6dbaac710..ca84865e1dba1 100644
--- a/lld/test/ELF/hexagon-tls-gd-nonpreemptible.s
+++ b/lld/test/ELF/hexagon-tls-gd-nonpreemptible.s
@@ -14,7 +14,6 @@
 .type _start, @function
 
 # RELOC:      Section ({{.*}}) .rela.plt {
-# RELOC-NEXT:   R_HEX_JMP_SLOT - 0x0
 # RELOC-NEXT:   R_HEX_JMP_SLOT __tls_get_addr 0x0
 # RELOC-NEXT: }
 

diff  --git a/lld/test/ELF/hexagon-tls-gd-plt-direct.s b/lld/test/ELF/hexagon-tls-gd-plt-direct.s
new file mode 100644
index 0000000000000..f52cb28130de0
--- /dev/null
+++ b/lld/test/ELF/hexagon-tls-gd-plt-direct.s
@@ -0,0 +1,31 @@
+# REQUIRES: hexagon
+# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf %s -o %t.o
+# RUN: ld.lld -shared %t.o --gc-sections -o %t.so
+# RUN: llvm-readobj -r %t.so | FileCheck %s
+
+## This test verifies that a GD_PLT relocation on a TLS variable does not
+## create a spurious R_HEX_JMP_SLOT for that variable — only
+## __tls_get_addr should get a PLT entry.
+
+# CHECK:      Section ({{.*}}) .rela.dyn {
+# CHECK-NEXT:   R_HEX_DTPMOD_32 foo 0x0
+# CHECK-NEXT:   R_HEX_DTPREL_32 foo 0x0
+# CHECK-NEXT: }
+# CHECK:      Section ({{.*}}) .rela.plt {
+# CHECK-NEXT:   R_HEX_JMP_SLOT __tls_get_addr 0x0
+# CHECK-NEXT: }
+
+.globl _start
+.type _start, @function
+_start:
+  ## Use GD_GOT to set up TLS GOT entry for foo
+  r2 = add(pc, ##_GLOBAL_OFFSET_TABLE_ at PCREL)
+  r0 = add(r2, ##foo at GDGOT)
+  call foo at GDPLT
+  jumpr r31
+
+.section .tdata,"awT", at progbits
+.globl foo
+.type foo, @object
+foo:
+  .word 0x11111111

diff  --git a/lld/test/ELF/hexagon-tls-gd-xform.s b/lld/test/ELF/hexagon-tls-gd-xform.s
index ade54e8a16fad..a87ec4bd61cdd 100644
--- a/lld/test/ELF/hexagon-tls-gd-xform.s
+++ b/lld/test/ELF/hexagon-tls-gd-xform.s
@@ -18,25 +18,24 @@
 _start:
 .ifdef GDPLT
                         call x at gdplt
-# CHECK_GDPLT:  101ec: { call 0x10220 <__tls_get_addr at plt> }
+# CHECK_GDPLT:  101e0: { call 0x10210 <__tls_get_addr at plt> }
 .else
                   call x
 # CHECK:  101b8: { call 0x101e0 <x at plt> }
 .endif
 
-# CHECK_GDPLT:        10220: { immext(#0x20040)
-# CHECK_GDPLT-NEXT:   10224:   r14 = add(pc,##0x2007c) }
-# CHECK_GDPLT-NEXT:   10228: { r28 = memw(r14+#0x0) }
-# CHECK_GDPLT-NEXT:   1022c: { jumpr r28 }
+# CHECK_GDPLT:        10210: { immext(#0x20040)
+# CHECK_GDPLT-NEXT:   10214:   r14 = add(pc,##0x20078) }
+# CHECK_GDPLT-NEXT:   10218: { r28 = memw(r14+#0x0) }
+# CHECK_GDPLT-NEXT:   1021c: { jumpr r28 }
 
 
-## Looking at the above check, 0x10220+0x2007c must equal the entry for
-##  __tls_get_addr, 0x3029C
+## Looking at the above check, 0x10210+0x20078 must equal the entry for
+##  __tls_get_addr, 0x30288
 
 # RELA_GDPLT: Relocations [
 # RELA_GDPLT-NEXT:  Section (5) .rela.plt {
-# RELA_GDPLT-NEXT:    0x30298 R_HEX_JMP_SLOT x 0x0
-# RELA_GDPLT-NEXT:    0x3029C R_HEX_JMP_SLOT __tls_get_addr 0x0
+# RELA_GDPLT-NEXT:    0x30288 R_HEX_JMP_SLOT __tls_get_addr 0x0
 # RELA_GDPLT-NEXT:  }
 # RELA_GDPLT-NEXT:]
 


        


More information about the llvm-commits mailing list