Skip to content

[AArch64] Support TLS variables in debug info - #146572

Merged
xgupta merged 21 commits into
llvm:mainfrom
xgupta:pr83466
Mar 31, 2026
Merged

xgupta merged 21 commits into
llvm:mainfrom
xgupta:pr83466

Conversation

@xgupta

@xgupta xgupta commented Jul 1, 2025

Copy link
Copy Markdown
Contributor

This adds an implementation of getDebugThreadLocalSymbol for AArch64 by using AArch::S_DTPREL.

Fixes #83466

@llvmbot

llvmbot commented Jul 1, 2025 •

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-mc

@llvm/pr-subscribers-lld-elf

Author: Shivam Gupta (xgupta)

Changes

This adds an implementation of getDebugThreadLocalSymbol for AArch64 by using AArch::S_DTPREL.

Fixes #83466


Full diff: https://github.com/llvm/llvm-project/pull/146572.diff

10 Files Affected:

  • (modified) lld/ELF/Arch/AArch64.cpp (+6)
  • (modified) lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp (+1)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp (+6-1)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.h (+3)
  • (modified) llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp (+3)
  • (modified) llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp (+5)
  • (modified) llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp (+2)
  • (added) llvm/test/CodeGen/AArch64/tls-dtprel64.ll (+37)
  • (modified) llvm/test/DebugInfo/AArch64/tls-at-location.ll (+9-3)
  • (added) llvm/test/MC/AArch64/tls-dtprel64.s (+38)
diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 1812f2af419d2..7576d904e3a60 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -156,6 +156,8 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
     return R_ABS;
   case R_AARCH64_AUTH_ABS64:
     return RE_AARCH64_AUTH;
+  case R_AARCH64_TLS_DTPREL64:
+    return R_DTPREL;
   case R_AARCH64_TLSDESC_ADR_PAGE21:
     return RE_AARCH64_TLSDESC_PAGE;
   case R_AARCH64_AUTH_TLSDESC_ADR_PAGE21:
@@ -542,6 +544,10 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel,
     if (isInt<32>(val))
       write32(ctx, loc, val);
     break;
