Skip to content

[AArch64] Support TLS variables in debug info #146572

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

xgupta
Copy link
Contributor

@xgupta xgupta commented Jul 1, 2025

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

Fixes #83466

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

Fixes llvm#83466
@llvmbot
Copy link
Member

llvmbot commented Jul 1, 2025

@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
Copy link
Member

llvmbot commented Jul 1, 2025

@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
Copy link
Member

llvmbot commented Jul 1, 2025

@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
Copy link
Member

llvmbot commented Jul 1, 2025

@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
Copy link
Member

llvmbot commented Jul 1, 2025

@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
Copy link
Collaborator

smithp35 commented Jul 2, 2025

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
Copy link
Contributor Author

xgupta commented Jul 2, 2025

Thanks, we can wait for the ABI change.

Comment on lines 31 to 32
// AARCH64 ELF ABI does not define static relocation type for TLS offset
// within a module. Do not generate AT_location for TLS variables.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This comment should be removed.

@@ -0,0 +1,37 @@
; RUN: llc -O0 -mtriple=aarch64-linux-gnu -filetype=obj < %s \
; RUN: | llvm-objdump -r - | FileCheck %s
Copy link
Collaborator

Choose a reason for hiding this comment

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

indent the second line a bit so it's clear it's a continuation:

; RUN: <more spaces> | llvm...

; RUN: llc -O0 -mtriple=aarch64-linux-gnu -filetype=obj < %s \
; RUN: | llvm-objdump -r - | FileCheck %s

; CHECK: R_AARCH64_TLS_DTPREL64
Copy link
Collaborator

Choose a reason for hiding this comment

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

What does the full output look like? Because this check looks a bit non-specific. I know it's basically "do we produce this at all" but is there anything else we can check to make sure it is in fact for "var" not something else?

; 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
Copy link
Collaborator

Choose a reason for hiding this comment

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

indent the continuation

Copy link
Collaborator

@DavidSpickett DavidSpickett left a comment

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
Copy link
Collaborator

smithp35 commented Jul 2, 2025

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

DW_OP_form_tls_address debug info not outputted for aarch64
4 participants