[PATCH] aarch64: Add support for %dtprel(var) and R_AARCH64_TLS_DTPREL64

Shivam Gupta shivam98.tkg@gmail.com
Fri Mar 13 20:41:53 GMT 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:
  - https://github.com/llvm/llvm-project/pull/146572
    [AArch64] Support TLS variables in debug info
  - https://github.com/llvm/llvm-project/pull/183962
    [LLD][AArch64] Handle R_AARCH64_TLS_DTPREL64 in non-alloc sections
  - https://github.com/ARM-software/abi-aa/pull/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.

gold/
        * aarch64-reloc-property.h (code_to_array_index): Handle
        R_AARCH64_TLS_DTPREL64.
        * aarch64.cc (Relocate::relocate): Handle R_AARCH64_TLS_DTPREL64.

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

Signed-off-by: Shivam Gupta <shivam98.tkg@gmail.com>
---
 bfd/elfnn-aarch64.c                     | 12 +++++++
 gas/config/tc-aarch64.c                 | 44 +++++++++++++++++++++++++
 gas/testsuite/gas/aarch64/tls-debug.d   | 10 ++++++
 gas/testsuite/gas/aarch64/tls-debug.s   |  9 +++++
 gold/aarch64-reloc-property.h           |  2 ++
 gold/aarch64.cc                         |  8 ++++-
 ld/testsuite/ld-aarch64/aarch64-elf.exp |  1 +
 ld/testsuite/ld-aarch64/tls-debug.d     |  9 +++++
 ld/testsuite/ld-aarch64/tls-debug.s     |  9 +++++
 9 files changed, 103 insertions(+), 1 deletion(-)
 create mode 100644 gas/testsuite/gas/aarch64/tls-debug.d
 create mode 100644 gas/testsuite/gas/aarch64/tls-debug.s
 create mode 100644 ld/testsuite/ld-aarch64/tls-debug.d
 create mode 100644 ld/testsuite/ld-aarch64/tls-debug.s

diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
index 66049c81..acf207dd 100644
--- a/bfd/elfnn-aarch64.c
+++ b/bfd/elfnn-aarch64.c
@@ -6496,6 +6496,18 @@ elfNN_aarch64_final_link_relocate (reloc_howto_type *howto,
       *unresolved_reloc_p = false;
       break;
 
+    case BFD_RELOC_AARCH64_TLS_DTPREL:
+      /* Handle R_AARCH64_TLS_DTPREL64 in non-alloc sections  */
+      if (!(input_section->flags & SEC_ALLOC))
+	{
+	  value -= dtpoff_base (info);
+	  value += rel->r_addend;
+
+	  bfd_put_64 (output_bfd, value, contents + rel->r_offset);
+	}
+      *unresolved_reloc_p = false;
+      break;
+
     case BFD_RELOC_AARCH64_TLSDESC_OFF_G0_NC:
     case BFD_RELOC_AARCH64_TLSDESC_OFF_G1:
       if (globals->root.sgot == NULL)
diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
index cd761634..6cc7481a 100644
--- a/gas/config/tc-aarch64.c
+++ b/gas/config/tc-aarch64.c
@@ -2229,6 +2229,40 @@ s_aarch64_cons (int nbytes)
     {
       struct reloc_table_entry *reloc;
 
+      /* Check for %dtprel(var) syntax */
+      if (*input_line_pointer == '%')
+	{
+	  char *p = ++input_line_pointer; /* skip '%' */
+
+	  /* Look for "dtprel" */
+	  if (strncmp (p, "dtprel", 6) == 0 && p[6] == '(')
+	    {
+	      input_line_pointer = p + 7; /* Skip "dtprel(" */
+
+	      /* Parse the symbol inside the parentheses */
+	      expression (&exp);
+
+	      /* Ensure we have a closing parenthesis */
+	      if (*input_line_pointer == ')')
+		input_line_pointer++;
+	      else
+		as_bad (_("missing ')' after %%dtprel(...)"));
+
+	      /* Create the fixup using your DTPREL ID */
+	      addressT where = frag_now_fix ();
+	      fix_new_exp (frag_now, where, nbytes, &exp, 0,
+			   BFD_RELOC_AARCH64_TLS_DTPREL);
+
+	      char *dest = frag_more (nbytes);
+	      memset (dest, 0, nbytes);
+	      continue;
+	    }
+	  else
+	    {
+	      as_bad (_("unknown relocation operator %%%s"), p);
+	    }
+	}
+
       expression (&exp);
 
       if (exp.X_op != O_symbol)
@@ -3292,6 +3326,15 @@ static struct reloc_table_entry reloc_table[] =
    0,
    0},
 
+  /* TLS relative offset.  */
+  {"dtprel", 0,
+   0,        /* adr_type */
+   0,
+   0,
+   0,
+   0,
+   BFD_RELOC_AARCH64_TLS_DTPREL},
+
   /* Lower 16 bit offset into GOT entry for a symbol */
   {"tlsdesc_off_g0_nc", 0,
    0,				/* adr_type */
@@ -10114,6 +10157,7 @@ md_apply_fix (fixS * fixP, valueT * valP, segT seg)
     case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1:
     case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
     case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2:
+    case BFD_RELOC_AARCH64_TLS_DTPREL:
       S_SET_THREAD_LOCAL (fixP->fx_addsy);
       /* Should always be exported to object file, see
 	 aarch64_force_relocation().  */
diff --git a/gas/testsuite/gas/aarch64/tls-debug.d b/gas/testsuite/gas/aarch64/tls-debug.d
new file mode 100644
index 00000000..a64ff67f
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/tls-debug.d
@@ -0,0 +1,10 @@
+#as:
+#objdump: -r
+#target: aarch64*-*-*
+
+#...
+RELOCATION RECORDS FOR \[\.debug_info\]:
+OFFSET +TYPE +VALUE
+0+0 R_AARCH64_TLS_DTPREL64 +var
+0+8 R_AARCH64_TLS_DTPREL64 +var\+0x0+1
+#...
diff --git a/gas/testsuite/gas/aarch64/tls-debug.s b/gas/testsuite/gas/aarch64/tls-debug.s
new file mode 100644
index 00000000..9590ad40
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/tls-debug.s
@@ -0,0 +1,9 @@
+.section .tdata,"awT",@progbits
+.skip 8          // Force var to have an offset of 8
+.globl var
+var:
+  .word 0
+
+.section        .debug_info,"",@progbits
+  .xword  %dtprel(var)
+  .xword  %dtprel(var+1)
diff --git a/gold/aarch64-reloc-property.h b/gold/aarch64-reloc-property.h
index 986adf9d..c1ce0033 100644
--- a/gold/aarch64-reloc-property.h
+++ b/gold/aarch64-reloc-property.h
@@ -220,6 +220,8 @@ class AArch64_reloc_property_table
   code_to_array_index(unsigned int code) const
   {
     if (code == 0) return 0;
+    if (code == elfcpp::R_AARCH64_TLS_DTPREL64)
+      return 0;
     if (!((code >= elfcpp::R_AARCH64_ABS64 &&
 	   code <= elfcpp::R_AARCH64_LD64_GOTPAGE_LO15)
 	  || (code >= elfcpp::R_AARCH64_TLSGD_ADR_PREL21 &&
diff --git a/gold/aarch64.cc b/gold/aarch64.cc
index 9ef5df79..5575f78b 100644
--- a/gold/aarch64.cc
+++ b/gold/aarch64.cc
@@ -7330,13 +7330,19 @@ Target_aarch64<size, big_endian>::Relocate::relocate(
 				  gsym, psymval, view, address);
       break;
 
+    case elfcpp::R_AARCH64_TLS_DTPREL64:
+      {
+	uint64_t val = psymval->value (object, 0) + rela.get_r_addend ();
+	elfcpp::Swap<64, big_endian>::writeval (view, val);
+	break;
+      }
+
     // These are dynamic relocations, which are unexpected when linking.
     case elfcpp::R_AARCH64_COPY:
     case elfcpp::R_AARCH64_GLOB_DAT:
     case elfcpp::R_AARCH64_JUMP_SLOT:
     case elfcpp::R_AARCH64_RELATIVE:
     case elfcpp::R_AARCH64_IRELATIVE:
-    case elfcpp::R_AARCH64_TLS_DTPREL64:
     case elfcpp::R_AARCH64_TLS_DTPMOD64:
     case elfcpp::R_AARCH64_TLS_TPREL64:
     case elfcpp::R_AARCH64_TLSDESC:
diff --git a/ld/testsuite/ld-aarch64/aarch64-elf.exp b/ld/testsuite/ld-aarch64/aarch64-elf.exp
index a0a8867d..096f5f9f 100644
--- a/ld/testsuite/ld-aarch64/aarch64-elf.exp
+++ b/ld/testsuite/ld-aarch64/aarch64-elf.exp
@@ -252,6 +252,7 @@ run_dump_test "tls-relax-ld-le-small-ilp32"
 run_dump_test "tls-relax-ld-le-tiny"
 run_dump_test "tls-relax-ld-le-tiny-ilp32"
 run_dump_test "tls-desc-ie"
+run_dump_test "tls-debug"
 run_dump_test "tls-desc-ie-ilp32"
 run_dump_test "tls-relax-gdesc-le-2"
 run_dump_test "tls-relax-gdesc-le-2-ilp32"
diff --git a/ld/testsuite/ld-aarch64/tls-debug.d b/ld/testsuite/ld-aarch64/tls-debug.d
new file mode 100644
index 00000000..4344b02e
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/tls-debug.d
@@ -0,0 +1,9 @@
+#source: tls-debug.s
+#ld: -shared -T relocs.ld -e0
+#objdump: -s -j .debug_info
+
+#...
+Contents of section .debug_info:
+#...
+ [0-9a-f]+ 08000000 00000000 09000000 00000000 .*
+#...
diff --git a/ld/testsuite/ld-aarch64/tls-debug.s b/ld/testsuite/ld-aarch64/tls-debug.s
new file mode 100644
index 00000000..9590ad40
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/tls-debug.s
@@ -0,0 +1,9 @@
+.section .tdata,"awT",@progbits
+.skip 8          // Force var to have an offset of 8
+.globl var
+var:
+  .word 0
+
+.section        .debug_info,"",@progbits
+  .xword  %dtprel(var)
+  .xword  %dtprel(var+1)
-- 
2.34.1



More information about the Binutils mailing list