+  case R_AARCH64_TLS_DTPREL64:
+    checkInt(ctx, loc, val, 64, rel);
+    write64(ctx, loc, val);
+    break;
   case R_AARCH64_ADD_ABS_LO12_NC:
   case R_AARCH64_AUTH_GOT_ADD_LO12_NC:
     write32Imm12(loc, val);
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index f69358de6a288..4593b7253a574 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -2843,6 +2843,7 @@ unsigned ObjectFileELF::ApplyRelocations(
       case llvm::ELF::EM_AARCH64:
         switch (reloc_type(rel)) {
         case R_AARCH64_ABS64:
+        case R_AARCH64_TLS_DTPREL64:
           ApplyELF64ABS64Relocation(symtab, rel, debug_data, rel_section);
           break;
         case R_AARCH64_ABS32:
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
index c218831ce0400..f76103733969a 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
@@ -30,7 +30,7 @@ void AArch64_ELFTargetObjectFile::Initialize(MCContext &Ctx,
 
   // AARCH64 ELF ABI does not define static relocation type for TLS offset
   // within a module.  Do not generate AT_location for TLS variables.
-  SupportDebugThreadLocalLocation = false;
+  SupportDebugThreadLocalLocation = true;
 
   // Make sure the implicitly created empty .text section has the
   // SHF_AARCH64_PURECODE flag set if the "+execute-only" target feature is
@@ -186,3 +186,8 @@ MCSection *AArch64_ELFTargetObjectFile::SelectSectionForGlobal(
 
   return TargetLoweringObjectFileELF::SelectSectionForGlobal(GO, Kind, TM);
 }
+
+const MCExpr *AArch64_ELFTargetObjectFile::getDebugThreadLocalSymbol(
+    const MCSymbol *Sym) const {
+  return MCSymbolRefExpr::create(Sym, AArch64::S_DTPREL, getContext());
+}
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
index 6b3381452c70b..78c0c22da8d1b 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
@@ -40,6 +40,9 @@ class AArch64_ELFTargetObjectFile : public TargetLoweringObjectFileELF {
 
   MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind,
                                     const TargetMachine &TM) const override;
+
+  /// Describe a TLS variable address within debug info.
+  const MCExpr *getDebugThreadLocalSymbol(const MCSymbol *Sym) const override;
 };
 
 /// AArch64_MachoTargetObjectFile - This TLOF implementation is used for Darwin.
diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
index f16fc6cfefa42..35dc8e6e9902d 100644
--- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
+++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
@@ -4421,6 +4421,7 @@ bool AArch64AsmParser::parseSymbolicImmVal(const MCExpr *&ImmVal) {
                   .Case("prel_g1_nc", AArch64::S_PREL_G1_NC)
                   .Case("prel_g0", AArch64::S_PREL_G0)
                   .Case("prel_g0_nc", AArch64::S_PREL_G0_NC)
+                  .Case("dtprel", AArch64::S_DTPREL)
                   .Case("dtprel_g2", AArch64::S_DTPREL_G2)
                   .Case("dtprel_g1", AArch64::S_DTPREL_G1)
                   .Case("dtprel_g1_nc", AArch64::S_DTPREL_G1_NC)
@@ -8191,6 +8192,8 @@ bool AArch64AsmParser::parseDataExpr(const MCExpr *&Res) {
       Spec = AArch64::S_GOTPCREL;
     else if (Identifier == "plt")
       Spec = AArch64::S_PLT;
+    else if (Identifier == "dtprel")
+      Spec = AArch64::S_DTPREL;
   }
   if (Spec == AArch64::S_None)
     return Error(Loc, "invalid relocation specifier");
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
index ebd5f30796195..9a9dd369f1ff3 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
@@ -234,6 +234,9 @@ unsigned AArch64ELFObjectWriter::getRelocType(const MCFixup &Fixup,
       }
       if (RefKind == AArch64::S_AUTH || RefKind == AArch64::S_AUTHADDR)
         return ELF::R_AARCH64_AUTH_ABS64;
+      if (RefKind == AArch64::S_DTPREL) {
+        return ELF::R_AARCH64_TLS_DTPREL64;
+      }
       return ELF::R_AARCH64_ABS64;
     }
     case AArch64::fixup_aarch64_add_imm12:
@@ -448,6 +451,8 @@ unsigned AArch64ELFObjectWriter::getRelocType(const MCFixup &Fixup,
         return R_CLS(MOVW_PREL_G0);
       if (RefKind == AArch64::S_PREL_G0_NC)
         return R_CLS(MOVW_PREL_G0_NC);
+      if (RefKind == AArch64::S_DTPREL)
+        return ELF::R_AARCH64_TLS_DTPREL64;
       if (RefKind == AArch64::S_DTPREL_G2)
         return ELF::R_AARCH64_TLSLD_MOVW_DTPREL_G2;
       if (RefKind == AArch64::S_DTPREL_G1)
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
index c08664944f727..01c50d13f1945 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
@@ -41,6 +41,7 @@ const MCAsmInfo::AtSpecifier ELFAtSpecifiers[] = {
     {AArch64::S_GOT, "GOT"},
     {AArch64::S_GOTPCREL, "GOTPCREL"},
     {AArch64::S_PLT, "PLT"},
+    {AArch64::S_DTPREL, "DTPREL"},
 };
 
 const MCAsmInfo::AtSpecifier MachOAtSpecifiers[] = {
@@ -76,6 +77,7 @@ StringRef AArch64::getSpecifierName(const MCSpecifierExpr &Expr) {
   case AArch64::S_PREL_G1_NC:          return ":prel_g1_nc:";
   case AArch64::S_PREL_G0:             return ":prel_g0:";
   case AArch64::S_PREL_G0_NC:          return ":prel_g0_nc:";
+  case AArch64::S_DTPREL:              return ":dtprel:";
   case AArch64::S_DTPREL_G2:           return ":dtprel_g2:";
   case AArch64::S_DTPREL_G1:           return ":dtprel_g1:";
   case AArch64::S_DTPREL_G1_NC:        return ":dtprel_g1_nc:";
diff --git a/llvm/test/CodeGen/AArch64/tls-dtprel64.ll b/llvm/test/CodeGen/AArch64/tls-dtprel64.ll
new file mode 100644
index 0000000000000..c481980354905
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/tls-dtprel64.ll
@@ -0,0 +1,37 @@
+; RUN: llc -O0 -mtriple=aarch64-linux-gnu -filetype=obj < %s \
+; RUN: | llvm-objdump -r - | FileCheck %s
+
+; CHECK: R_AARCH64_TLS_DTPREL64
+
+@var = thread_local global i32 0, align 4, !dbg !0
+
+; Function Attrs: noinline nounwind optnone
+define i32 @foo() #0 !dbg !11 {
+entry:
+  %0 = load i32, ptr @var, align 4, !dbg !14
+  ret i32 %0, !dbg !15
+}
+
+attributes #0 = { noinline nounwind optnone }
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "var", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 7.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5)
+!3 = !DIFile(filename: "tls-at-location.c", directory: "/home/lliu0/llvm/tls-at-location/DebugInfo/AArch64")
+!4 = !{}
+!5 = !{!0}
+!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!7 = !{i32 2, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 7.0.0"}
+!11 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 3, type: !12, isLocal: false, isDefinition: true, scopeLine: 3, isOptimized: false, unit: !2)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!6}
+!14 = !DILocation(line: 4, column: 10, scope: !11)
+!15 = !DILocation(line: 4, column: 3, scope: !11)
+
diff --git a/llvm/test/DebugInfo/AArch64/tls-at-location.ll b/llvm/test/DebugInfo/AArch64/tls-at-location.ll
index 20a0afb789771..d619962ccbf69 100644
--- a/llvm/test/DebugInfo/AArch64/tls-at-location.ll
+++ b/llvm/test/DebugInfo/AArch64/tls-at-location.ll
@@ -1,8 +1,14 @@
-; RUN: llc -filetype=obj -mtriple=aarch64--linux-gnu -o - %s | llvm-dwarfdump -v - | FileCheck %s
-;
+; RUN: llc -O0 -mtriple=aarch64-non-linux-gnu -filetype=obj < %s \
+; RUN:  | llvm-dwarfdump - | FileCheck %s
+
 ; CHECK: .debug_info contents:
 ; CHECK: DW_TAG_variable
-; CHECK-NOT: DW_AT_location
+; CHECK:   DW_AT_name      ("var")
+; CHECK-NEXT:   DW_AT_type      (0x00000040 "int")
+; CHECK-NEXT:   DW_AT_external  (true)
+; CHECK-NEXT:   DW_AT_decl_file ("{{.*}}tls-at-location.c")
+; CHECK-NEXT:   DW_AT_decl_line (1)
+; CHECK-NEXT:   DW_AT_location  (DW_OP_const8u 0x0, DW_OP_GNU_push_tls_address)
 
 @var = thread_local global i32 0, align 4, !dbg !0
 
diff --git a/llvm/test/MC/AArch64/tls-dtprel64.s b/llvm/test/MC/AArch64/tls-dtprel64.s
new file mode 100644
index 0000000000000..7cb47f9543f68
--- /dev/null
+++ b/llvm/test/MC/AArch64/tls-dtprel64.s
@@ -0,0 +1,38 @@
+# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux-gnu %s | llvm-readobj -r - | FileCheck %s
+  
+# CHECK: Relocations [
+# CHECK:   Section {{.*}} .rela.debug_info {
+# CHECK:     0x{{[0-9A-F]+}} R_AARCH64_TLS_DTPREL64 var {{.*}}
+# CHECK:   }
+
+.section .tdata,"awT",@progbits
+.globl var
+var:
+  .word 0
+
+.section .debug_abbrev,"",@progbits
+.byte 1                  // Abbreviation Code
+.byte 17                 // DW_TAG_compile_unit
+.byte 1                  // DW_CHILDREN_yes
+.byte 0                  // EOM(1)
+.byte 0                  // EOM(2)
+
+.byte 2                  // Abbreviation Code
+.byte 52                 // DW_TAG_variable
+.byte 0                  // DW_CHILDREN_no
+.byte 2;                 // DW_AT_location
+.byte 24                 // DW_FORM_exprloc
+.byte 0                  // EOM(1)
+.byte 0                  // EOM(2)
+
+.section        .debug_info,"",@progbits
+.Lcu_begin0:
+  .word .Lcu_end - .Lcu_body // Length of Unit
+.Lcu_body:
+  .hword 4               // DWARF version number
+  .word   .debug_abbrev  // Offset Into Abbrev. Section
+  .byte   8              // Address Size (in bytes)
+  .byte   1              // Abbrev [1] DW_TAG_compile_unit
+  .byte   2              // Abbrev [2] DW_TAG_variable
+  .xword  var@DTPREL
+.Lcu_end:

@llvmbot

llvmbot commented Jul 1, 2025

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-backend-aarch64

Author: Shivam Gupta (xgupta)

Changes

This adds an implementation of getDebugThreadLocalSymbol for AArch64 by using AArch::S_DTPREL.

Fixes #83466


Full diff: https://github.com/llvm/llvm-project/pull/146572.diff

10 Files Affected:

  • (modified) lld/ELF/Arch/AArch64.cpp (+6)
  • (modified) lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp (+1)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp (+6-1)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.h (+3)
  • (modified) llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp (+3)
  • (modified) llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp (+5)
  • (modified) llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp (+2)
  • (added) llvm/test/CodeGen/AArch64/tls-dtprel64.ll (+37)
  • (modified) llvm/test/DebugInfo/AArch64/tls-at-location.ll (+9-3)
  • (added) llvm/test/MC/AArch64/tls-dtprel64.s (+38)
diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 1812f2af419d2..7576d904e3a60 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -156,6 +156,8 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
     return R_ABS;
   case R_AARCH64_AUTH_ABS64:
     return RE_AARCH64_AUTH;
+  case R_AARCH64_TLS_DTPREL64:
+    return R_DTPREL;
   case R_AARCH64_TLSDESC_ADR_PAGE21:
     return RE_AARCH64_TLSDESC_PAGE;
   case R_AARCH64_AUTH_TLSDESC_ADR_PAGE21:
@@ -542,6 +544,10 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel,
     if (isInt<32>(val))
       write32(ctx, loc, val);
     break;
+  case R_AARCH64_TLS_DTPREL64:
+    checkInt(ctx, loc, val, 64, rel);
+    write64(ctx, loc, val);
+    break;
   case R_AARCH64_ADD_ABS_LO12_NC:
   case R_AARCH64_AUTH_GOT_ADD_LO12_NC:
     write32Imm12(loc, val);
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index f69358de6a288..4593b7253a574 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -2843,6 +2843,7 @@ unsigned ObjectFileELF::ApplyRelocations(
       case llvm::ELF::EM_AARCH64:
         switch (reloc_type(rel)) {
         case R_AARCH64_ABS64:
+        case R_AARCH64_TLS_DTPREL64:
           ApplyELF64ABS64Relocation(symtab, rel, debug_data, rel_section);
           break;
         case R_AARCH64_ABS32:
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
index c218831ce0400..f76103733969a 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
@@ -30,7 +30,7 @@ void AArch64_ELFTargetObjectFile::Initialize(MCContext &Ctx,
 
   // AARCH64 ELF ABI does not define static relocation type for TLS offset
   // within a module.  Do not generate AT_location for TLS variables.
-  SupportDebugThreadLocalLocation = false;
+  SupportDebugThreadLocalLocation = true;
 
   // Make sure the implicitly created empty .text section has the
   // SHF_AARCH64_PURECODE flag set if the "+execute-only" target feature is
@@ -186,3 +186,8 @@ MCSection *AArch64_ELFTargetObjectFile::SelectSectionForGlobal(
 
   return TargetLoweringObjectFileELF::SelectSectionForGlobal(GO, Kind, TM);
 }
+
+const MCExpr *AArch64_ELFTargetObjectFile::getDebugThreadLocalSymbol(
+    const MCSymbol *Sym) const {
+  return MCSymbolRefExpr::create(Sym, AArch64::S_DTPREL, getContext());
+}
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
index 6b3381452c70b..78c0c22da8d1b 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
@@ -40,6 +40,9 @@ class AArch64_ELFTargetObjectFile : public TargetLoweringObjectFileELF {
 
   MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind,
                                     const TargetMachine &TM) const override;
+
+  /// Describe a TLS variable address within debug info.
+  const MCExpr *getDebugThreadLocalSymbol(const MCSymbol *Sym) const override;
 };
 
 /// AArch64_MachoTargetObjectFile - This TLOF implementation is used for Darwin.
diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
index f16fc6cfefa42..35dc8e6e9902d 100644
--- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
+++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
@@ -4421,6 +4421,7 @@ bool AArch64AsmParser::parseSymbolicImmVal(const MCExpr *&ImmVal) {
                   .Case("prel_g1_nc", AArch64::S_PREL_G1_NC)
                   .Case("prel_g0", AArch64::S_PREL_G0)
                   .Case("prel_g0_nc", AArch64::S_PREL_G0_NC)
+                  .Case("dtprel", AArch64::S_DTPREL)
                   .Case("dtprel_g2", AArch64::S_DTPREL_G2)
                   .Case("dtprel_g1", AArch64::S_DTPREL_G1)
                   .Case("dtprel_g1_nc", AArch64::S_DTPREL_G1_NC)
@@ -8191,6 +8192,8 @@ bool AArch64AsmParser::parseDataExpr(const MCExpr *&Res) {
       Spec = AArch64::S_GOTPCREL;
     else if (Identifier == "plt")
       Spec = AArch64::S_PLT;
+    else if (Identifier == "dtprel")
+      Spec = AArch64::S_DTPREL;
   }
   if (Spec == AArch64::S_None)
     return Error(Loc, "invalid relocation specifier");
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
index ebd5f30796195..9a9dd369f1ff3 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
@@ -234,6 +234,9 @@ unsigned AArch64ELFObjectWriter::getRelocType(const MCFixup &Fixup,
       }
       if (RefKind == AArch64::S_AUTH || RefKind == AArch64::S_AUTHADDR)
         return ELF::R_AARCH64_AUTH_ABS64;
+      if (RefKind == AArch64::S_DTPREL) {
+        return ELF::R_AARCH64_TLS_DTPREL64;
+      }
       return ELF::R_AARCH64_ABS64;
     }
     case AArch64::fixup_aarch64_add_imm12:
@@ -448,6 +451,8 @@ unsigned AArch64ELFObjectWriter::getRelocType(const MCFixup &Fixup,
         return R_CLS(MOVW_PREL_G0);
       if (RefKind == AArch64::S_PREL_G0_NC)
         return R_CLS(MOVW_PREL_G0_NC);
+      if (RefKind == AArch64::S_DTPREL)
+        return ELF::R_AARCH64_TLS_DTPREL64;
       if (RefKind == AArch64::S_DTPREL_G2)
         return ELF::R_AARCH64_TLSLD_MOVW_DTPREL_G2;
       if (RefKind == AArch64::S_DTPREL_G1)
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
index c08664944f727..01c50d13f1945 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
@@ -41,6 +41,7 @@ const MCAsmInfo::AtSpecifier ELFAtSpecifiers[] = {
     {AArch64::S_GOT, "GOT"},
     {AArch64::S_GOTPCREL, "GOTPCREL"},
     {AArch64::S_PLT, "PLT"},
+    {AArch64::S_DTPREL, "DTPREL"},
 };
 
 const MCAsmInfo::AtSpecifier MachOAtSpecifiers[] = {
@@ -76,6 +77,7 @@ StringRef AArch64::getSpecifierName(const MCSpecifierExpr &Expr) {
   case AArch64::S_PREL_G1_NC:          return ":prel_g1_nc:";
   case AArch64::S_PREL_G0:             return ":prel_g0:";
   case AArch64::S_PREL_G0_NC:          return ":prel_g0_nc:";
+  case AArch64::S_DTPREL:              return ":dtprel:";
   case AArch64::S_DTPREL_G2:           return ":dtprel_g2:";
   case AArch64::S_DTPREL_G1:           return ":dtprel_g1:";
   case AArch64::S_DTPREL_G1_NC:        return ":dtprel_g1_nc:";
diff --git a/llvm/test/CodeGen/AArch64/tls-dtprel64.ll b/llvm/test/CodeGen/AArch64/tls-dtprel64.ll
new file mode 100644
index 0000000000000..c481980354905
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/tls-dtprel64.ll
@@ -0,0 +1,37 @@
+; RUN: llc -O0 -mtriple=aarch64-linux-gnu -filetype=obj < %s \
+; RUN: | llvm-objdump -r - | FileCheck %s
+
+; CHECK: R_AARCH64_TLS_DTPREL64
+
+@var = thread_local global i32 0, align 4, !dbg !0
+
+; Function Attrs: noinline nounwind optnone
+define i32 @foo() #0 !dbg !11 {
+entry:
+  %0 = load i32, ptr @var, align 4, !dbg !14
+  ret i32 %0, !dbg !15
+}
+
+attributes #0 = { noinline nounwind optnone }
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "var", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 7.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5)
+!3 = !DIFile(filename: "tls-at-location.c", directory: "/home/lliu0/llvm/tls-at-location/DebugInfo/AArch64")
+!4 = !{}
+!5 = !{!0}
+!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!7 = !{i32 2, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 7.0.0"}
+!11 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 3, type: !12, isLocal: false, isDefinition: true, scopeLine: 3, isOptimized: false, unit: !2)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!6}
+!14 = !DILocation(line: 4, column: 10, scope: !11)
+!15 = !DILocation(line: 4, column: 3, scope: !11)
+
diff --git a/llvm/test/DebugInfo/AArch64/tls-at-location.ll b/llvm/test/DebugInfo/AArch64/tls-at-location.ll
index 20a0afb789771..d619962ccbf69 100644
--- a/llvm/test/DebugInfo/AArch64/tls-at-location.ll
+++ b/llvm/test/DebugInfo/AArch64/tls-at-location.ll
@@ -1,8 +1,14 @@
-; RUN: llc -filetype=obj -mtriple=aarch64--linux-gnu -o - %s | llvm-dwarfdump -v - | FileCheck %s
-;
+; RUN: llc -O0 -mtriple=aarch64-non-linux-gnu -filetype=obj < %s \
+; RUN:  | llvm-dwarfdump - | FileCheck %s
+
 ; CHECK: .debug_info contents:
 ; CHECK: DW_TAG_variable
-; CHECK-NOT: DW_AT_location
+; CHECK:   DW_AT_name      ("var")
+; CHECK-NEXT:   DW_AT_type      (0x00000040 "int")
+; CHECK-NEXT:   DW_AT_external  (true)
+; CHECK-NEXT:   DW_AT_decl_file ("{{.*}}tls-at-location.c")
+; CHECK-NEXT:   DW_AT_decl_line (1)
+; CHECK-NEXT:   DW_AT_location  (DW_OP_const8u 0x0, DW_OP_GNU_push_tls_address)
 
 @var = thread_local global i32 0, align 4, !dbg !0
 
diff --git a/llvm/test/MC/AArch64/tls-dtprel64.s b/llvm/test/MC/AArch64/tls-dtprel64.s
new file mode 100644
index 0000000000000..7cb47f9543f68
--- /dev/null
+++ b/llvm/test/MC/AArch64/tls-dtprel64.s
@@ -0,0 +1,38 @@
+# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux-gnu %s | llvm-readobj -r - | FileCheck %s
+  
+# CHECK: Relocations [
+# CHECK:   Section {{.*}} .rela.debug_info {
+# CHECK:     0x{{[0-9A-F]+}} R_AARCH64_TLS_DTPREL64 var {{.*}}
+# CHECK:   }
+
+.section .tdata,"awT",@progbits
+.globl var
+var:
+  .word 0
+
+.section .debug_abbrev,"",@progbits
+.byte 1                  // Abbreviation Code
+.byte 17                 // DW_TAG_compile_unit
+.byte 1                  // DW_CHILDREN_yes
+.byte 0                  // EOM(1)
+.byte 0                  // EOM(2)
+
+.byte 2                  // Abbreviation Code
+.byte 52                 // DW_TAG_variable
+.byte 0                  // DW_CHILDREN_no
+.byte 2;                 // DW_AT_location
+.byte 24                 // DW_FORM_exprloc
+.byte 0                  // EOM(1)
+.byte 0                  // EOM(2)
+
+.section        .debug_info,"",@progbits
+.Lcu_begin0:
+  .word .Lcu_end - .Lcu_body // Length of Unit
+.Lcu_body:
+  .hword 4               // DWARF version number
+  .word   .debug_abbrev  // Offset Into Abbrev. Section
+  .byte   8              // Address Size (in bytes)
+  .byte   1              // Abbrev [1] DW_TAG_compile_unit
+  .byte   2              // Abbrev [2] DW_TAG_variable
+  .xword  var@DTPREL
+.Lcu_end:

@llvmbot

llvmbot commented Jul 1, 2025

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-lld

Author: Shivam Gupta (xgupta)

Changes

This adds an implementation of getDebugThreadLocalSymbol for AArch64 by using AArch::S_DTPREL.

Fixes #83466


Full diff: https://github.com/llvm/llvm-project/pull/146572.diff

10 Files Affected:

  • (modified) lld/ELF/Arch/AArch64.cpp (+6)
  • (modified) lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp (+1)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp (+6-1)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.h (+3)
  • (modified) llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp (+3)
  • (modified) llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp (+5)
  • (modified) llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp (+2)
  • (added) llvm/test/CodeGen/AArch64/tls-dtprel64.ll (+37)
  • (modified) llvm/test/DebugInfo/AArch64/tls-at-location.ll (+9-3)
  • (added) llvm/test/MC/AArch64/tls-dtprel64.s (+38)
diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 1812f2af419d2..7576d904e3a60 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -156,6 +156,8 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
     return R_ABS;
   case R_AARCH64_AUTH_ABS64:
     return RE_AARCH64_AUTH;
+  case R_AARCH64_TLS_DTPREL64:
+    return R_DTPREL;
   case R_AARCH64_TLSDESC_ADR_PAGE21:
     return RE_AARCH64_TLSDESC_PAGE;
   case R_AARCH64_AUTH_TLSDESC_ADR_PAGE21:
@@ -542,6 +544,10 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel,
     if (isInt<32>(val))
       write32(ctx, loc, val);
     break;
+  case R_AARCH64_TLS_DTPREL64:
+    checkInt(ctx, loc, val, 64, rel);
+    write64(ctx, loc, val);
+    break;
   case R_AARCH64_ADD_ABS_LO12_NC:
   case R_AARCH64_AUTH_GOT_ADD_LO12_NC:
     write32Imm12(loc, val);
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index f69358de6a288..4593b7253a574 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -2843,6 +2843,7 @@ unsigned ObjectFileELF::ApplyRelocations(
       case llvm::ELF::EM_AARCH64:
         switch (reloc_type(rel)) {
         case R_AARCH64_ABS64:
+        case R_AARCH64_TLS_DTPREL64:
           ApplyELF64ABS64Relocation(symtab, rel, debug_data, rel_section);
           break;
         case R_AARCH64_ABS32:
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
index c218831ce0400..f76103733969a 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
@@ -30,7 +30,7 @@ void AArch64_ELFTargetObjectFile::Initialize(MCContext &Ctx,
 
   // AARCH64 ELF ABI does not define static relocation type for TLS offset
   // within a module.  Do not generate AT_location for TLS variables.
-  SupportDebugThreadLocalLocation = false;
+  SupportDebugThreadLocalLocation = true;
 
   // Make sure the implicitly created empty .text section has the
   // SHF_AARCH64_PURECODE flag set if the "+execute-only" target feature is
@@ -186,3 +186,8 @@ MCSection *AArch64_ELFTargetObjectFile::SelectSectionForGlobal(
 
   return TargetLoweringObjectFileELF::SelectSectionForGlobal(GO, Kind, TM);
 }
+
+const MCExpr *AArch64_ELFTargetObjectFile::getDebugThreadLocalSymbol(
+    const MCSymbol *Sym) const {
+  return MCSymbolRefExpr::create(Sym, AArch64::S_DTPREL, getContext());
+}
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
index 6b3381452c70b..78c0c22da8d1b 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
@@ -40,6 +40,9 @@ class AArch64_ELFTargetObjectFile : public TargetLoweringObjectFileELF {
 
   MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind,
                                     const TargetMachine &TM) const override;
+
+  /// Describe a TLS variable address within debug info.
+  const MCExpr *getDebugThreadLocalSymbol(const MCSymbol *Sym) const override;
 };
 
 /// AArch64_MachoTargetObjectFile - This TLOF implementation is used for Darwin.
diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
index f16fc6cfefa42..35dc8e6e9902d 100644
--- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
+++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
@@ -4421,6 +4421,7 @@ bool AArch64AsmParser::parseSymbolicImmVal(const MCExpr *&ImmVal) {
                   .Case("prel_g1_nc", AArch64::S_PREL_G1_NC)
                   .Case("prel_g0", AArch64::S_PREL_G0)
                   .Case("prel_g0_nc", AArch64::S_PREL_G0_NC)
+                  .Case("dtprel", AArch64::S_DTPREL)
                   .Case("dtprel_g2", AArch64::S_DTPREL_G2)
                   .Case("dtprel_g1", AArch64::S_DTPREL_G1)
                   .Case("dtprel_g1_nc", AArch64::S_DTPREL_G1_NC)
@@ -8191,6 +8192,8 @@ bool AArch64AsmParser::parseDataExpr(const MCExpr *&Res) {
       Spec = AArch64::S_GOTPCREL;
     else if (Identifier == "plt")
       Spec = AArch64::S_PLT;
+    else if (Identifier == "dtprel")
+      Spec = AArch64::S_DTPREL;
   }
   if (Spec == AArch64::S_None)
     return Error(Loc, "invalid relocation specifier");
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
index ebd5f30796195..9a9dd369f1ff3 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
@@ -234,6 +234,9 @@ unsigned AArch64ELFObjectWriter::getRelocType(const MCFixup &Fixup,
       }
       if (RefKind == AArch64::S_AUTH || RefKind == AArch64::S_AUTHADDR)
         return ELF::R_AARCH64_AUTH_ABS64;
+      if (RefKind == AArch64::S_DTPREL) {
+        return ELF::R_AARCH64_TLS_DTPREL64;
+      }
       return ELF::R_AARCH64_ABS64;
     }
     case AArch64::fixup_aarch64_add_imm12:
@@ -448,6 +451,8 @@ unsigned AArch64ELFObjectWriter::getRelocType(const MCFixup &Fixup,
         return R_CLS(MOVW_PREL_G0);
       if (RefKind == AArch64::S_PREL_G0_NC)
         return R_CLS(MOVW_PREL_G0_NC);
+      if (RefKind == AArch64::S_DTPREL)
+        return ELF::R_AARCH64_TLS_DTPREL64;
       if (RefKind == AArch64::S_DTPREL_G2)
         return ELF::R_AARCH64_TLSLD_MOVW_DTPREL_G2;
       if (RefKind == AArch64::S_DTPREL_G1)
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
index c08664944f727..01c50d13f1945 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
@@ -41,6 +41,7 @@ const MCAsmInfo::AtSpecifier ELFAtSpecifiers[] = {
     {AArch64::S_GOT, "GOT"},
     {AArch64::S_GOTPCREL, "GOTPCREL"},
     {AArch64::S_PLT, "PLT"},
+    {AArch64::S_DTPREL, "DTPREL"},
 };
 
 const MCAsmInfo::AtSpecifier MachOAtSpecifiers[] = {
@@ -76,6 +77,7 @@ StringRef AArch64::getSpecifierName(const MCSpecifierExpr &Expr) {
   case AArch64::S_PREL_G1_NC:          return ":prel_g1_nc:";
   case AArch64::S_PREL_G0:             return ":prel_g0:";
   case AArch64::S_PREL_G0_NC:          return ":prel_g0_nc:";
+  case AArch64::S_DTPREL:              return ":dtprel:";
   case AArch64::S_DTPREL_G2:           return ":dtprel_g2:";
   case AArch64::S_DTPREL_G1:           return ":dtprel_g1:";
   case AArch64::S_DTPREL_G1_NC:        return ":dtprel_g1_nc:";
diff --git a/llvm/test/CodeGen/AArch64/tls-dtprel64.ll b/llvm/test/CodeGen/AArch64/tls-dtprel64.ll
new file mode 100644
index 0000000000000..c481980354905
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/tls-dtprel64.ll
@@ -0,0 +1,37 @@
+; RUN: llc -O0 -mtriple=aarch64-linux-gnu -filetype=obj < %s \
+; RUN: | llvm-objdump -r - | FileCheck %s
+
+; CHECK: R_AARCH64_TLS_DTPREL64
+
+@var = thread_local global i32 0, align 4, !dbg !0
+
+; Function Attrs: noinline nounwind optnone
+define i32 @foo() #0 !dbg !11 {
+entry:
+  %0 = load i32, ptr @var, align 4, !dbg !14
+  ret i32 %0, !dbg !15
+}
+
+attributes #0 = { noinline nounwind optnone }
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "var", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 7.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5)
+!3 = !DIFile(filename: "tls-at-location.c", directory: "/home/lliu0/llvm/tls-at-location/DebugInfo/AArch64")
+!4 = !{}
+!5 = !{!0}
+!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!7 = !{i32 2, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 7.0.0"}
+!11 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 3, type: !12, isLocal: false, isDefinition: true, scopeLine: 3, isOptimized: false, unit: !2)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!6}
+!14 = !DILocation(line: 4, column: 10, scope: !11)
+!15 = !DILocation(line: 4, column: 3, scope: !11)
+
diff --git a/llvm/test/DebugInfo/AArch64/tls-at-location.ll b/llvm/test/DebugInfo/AArch64/tls-at-location.ll
index 20a0afb789771..d619962ccbf69 100644
--- a/llvm/test/DebugInfo/AArch64/tls-at-location.ll
+++ b/llvm/test/DebugInfo/AArch64/tls-at-location.ll
@@ -1,8 +1,14 @@
-; RUN: llc -filetype=obj -mtriple=aarch64--linux-gnu -o - %s | llvm-dwarfdump -v - | FileCheck %s
-;
+; RUN: llc -O0 -mtriple=aarch64-non-linux-gnu -filetype=obj < %s \
+; RUN:  | llvm-dwarfdump - | FileCheck %s
+
 ; CHECK: .debug_info contents:
 ; CHECK: DW_TAG_variable
-; CHECK-NOT: DW_AT_location
+; CHECK:   DW_AT_name      ("var")
+; CHECK-NEXT:   DW_AT_type      (0x00000040 "int")
+; CHECK-NEXT:   DW_AT_external  (true)
+; CHECK-NEXT:   DW_AT_decl_file ("{{.*}}tls-at-location.c")
+; CHECK-NEXT:   DW_AT_decl_line (1)
+; CHECK-NEXT:   DW_AT_location  (DW_OP_const8u 0x0, DW_OP_GNU_push_tls_address)
 
 @var = thread_local global i32 0, align 4, !dbg !0
 
diff --git a/llvm/test/MC/AArch64/tls-dtprel64.s b/llvm/test/MC/AArch64/tls-dtprel64.s
new file mode 100644
index 0000000000000..7cb47f9543f68
--- /dev/null
+++ b/llvm/test/MC/AArch64/tls-dtprel64.s
@@ -0,0 +1,38 @@
+# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux-gnu %s | llvm-readobj -r - | FileCheck %s
+  
+# CHECK: Relocations [
+# CHECK:   Section {{.*}} .rela.debug_info {
+# CHECK:     0x{{[0-9A-F]+}} R_AARCH64_TLS_DTPREL64 var {{.*}}
+# CHECK:   }
+
+.section .tdata,"awT",@progbits
+.globl var
+var:
+  .word 0
+
+.section .debug_abbrev,"",@progbits
+.byte 1                  // Abbreviation Code
+.byte 17                 // DW_TAG_compile_unit
+.byte 1                  // DW_CHILDREN_yes
+.byte 0                  // EOM(1)
+.byte 0                  // EOM(2)
+
+.byte 2                  // Abbreviation Code
+.byte 52                 // DW_TAG_variable
+.byte 0                  // DW_CHILDREN_no
+.byte 2;                 // DW_AT_location
+.byte 24                 // DW_FORM_exprloc
+.byte 0                  // EOM(1)
+.byte 0                  // EOM(2)
+
+.section        .debug_info,"",@progbits
+.Lcu_begin0:
+  .word .Lcu_end - .Lcu_body // Length of Unit
+.Lcu_body:
+  .hword 4               // DWARF version number
+  .word   .debug_abbrev  // Offset Into Abbrev. Section
+  .byte   8              // Address Size (in bytes)
+  .byte   1              // Abbrev [1] DW_TAG_compile_unit
+  .byte   2              // Abbrev [2] DW_TAG_variable
+  .xword  var@DTPREL
+.Lcu_end:

@llvmbot

llvmbot commented Jul 1, 2025

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-lldb

Author: Shivam Gupta (xgupta)

Changes

This adds an implementation of getDebugThreadLocalSymbol for AArch64 by using AArch::S_DTPREL.

Fixes #83466


Full diff: https://github.com/llvm/llvm-project/pull/146572.diff

10 Files Affected:

  • (modified) lld/ELF/Arch/AArch64.cpp (+6)
  • (modified) lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp (+1)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp (+6-1)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.h (+3)
  • (modified) llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp (+3)
  • (modified) llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp (+5)
  • (modified) llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp (+2)
  • (added) llvm/test/CodeGen/AArch64/tls-dtprel64.ll (+37)
  • (modified) llvm/test/DebugInfo/AArch64/tls-at-location.ll (+9-3)
  • (added) llvm/test/MC/AArch64/tls-dtprel64.s (+38)
diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 1812f2af419d2..7576d904e3a60 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -156,6 +156,8 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
     return R_ABS;
   case R_AARCH64_AUTH_ABS64:
     return RE_AARCH64_AUTH;
+  case R_AARCH64_TLS_DTPREL64:
+    return R_DTPREL;
   case R_AARCH64_TLSDESC_ADR_PAGE21:
     return RE_AARCH64_TLSDESC_PAGE;
   case R_AARCH64_AUTH_TLSDESC_ADR_PAGE21:
@@ -542,6 +544,10 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel,
     if (isInt<32>(val))
       write32(ctx, loc, val);
     break;
+  case R_AARCH64_TLS_DTPREL64:
+    checkInt(ctx, loc, val, 64, rel);
+    write64(ctx, loc, val);
+    break;
   case R_AARCH64_ADD_ABS_LO12_NC:
   case R_AARCH64_AUTH_GOT_ADD_LO12_NC:
     write32Imm12(loc, val);
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index f69358de6a288..4593b7253a574 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -2843,6 +2843,7 @@ unsigned ObjectFileELF::ApplyRelocations(
       case llvm::ELF::EM_AARCH64:
         switch (reloc_type(rel)) {
         case R_AARCH64_ABS64:
+        case R_AARCH64_TLS_DTPREL64:
           ApplyELF64ABS64Relocation(symtab, rel, debug_data, rel_section);
           break;
         case R_AARCH64_ABS32:
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
index c218831ce0400..f76103733969a 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
@@ -30,7 +30,7 @@ void AArch64_ELFTargetObjectFile::Initialize(MCContext &Ctx,
 
   // AARCH64 ELF ABI does not define static relocation type for TLS offset
   // within a module.  Do not generate AT_location for TLS variables.
-  SupportDebugThreadLocalLocation = false;
+  SupportDebugThreadLocalLocation = true;
 
   // Make sure the implicitly created empty .text section has the
   // SHF_AARCH64_PURECODE flag set if the "+execute-only" target feature is
@@ -186,3 +186,8 @@ MCSection *AArch64_ELFTargetObjectFile::SelectSectionForGlobal(
 
   return TargetLoweringObjectFileELF::SelectSectionForGlobal(GO, Kind, TM);
 }
+
+const MCExpr *AArch64_ELFTargetObjectFile::getDebugThreadLocalSymbol(
+    const MCSymbol *Sym) const {
+  return MCSymbolRefExpr::create(Sym, AArch64::S_DTPREL, getContext());
+}
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
index 6b3381452c70b..78c0c22da8d1b 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
@@ -40,6 +40,9 @@ class AArch64_ELFTargetObjectFile : public TargetLoweringObjectFileELF {
 
   MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind,
                                     const TargetMachine &TM) const override;
+
+  /// Describe a TLS variable address within debug info.
+  const MCExpr *getDebugThreadLocalSymbol(const MCSymbol *Sym) const override;
 };
 
 /// AArch64_MachoTargetObjectFile - This TLOF implementation is used for Darwin.
diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
index f16fc6cfefa42..35dc8e6e9902d 100644
--- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
+++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
@@ -4421,6 +4421,7 @@ bool AArch64AsmParser::parseSymbolicImmVal(const MCExpr *&ImmVal) {
                   .Case("prel_g1_nc", AArch64::S_PREL_G1_NC)
                   .Case("prel_g0", AArch64::S_PREL_G0)
                   .Case("prel_g0_nc", AArch64::S_PREL_G0_NC)
+                  .Case("dtprel", AArch64::S_DTPREL)
                   .Case("dtprel_g2", AArch64::S_DTPREL_G2)
                   .Case("dtprel_g1", AArch64::S_DTPREL_G1)
                   .Case("dtprel_g1_nc", AArch64::S_DTPREL_G1_NC)
@@ -8191,6 +8192,8 @@ bool AArch64AsmParser::parseDataExpr(const MCExpr *&Res) {
       Spec = AArch64::S_GOTPCREL;
     else if (Identifier == "plt")
       Spec = AArch64::S_PLT;
+    else if (Identifier == "dtprel")
+      Spec = AArch64::S_DTPREL;
   }
   if (Spec == AArch64::S_None)
     return Error(Loc, "invalid relocation specifier");
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
index ebd5f30796195..9a9dd369f1ff3 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
@@ -234,6 +234,9 @@ unsigned AArch64ELFObjectWriter::getRelocType(const MCFixup &Fixup,
       }
       if (RefKind == AArch64::S_AUTH || RefKind == AArch64::S_AUTHADDR)
         return ELF::R_AARCH64_AUTH_ABS64;
+      if (RefKind == AArch64::S_DTPREL) {
+        return ELF::R_AARCH64_TLS_DTPREL64;
+      }
       return ELF::R_AARCH64_ABS64;
     }
     case AArch64::fixup_aarch64_add_imm12:
@@ -448,6 +451,8 @@ unsigned AArch64ELFObjectWriter::getRelocType(const MCFixup &Fixup,
         return R_CLS(MOVW_PREL_G0);
       if (RefKind == AArch64::S_PREL_G0_NC)
         return R_CLS(MOVW_PREL_G0_NC);
+      if (RefKind == AArch64::S_DTPREL)
+        return ELF::R_AARCH64_TLS_DTPREL64;
       if (RefKind == AArch64::S_DTPREL_G2)
         return ELF::R_AARCH64_TLSLD_MOVW_DTPREL_G2;
       if (RefKind == AArch64::S_DTPREL_G1)
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
index c08664944f727..01c50d13f1945 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
@@ -41,6 +41,7 @@ const MCAsmInfo::AtSpecifier ELFAtSpecifiers[] = {
     {AArch64::S_GOT, "GOT"},
     {AArch64::S_GOTPCREL, "GOTPCREL"},
     {AArch64::S_PLT, "PLT"},
+    {AArch64::S_DTPREL, "DTPREL"},
 };
 
 const MCAsmInfo::AtSpecifier MachOAtSpecifiers[] = {
@@ -76,6 +77,7 @@ StringRef AArch64::getSpecifierName(const MCSpecifierExpr &Expr) {
   case AArch64::S_PREL_G1_NC:          return ":prel_g1_nc:";
   case AArch64::S_PREL_G0:             return ":prel_g0:";
   case AArch64::S_PREL_G0_NC:          return ":prel_g0_nc:";
+  case AArch64::S_DTPREL:              return ":dtprel:";
   case AArch64::S_DTPREL_G2:           return ":dtprel_g2:";
   case AArch64::S_DTPREL_G1:           return ":dtprel_g1:";
   case AArch64::S_DTPREL_G1_NC:        return ":dtprel_g1_nc:";
diff --git a/llvm/test/CodeGen/AArch64/tls-dtprel64.ll b/llvm/test/CodeGen/AArch64/tls-dtprel64.ll
new file mode 100644
index 0000000000000..c481980354905
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/tls-dtprel64.ll
@@ -0,0 +1,37 @@
+; RUN: llc -O0 -mtriple=aarch64-linux-gnu -filetype=obj < %s \
+; RUN: | llvm-objdump -r - | FileCheck %s
+
+; CHECK: R_AARCH64_TLS_DTPREL64
+
+@var = thread_local global i32 0, align 4, !dbg !0
+
+; Function Attrs: noinline nounwind optnone
+define i32 @foo() #0 !dbg !11 {
+entry:
+  %0 = load i32, ptr @var, align 4, !dbg !14
+  ret i32 %0, !dbg !15
+}
+
+attributes #0 = { noinline nounwind optnone }
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "var", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 7.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5)
+!3 = !DIFile(filename: "tls-at-location.c", directory: "/home/lliu0/llvm/tls-at-location/DebugInfo/AArch64")
+!4 = !{}
+!5 = !{!0}
+!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!7 = !{i32 2, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 7.0.0"}
+!11 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 3, type: !12, isLocal: false, isDefinition: true, scopeLine: 3, isOptimized: false, unit: !2)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!6}
+!14 = !DILocation(line: 4, column: 10, scope: !11)
+!15 = !DILocation(line: 4, column: 3, scope: !11)
+
diff --git a/llvm/test/DebugInfo/AArch64/tls-at-location.ll b/llvm/test/DebugInfo/AArch64/tls-at-location.ll
index 20a0afb789771..d619962ccbf69 100644
--- a/llvm/test/DebugInfo/AArch64/tls-at-location.ll
+++ b/llvm/test/DebugInfo/AArch64/tls-at-location.ll
@@ -1,8 +1,14 @@
-; RUN: llc -filetype=obj -mtriple=aarch64--linux-gnu -o - %s | llvm-dwarfdump -v - | FileCheck %s
-;
+; RUN: llc -O0 -mtriple=aarch64-non-linux-gnu -filetype=obj < %s \
+; RUN:  | llvm-dwarfdump - | FileCheck %s
+
 ; CHECK: .debug_info contents:
 ; CHECK: DW_TAG_variable
-; CHECK-NOT: DW_AT_location
+; CHECK:   DW_AT_name      ("var")
+; CHECK-NEXT:   DW_AT_type      (0x00000040 "int")
+; CHECK-NEXT:   DW_AT_external  (true)
+; CHECK-NEXT:   DW_AT_decl_file ("{{.*}}tls-at-location.c")
+; CHECK-NEXT:   DW_AT_decl_line (1)
+; CHECK-NEXT:   DW_AT_location  (DW_OP_const8u 0x0, DW_OP_GNU_push_tls_address)
 
 @var = thread_local global i32 0, align 4, !dbg !0
 
diff --git a/llvm/test/MC/AArch64/tls-dtprel64.s b/llvm/test/MC/AArch64/tls-dtprel64.s
new file mode 100644
index 0000000000000..7cb47f9543f68
--- /dev/null
+++ b/llvm/test/MC/AArch64/tls-dtprel64.s
@@ -0,0 +1,38 @@
+# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux-gnu %s | llvm-readobj -r - | FileCheck %s
+  
+# CHECK: Relocations [
+# CHECK:   Section {{.*}} .rela.debug_info {
+# CHECK:     0x{{[0-9A-F]+}} R_AARCH64_TLS_DTPREL64 var {{.*}}
+# CHECK:   }
+
+.section .tdata,"awT",@progbits
+.globl var
+var:
+  .word 0
+
+.section .debug_abbrev,"",@progbits
+.byte 1                  // Abbreviation Code
+.byte 17                 // DW_TAG_compile_unit
+.byte 1                  // DW_CHILDREN_yes
+.byte 0                  // EOM(1)
+.byte 0                  // EOM(2)
+
+.byte 2                  // Abbreviation Code
+.byte 52                 // DW_TAG_variable
+.byte 0                  // DW_CHILDREN_no
+.byte 2;                 // DW_AT_location
+.byte 24                 // DW_FORM_exprloc
+.byte 0                  // EOM(1)
+.byte 0                  // EOM(2)
+
+.section        .debug_info,"",@progbits
+.Lcu_begin0:
+  .word .Lcu_end - .Lcu_body // Length of Unit
+.Lcu_body:
+  .hword 4               // DWARF version number
+  .word   .debug_abbrev  // Offset Into Abbrev. Section
+  .byte   8              // Address Size (in bytes)
+  .byte   1              // Abbrev [1] DW_TAG_compile_unit
+  .byte   2              // Abbrev [2] DW_TAG_variable
+  .xword  var@DTPREL
+.Lcu_end:

@llvmbot

llvmbot commented Jul 1, 2025

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-debuginfo

Author: Shivam Gupta (xgupta)

Changes

This adds an implementation of getDebugThreadLocalSymbol for AArch64 by using AArch::S_DTPREL.

Fixes #83466


Full diff: https://github.com/llvm/llvm-project/pull/146572.diff

10 Files Affected:

  • (modified) lld/ELF/Arch/AArch64.cpp (+6)
  • (modified) lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp (+1)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp (+6-1)
  • (modified) llvm/lib/Target/AArch64/AArch64TargetObjectFile.h (+3)
  • (modified) llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp (+3)
  • (modified) llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp (+5)
  • (modified) llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp (+2)
  • (added) llvm/test/CodeGen/AArch64/tls-dtprel64.ll (+37)
  • (modified) llvm/test/DebugInfo/AArch64/tls-at-location.ll (+9-3)
  • (added) llvm/test/MC/AArch64/tls-dtprel64.s (+38)
diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 1812f2af419d2..7576d904e3a60 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -156,6 +156,8 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
     return R_ABS;
   case R_AARCH64_AUTH_ABS64:
     return RE_AARCH64_AUTH;
+  case R_AARCH64_TLS_DTPREL64:
+    return R_DTPREL;
   case R_AARCH64_TLSDESC_ADR_PAGE21:
     return RE_AARCH64_TLSDESC_PAGE;
   case R_AARCH64_AUTH_TLSDESC_ADR_PAGE21:
@@ -542,6 +544,10 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel,
     if (isInt<32>(val))
       write32(ctx, loc, val);
     break;
+  case R_AARCH64_TLS_DTPREL64:
+    checkInt(ctx, loc, val, 64, rel);
+    write64(ctx, loc, val);
+    break;
   case R_AARCH64_ADD_ABS_LO12_NC:
   case R_AARCH64_AUTH_GOT_ADD_LO12_NC:
     write32Imm12(loc, val);
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index f69358de6a288..4593b7253a574 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -2843,6 +2843,7 @@ unsigned ObjectFileELF::ApplyRelocations(
       case llvm::ELF::EM_AARCH64:
         switch (reloc_type(rel)) {
         case R_AARCH64_ABS64:
+        case R_AARCH64_TLS_DTPREL64:
           ApplyELF64ABS64Relocation(symtab, rel, debug_data, rel_section);
           break;
         case R_AARCH64_ABS32:
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
index c218831ce0400..f76103733969a 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp
@@ -30,7 +30,7 @@ void AArch64_ELFTargetObjectFile::Initialize(MCContext &Ctx,
 
   // AARCH64 ELF ABI does not define static relocation type for TLS offset
   // within a module.  Do not generate AT_location for TLS variables.
-  SupportDebugThreadLocalLocation = false;
+  SupportDebugThreadLocalLocation = true;
 
   // Make sure the implicitly created empty .text section has the
   // SHF_AARCH64_PURECODE flag set if the "+execute-only" target feature is
@@ -186,3 +186,8 @@ MCSection *AArch64_ELFTargetObjectFile::SelectSectionForGlobal(
 
   return TargetLoweringObjectFileELF::SelectSectionForGlobal(GO, Kind, TM);
 }
+
+const MCExpr *AArch64_ELFTargetObjectFile::getDebugThreadLocalSymbol(
+    const MCSymbol *Sym) const {
+  return MCSymbolRefExpr::create(Sym, AArch64::S_DTPREL, getContext());
+}
diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
index 6b3381452c70b..78c0c22da8d1b 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
+++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h
@@ -40,6 +40,9 @@ class AArch64_ELFTargetObjectFile : public TargetLoweringObjectFileELF {
 
   MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind,
                                     const TargetMachine &TM) const override;
+
+  /// Describe a TLS variable address within debug info.
+  const MCExpr *getDebugThreadLocalSymbol(const MCSymbol *Sym) const override;
 };
 
 /// AArch64_MachoTargetObjectFile - This TLOF implementation is used for Darwin.
diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
index f16fc6cfefa42..35dc8e6e9902d 100644
--- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
+++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
@@ -4421,6 +4421,7 @@ bool AArch64AsmParser::parseSymbolicImmVal(const MCExpr *&ImmVal) {
                   .Case("prel_g1_nc", AArch64::S_PREL_G1_NC)
                   .Case("prel_g0", AArch64::S_PREL_G0)
                   .Case("prel_g0_nc", AArch64::S_PREL_G0_NC)
+                  .Case("dtprel", AArch64::S_DTPREL)
                   .Case("dtprel_g2", AArch64::S_DTPREL_G2)
                   .Case("dtprel_g1", AArch64::S_DTPREL_G1)
                   .Case("dtprel_g1_nc", AArch64::S_DTPREL_G1_NC)
@@ -8191,6 +8192,8 @@ bool AArch64AsmParser::parseDataExpr(const MCExpr *&Res) {
       Spec = AArch64::S_GOTPCREL;
     else if (Identifier == "plt")
       Spec = AArch64::S_PLT;
+    else if (Identifier == "dtprel")
+      Spec = AArch64::S_DTPREL;
   }
   if (Spec == AArch64::S_None)
     return Error(Loc, "invalid relocation specifier");
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
index ebd5f30796195..9a9dd369f1ff3 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFObjectWriter.cpp
@@ -234,6 +234,9 @@ unsigned AArch64ELFObjectWriter::getRelocType(const MCFixup &Fixup,
       }
       if (RefKind == AArch64::S_AUTH || RefKind == AArch64::S_AUTHADDR)
         return ELF::R_AARCH64_AUTH_ABS64;
+      if (RefKind == AArch64::S_DTPREL) {
+        return ELF::R_AARCH64_TLS_DTPREL64;
+      }
       return ELF::R_AARCH64_ABS64;
     }
     case AArch64::fixup_aarch64_add_imm12:
@@ -448,6 +451,8 @@ unsigned AArch64ELFObjectWriter::getRelocType(const MCFixup &Fixup,
         return R_CLS(MOVW_PREL_G0);
       if (RefKind == AArch64::S_PREL_G0_NC)
         return R_CLS(MOVW_PREL_G0_NC);
+      if (RefKind == AArch64::S_DTPREL)
+        return ELF::R_AARCH64_TLS_DTPREL64;
       if (RefKind == AArch64::S_DTPREL_G2)
         return ELF::R_AARCH64_TLSLD_MOVW_DTPREL_G2;
       if (RefKind == AArch64::S_DTPREL_G1)
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
index c08664944f727..01c50d13f1945 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp
@@ -41,6 +41,7 @@ const MCAsmInfo::AtSpecifier ELFAtSpecifiers[] = {
     {AArch64::S_GOT, "GOT"},
     {AArch64::S_GOTPCREL, "GOTPCREL"},
     {AArch64::S_PLT, "PLT"},
+    {AArch64::S_DTPREL, "DTPREL"},
 };
 
 const MCAsmInfo::AtSpecifier MachOAtSpecifiers[] = {
@@ -76,6 +77,7 @@ StringRef AArch64::getSpecifierName(const MCSpecifierExpr &Expr) {
   case AArch64::S_PREL_G1_NC:          return ":prel_g1_nc:";
   case AArch64::S_PREL_G0:             return ":prel_g0:";
   case AArch64::S_PREL_G0_NC:          return ":prel_g0_nc:";
+  case AArch64::S_DTPREL:              return ":dtprel:";
   case AArch64::S_DTPREL_G2:           return ":dtprel_g2:";
   case AArch64::S_DTPREL_G1:           return ":dtprel_g1:";
   case AArch64::S_DTPREL_G1_NC:        return ":dtprel_g1_nc:";
diff --git a/llvm/test/CodeGen/AArch64/tls-dtprel64.ll b/llvm/test/CodeGen/AArch64/tls-dtprel64.ll
new file mode 100644
index 0000000000000..c481980354905
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/tls-dtprel64.ll
@@ -0,0 +1,37 @@
+; RUN: llc -O0 -mtriple=aarch64-linux-gnu -filetype=obj < %s \
+; RUN: | llvm-objdump -r - | FileCheck %s
+
+; CHECK: R_AARCH64_TLS_DTPREL64
+
+@var = thread_local global i32 0, align 4, !dbg !0
+
+; Function Attrs: noinline nounwind optnone
+define i32 @foo() #0 !dbg !11 {
+entry:
+  %0 = load i32, ptr @var, align 4, !dbg !14
+  ret i32 %0, !dbg !15
+}
+
+attributes #0 = { noinline nounwind optnone }
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "var", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 7.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5)
+!3 = !DIFile(filename: "tls-at-location.c", directory: "/home/lliu0/llvm/tls-at-location/DebugInfo/AArch64")
+!4 = !{}
+!5 = !{!0}
+!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!7 = !{i32 2, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 7.0.0"}
+!11 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 3, type: !12, isLocal: false, isDefinition: true, scopeLine: 3, isOptimized: false, unit: !2)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!6}
+!14 = !DILocation(line: 4, column: 10, scope: !11)
+!15 = !DILocation(line: 4, column: 3, scope: !11)
+
diff --git a/llvm/test/DebugInfo/AArch64/tls-at-location.ll b/llvm/test/DebugInfo/AArch64/tls-at-location.ll
index 20a0afb789771..d619962ccbf69 100644
--- a/llvm/test/DebugInfo/AArch64/tls-at-location.ll
+++ b/llvm/test/DebugInfo/AArch64/tls-at-location.ll
@@ -1,8 +1,14 @@
-; RUN: llc -filetype=obj -mtriple=aarch64--linux-gnu -o - %s | llvm-dwarfdump -v - | FileCheck %s
-;
+; RUN: llc -O0 -mtriple=aarch64-non-linux-gnu -filetype=obj < %s \
+; RUN:  | llvm-dwarfdump - | FileCheck %s
+
 ; CHECK: .debug_info contents:
 ; CHECK: DW_TAG_variable
-; CHECK-NOT: DW_AT_location
+; CHECK:   DW_AT_name      ("var")
+; CHECK-NEXT:   DW_AT_type      (0x00000040 "int")
+; CHECK-NEXT:   DW_AT_external  (true)
+; CHECK-NEXT:   DW_AT_decl_file ("{{.*}}tls-at-location.c")
+; CHECK-NEXT:   DW_AT_decl_line (1)
+; CHECK-NEXT:   DW_AT_location  (DW_OP_const8u 0x0, DW_OP_GNU_push_tls_address)
 
 @var = thread_local global i32 0, align 4, !dbg !0
 
diff --git a/llvm/test/MC/AArch64/tls-dtprel64.s b/llvm/test/MC/AArch64/tls-dtprel64.s
new file mode 100644
index 0000000000000..7cb47f9543f68
--- /dev/null
+++ b/llvm/test/MC/AArch64/tls-dtprel64.s
@@ -0,0 +1,38 @@
+# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux-gnu %s | llvm-readobj -r - | FileCheck %s
+  
+# CHECK: Relocations [
+# CHECK:   Section {{.*}} .rela.debug_info {
+# CHECK:     0x{{[0-9A-F]+}} R_AARCH64_TLS_DTPREL64 var {{.*}}
+# CHECK:   }
+
+.section .tdata,"awT",@progbits
+.globl var
+var:
+  .word 0
+
+.section .debug_abbrev,"",@progbits
+.byte 1                  // Abbreviation Code
+.byte 17                 // DW_TAG_compile_unit
+.byte 1                  // DW_CHILDREN_yes
+.byte 0                  // EOM(1)
+.byte 0                  // EOM(2)
+
+.byte 2                  // Abbreviation Code
+.byte 52                 // DW_TAG_variable
+.byte 0                  // DW_CHILDREN_no
+.byte 2;                 // DW_AT_location
+.byte 24                 // DW_FORM_exprloc
+.byte 0                  // EOM(1)
+.byte 0                  // EOM(2)
+
+.section        .debug_info,"",@progbits
+.Lcu_begin0:
+  .word .Lcu_end - .Lcu_body // Length of Unit
+.Lcu_body:
+  .hword 4               // DWARF version number
+  .word   .debug_abbrev  // Offset Into Abbrev. Section
+  .byte   8              // Address Size (in bytes)
+  .byte   1              // Abbrev [1] DW_TAG_compile_unit
+  .byte   2              // Abbrev [2] DW_TAG_variable
+  .xword  var@DTPREL
+.Lcu_end:

@smithp35

smithp35 commented Jul 2, 2025

Copy link
Copy Markdown
Contributor

The relocation used here is currently not defined in the ABI as a static relocation, only a dynamic one. I have got a PR raised on the ABI to change this but I've not yet been able to get any movement on it. ARM-software/abi-aa#330

@xgupta

xgupta commented Jul 2, 2025

Copy link
Copy Markdown
Contributor Author

Thanks, we can wait for the ABI change.

Comment thread llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp Outdated
Comment thread llvm/test/CodeGen/AArch64/tls-dtprel64.ll Outdated
Comment thread llvm/test/CodeGen/AArch64/tls-dtprel64.ll Outdated
Comment thread llvm/test/DebugInfo/AArch64/tls-at-location.ll Outdated

@DavidSpickett DavidSpickett left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just requesting changes so we don't forget about the ABI change.

Might need to coordinate with other toolchains, we can deal with that after ABI discussions.

@smithp35

smithp35 commented Jul 2, 2025

Copy link
Copy Markdown
Contributor

Just requesting changes so we don't forget about the ABI change.

Might need to coordinate with other toolchains, we can deal with that after ABI discussions.

I did a quick check of the support for R_AARCH64_TLS_DTPREL64 in GNU by making up a fake .debug_info section with .reloc

        .global foo
        .type foo, %tls_object
        .section .debug_info, "", %progbits
        .xword 0
        .reloc 0x0, R_AARCH64_TLS_DTPREL64, foo

Looks like there's a small change needed in GNU ld to make this work there

aarch64-none-linux-gnu-ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0
dtprel.o:(.debug_info+0x0): dangerous relocation: unsupported relocation

I think this is just a matter of putting the relocation into the switch statement as all the other parts seem to be there.

We may want to conditionally add the relocation on aarch64-linux-gnu targets as if we put it out universally we're going to make clang -g output incompatible with the system linker for many programs.

@avikivity

Copy link
Copy Markdown
Contributor

This appears to be stalled. I hope progress can be made, this is making debugging programs that make heavy use of thread locals on aarch64 impossible.

@github-actions

github-actions Bot commented Jan 18, 2026 •

Copy link
Copy Markdown

✅ With the latest revision this PR passed the C/C++ code formatter.

@avikivity

Copy link
Copy Markdown
Contributor

I just saw that a relevant ABI change was committed recently: ARM-software/abi-aa@d455ef1

@xgupta

xgupta commented Jan 18, 2026

Copy link
Copy Markdown
Contributor Author

I just saw that a relevant ABI change was committed recently: ARM-software/abi-aa@d455ef1

Yeah, I can try to update syntax in another PR if reviewers wants.

@llvm llvm deleted a comment from github-actions Bot Jan 18, 2026
@llvm llvm deleted a comment from github-actions Bot Jan 18, 2026
@igorkudrin

Copy link
Copy Markdown
Contributor

Hi @xgupta, what are your plans regarding the patch? I have verified it and can confirm that together with my patch #181315 that fixes the calculations of the dtv pointer for ARM and AArch64, it enables debugging thread local variables with LLDB on AArch64 Linux. It would be great if it made it into the next release.

@xgupta

xgupta commented Mar 31, 2026

Copy link
Copy Markdown
Contributor Author

Thanks a lot while working on this. While this isn't fixing a regression, I think a backport to 22.1 is warranted, the aarch64 port is useless without it for a class of programs that use thread-local storage heavily.

Sure, I will create PRs now, and if @MaskRay approves, will merge them to llvm:release/22.x. Thanks for checking.

@avikivity

Copy link
Copy Markdown
Contributor

There's a /cherry-pick command: https://llvm.org/docs/GitHub.html

@llvmbot

llvmbot commented Mar 31, 2026

Copy link
Copy Markdown
Member

There's a /cherry-pick command: https://llvm.org/docs/GitHub.html

Error: Command failed due to missing milestone.

@xgupta

xgupta commented Mar 31, 2026

Copy link
Copy Markdown
Contributor Author

/cherry-pick 8e20a6d

@llvmbot

llvmbot commented Mar 31, 2026

Copy link
Copy Markdown
Member

Failed to cherry-pick: 8e20a6d

https://github.com/llvm/llvm-project/actions/runs/23795290717

Please manually backport the fix and push it to your github fork. Once this is done, please create a pull request

@xgupta

xgupta commented Mar 31, 2026 •

Copy link
Copy Markdown
Contributor Author

There's a /cherry-pick command: https://llvm.org/docs/GitHub.html

I remember now this assembly syntax %dtprel(var) depends on AArch64: Replace @plt/%gotpcrel in data directives with %pltpcrel %gotpcrel which is also not in release branch. Maybe better to wait for @MaskRay opinion whether this should be backport.

@llvmbot

llvmbot commented Mar 31, 2026

Copy link
Copy Markdown
Member

Failed to cherry-pick: command:

https://github.com/llvm/llvm-project/actions/runs/23795907615

Please manually backport the fix and push it to your github fork. Once this is done, please create a pull request

@llvmbot

llvmbot commented Mar 31, 2026

Copy link
Copy Markdown
Member

Failed to cherry-pick: command:

https://github.com/llvm/llvm-project/actions/runs/23796015006

Please manually backport the fix and push it to your github fork. Once this is done, please create a pull request

sriyalamar pushed a commit to sriyalamar/cpullvm-toolchain that referenced this pull request Apr 7, 2026
…esolver.cpp (#187649)

In patch llvm/llvm-project#146572 we have plan
to emit R_AARCH64_TLS_DTPREL64. This give us the warning while using
llvm-dwarfdump for the object file which has tls variables -

warning: failed to compute relocation: R_AARCH64_TLS_DTPREL64, Invalid
data was encountered while parsing the file

To fix this warning we have mark the relocation as supported however
final absolute address of a TLS variable is determined at runtime,
resolving to the symbol's section-relative offset in the object file is
mitigate the warning.

(cherry picked from commit fa136df)
@avikivity

Copy link
Copy Markdown
Contributor

@MaskRay, a gentle reminder about backporting #155776

xgupta added a commit to xgupta/llvm-project that referenced this pull request Apr 8, 2026
…6599)

This patch adds support for the %dtprel relocation specifier in the
AArch64 assembler. This specifier is used to generate the
R_AARCH64_TLS_DTPREL64 relocation, which is used in .debug_info sections
to describe the location of thread-local variables.

Prerequisite for  llvm#146572

(cherry picked from commit 60c1020)
@xgupta

xgupta commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

Send the back port request in #191021 which takes necessary parts of #155776

xgupta added a commit to xgupta/llvm-project that referenced this pull request Apr 9, 2026
This adds an implementation of getDebugThreadLocalSymbol for AArch64 by
using AArch::S_DTPREL.

Fixes llvm#83466
xgupta added a commit to xgupta/llvm-project that referenced this pull request Apr 9, 2026
…esolver.cpp (llvm#187649)

In patch llvm#146572 we have plan
to emit R_AARCH64_TLS_DTPREL64. This give us the warning while using
llvm-dwarfdump for the object file which has tls variables -

warning: failed to compute relocation: R_AARCH64_TLS_DTPREL64, Invalid
data was encountered while parsing the file

To fix this warning we have mark the relocation as supported however
final absolute address of a TLS variable is determined at runtime,
resolving to the symbol's section-relative offset in the object file is
mitigate the warning.

(cherry picked from commit fa136df)
xgupta added a commit to xgupta/llvm-project that referenced this pull request Apr 9, 2026
This patch adds support for the %dtprel relocation specifier in the
AArch64 assembler. This specifier is used to generate the
R_AARCH64_TLS_DTPREL64 relocation, which is used in .debug_info sections
to describe the location of thread-local variables.

Prerequisite for  llvm#146572

(cherry picked from commit 60c1020)
xgupta added a commit to xgupta/llvm-project that referenced this pull request Apr 9, 2026
…6599)

This patch adds support for the %dtprel relocation specifier in the
AArch64 assembler. This specifier is used to generate the
R_AARCH64_TLS_DTPREL64 relocation, which is used in .debug_info sections
to describe the location of thread-local variables.

Prerequisite for  llvm#146572

(cherry picked from commit 60c1020)
saagarjha pushed a commit to ahjragaas/binutils-gdb that referenced this pull request Apr 9, 2026
This patch allows R_AARCH64_TLS_DTPREL64 relocations in non-allocated
sections, which is required for DWARF debug information when using
Thread Local Storage. This matches the behavior in LLD.

Also a new syntax to parse dtprel operator use to describe tls
location in debug information. Please see the reference 3 below.

References:
  - llvm/llvm-project#146572
    [AArch64] Support TLS variables in debug info
  - llvm/llvm-project#183962
    [LLD][AArch64] Handle R_AARCH64_TLS_DTPREL64 in non-alloc sections
  - ARM-software/abi-aa#330
    [AAELF64] Allow R_AARCH64_TLS_DTPREL to be used statically.

bfd/
        * elfnn-aarch64.c (elfNN_aarch64_final_link_relocate): Handle
        BFD_RELOC_AARCH64_TLS_DTPREL.

gas/
	* config/tc-aarch64.c (s_aarch64_cons): Parse %dtprel(var) syntax.
	* testsuite/gas/aarch64/tls-debug.s: New test.
	* testsuite/gas/aarch64/tls-debug.d: Run the test.

ld/
	* testsuite/ld-aarch64/tls-debug.s: New test.
	* testsuite/ld-aarch64/tls-debug.d: Run the test.

Bug: https://sourceware.org/PR28351

Signed-off-by: Shivam Gupta <shivam98.tkg@gmail.com>
zwu-2025 pushed a commit to zwu-2025/llvm-project that referenced this pull request May 17, 2026
…lvm#183962)

Clang plan to emit R_AARCH64_TLS_DTPREL64 in .debug_info (see PR
llvm#146572). LLD currently fails to recognize this relocation.

This prevent the debugger from correctly locating TLS variables when
using the DWARF DW_OP_GNU_push_tls_address or DW_AT_location with DTPREL
offsets.

This patch adds support for R_AARCH64_TLS_DTPREL64, adds its mapping to
R_DTPREL.
zwu-2025 pushed a commit to zwu-2025/llvm-project that referenced this pull request May 17, 2026
This adds an implementation of getDebugThreadLocalSymbol for AArch64 by
using AArch::S_DTPREL.

Fixes llvm#83466
zwu-2025 pushed a commit to zwu-2025/llvm-project that referenced this pull request May 17, 2026
…esolver.cpp (llvm#187649)

In patch llvm#146572 we have plan
to emit R_AARCH64_TLS_DTPREL64. This give us the warning while using
llvm-dwarfdump for the object file which has tls variables -

warning: failed to compute relocation: R_AARCH64_TLS_DTPREL64, Invalid
data was encountered while parsing the file

To fix this warning we have mark the relocation as supported however
final absolute address of a TLS variable is determined at runtime,
resolving to the symbol's section-relative offset in the object file is
mitigate the warning.
markrvmurray pushed a commit to markrvmurray/llvm-mc6809 that referenced this pull request Jun 14, 2026
This patch adds support for the %dtprel relocation specifier in the
AArch64 assembler. This specifier is used to generate the
R_AARCH64_TLS_DTPREL64 relocation, which is used in .debug_info sections
to describe the location of thread-local variables.

Prerequisite for  llvm/llvm-project#146572
markrvmurray pushed a commit to markrvmurray/llvm-mc6809 that referenced this pull request Jun 14, 2026
…esolver.cpp (#187649)

In patch llvm/llvm-project#146572 we have plan
to emit R_AARCH64_TLS_DTPREL64. This give us the warning while using
llvm-dwarfdump for the object file which has tls variables -

warning: failed to compute relocation: R_AARCH64_TLS_DTPREL64, Invalid
data was encountered while parsing the file

To fix this warning we have mark the relocation as supported however
final absolute address of a TLS variable is determined at runtime,
resolving to the symbol's section-relative offset in the object file is
mitigate the warning.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Development

Successfully merging this pull request may close these issues.

DW_OP_form_tls_address debug info not outputted for aarch64

8 participants