PowerPC64 tls_get_addr_desc static support

Alan Modra amodra@gmail.com
Wed Jan 22 07:17:00 GMT 2020


This provides a linker generated __tls_get_addr_desc wrapper function
preserving registers around a __tls_get_addr call.  The idea being to
support __tls_get_addr_desc without requiring a glibc update.

bfd/
	* elf64-ppc.c (struct ppc_link_hash_table): Add tga_group.
	(ppc64_elf_archive_symbol_lookup): Extract __tls_get_addr_opt for
	__tls_get_addr_desc.
	(ppc64_elf_size_stubs): Add section for linker generated
	__tls_get_addr_desc wrapper function.  Loop at least once if
	generating this function.
	(emit_tga_desc, emit_tga_desc_eh_frame): New functions.
	(ppc64_elf_build_stubs): Generate __tls_get_addr_desc.
ld/
	* testsuite/ld-powerpc/tlsdesc3.d,
	* testsuite/ld-powerpc/tlsdesc3.wf,
	* testsuite/ld-powerpc/tlsdesc4.d,
	* testsuite/ld-powerpc/tlsdesc4.s,
	* testsuite/ld-powerpc/tlsdesc4.wf: New tests.
	* testsuite/ld-powerpc/powerpc.exp: Run them.

diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index 6b11a11018..b3f8f6b359 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -3182,6 +3182,7 @@ struct ppc_link_hash_table
   struct ppc_link_hash_entry *tls_get_addr_fd;
   struct ppc_link_hash_entry *tga_desc;
   struct ppc_link_hash_entry *tga_desc_fd;
+  struct map_stub *tga_group;
 
   /* The size of reliplt used by got entry relocs.  */
   bfd_size_type got_reli_size;
@@ -4150,6 +4151,11 @@ ppc64_elf_archive_symbol_lookup (bfd *abfd,
   memcpy (dot_name + 1, name, len + 1);
   h = _bfd_elf_archive_symbol_lookup (abfd, info, dot_name);
   bfd_release (abfd, dot_name);
+  if (h != NULL)
+    return h;
+
+  if (strcmp (name, "__tls_get_addr_opt") == 0)
+    h = _bfd_elf_archive_symbol_lookup (abfd, info, "__tls_get_addr_desc");
   return h;
 }
 
@@ -13088,6 +13094,40 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
   if (!group_sections (info, stub_group_size, stubs_always_before_branch))
     return FALSE;
 
+  htab->tga_group = NULL;
+  if (!htab->params->no_tls_get_addr_regsave
+      && htab->tga_desc_fd != NULL
+      && (htab->tga_desc_fd->elf.root.type == bfd_link_hash_undefined
+	  || htab->tga_desc_fd->elf.root.type == bfd_link_hash_undefweak)
+      && htab->tls_get_addr_fd != NULL
+      && is_static_defined (&htab->tls_get_addr_fd->elf))
+    {
+      asection *sym_sec, *code_sec, *stub_sec;
+      bfd_vma sym_value;
+      struct _opd_sec_data *opd;
+
+      sym_sec = htab->tls_get_addr_fd->elf.root.u.def.section;
+      sym_value = defined_sym_val (&htab->tls_get_addr_fd->elf);
+      code_sec = sym_sec;
+      opd = get_opd_info (sym_sec);
+      if (opd != NULL)
+	opd_entry_value (sym_sec, sym_value, &code_sec, NULL, FALSE);
+      htab->tga_group = htab->sec_info[code_sec->id].u.group;
+      stub_sec = (*htab->params->add_stub_section) (".tga_desc.stub",
+						    htab->tga_group->link_sec);
+      if (stub_sec == NULL)
+	return FALSE;
+      htab->tga_group->stub_sec = stub_sec;
+
+      htab->tga_desc_fd->elf.root.type = bfd_link_hash_defined;
+      htab->tga_desc_fd->elf.root.u.def.section = stub_sec;
+      htab->tga_desc_fd->elf.root.u.def.value = 0;
+      htab->tga_desc_fd->elf.type = STT_FUNC;
+      htab->tga_desc_fd->elf.def_regular = 1;
+      htab->tga_desc_fd->elf.non_elf = 0;
+      _bfd_elf_link_hash_hide_symbol (info, &htab->tga_desc_fd->elf, TRUE);
+    }
+
 #define STUB_SHRINK_ITER 20
   /* Loop until no stubs added.  After iteration 20 of this loop we may
      exit on a stub section shrinking.  This is to break out of a
@@ -13517,6 +13557,14 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
 	      stub_sec->flags &= ~SEC_RELOC;
 	    }
 	}
+      if (htab->tga_group != NULL)
+	{
+	  /* See emit_tga_desc and emit_tga_desc_eh_frame.  */
+	  htab->tga_group->eh_size
+	    = 1 + 2 + (htab->opd_abi != 0) + 3 + 8 * 2 + 3 + 8 + 3;
+	  htab->tga_group->lr_restore = 23 * 4;
+	  htab->tga_group->stub_sec->size = 24 * 4;
+	}
 
       if (htab->stub_iteration <= STUB_SHRINK_ITER
 	  || htab->brlt->rawsize < htab->brlt->size)
@@ -13580,7 +13628,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
 	      || (htab->stub_iteration > STUB_SHRINK_ITER
 		  && htab->brlt->rawsize > htab->brlt->size))
 	  && (htab->glink_eh_frame == NULL
-	      || htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size))
+	      || htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size)
+	  && (htab->tga_group == NULL
+	      || htab->stub_iteration > 1))
 	break;
 
       /* Ask the linker to do its stuff.  */
@@ -14086,6 +14136,74 @@ write_plt_relocs_for_local_syms (struct bfd_link_info *info)
   return TRUE;
 }
 
+/* Emit the static wrapper function preserving registers around a
+   __tls_get_addr_opt call.  */
+
+static bfd_boolean
+emit_tga_desc (struct ppc_link_hash_table *htab)
+{
+  asection *stub_sec = htab->tga_group->stub_sec;
+  unsigned int cfa_updt = 11 * 4;
+  bfd_byte *p;
+  bfd_vma to, from, delta;
+
+  BFD_ASSERT (htab->tga_desc_fd->elf.root.type == bfd_link_hash_defined
+	      && htab->tga_desc_fd->elf.root.u.def.section == stub_sec
+	      && htab->tga_desc_fd->elf.root.u.def.value == 0);
+  to = defined_sym_val (&htab->tls_get_addr_fd->elf);
+  from = defined_sym_val (&htab->tga_desc_fd->elf) + cfa_updt;
+  delta = to - from;
+  if (delta + (1 << 25) >= 1 << 26)
+    {
+      _bfd_error_handler (_("__tls_get_addr call offset overflow"));
+      htab->stub_error = TRUE;
+      return FALSE;
+    }
+
+  p = stub_sec->contents;
+  p = tls_get_addr_prologue (htab->elf.dynobj, p, htab);
+  bfd_put_32 (stub_sec->owner, B_DOT | 1 | (delta & 0x3fffffc), p);
+  p += 4;
+  p = tls_get_addr_epilogue (htab->elf.dynobj, p, htab);
+  return stub_sec->size == (bfd_size_type) (p - stub_sec->contents);
+}
+
+/* Emit eh_frame describing the static wrapper function.  */
+
+static bfd_byte *
+emit_tga_desc_eh_frame (struct ppc_link_hash_table *htab, bfd_byte *p)
+{
+  unsigned int cfa_updt = 11 * 4;
+  unsigned int i;
+
+  *p++ = DW_CFA_advance_loc + cfa_updt / 4;
+  *p++ = DW_CFA_def_cfa_offset;
+  if (htab->opd_abi)
+    {
+      *p++ = 128;
+      *p++ = 1;
+    }
+  else
+    *p++ = 96;
+  *p++ = DW_CFA_offset_extended_sf;
+  *p++ = 65;
+  *p++ = (-16 / 8) & 0x7f;
+  for (i = 4; i < 12; i++)
+    {
+      *p++ = DW_CFA_offset + i;
+      *p++ = (htab->opd_abi ? 13 : 12) - i;
+    }
+  *p++ = DW_CFA_advance_loc + 10;
+  *p++ = DW_CFA_def_cfa_offset;
+  *p++ = 0;
+  for (i = 4; i < 12; i++)
+    *p++ = DW_CFA_restore + i;
+  *p++ = DW_CFA_advance_loc + 2;
+  *p++ = DW_CFA_restore_extended;
+  *p++ = 65;
+  return p;
+}
+
 /* Build all the stubs associated with the current output file.
    The stubs are kept in a hash table attached to the main linker
    hash table.  This function is called via gldelf64ppc_finish.  */
@@ -14245,6 +14363,24 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
 	}
     }
 
+  if (htab->tga_group != NULL)
+    {
+      htab->tga_group->lr_restore = 23 * 4;
+      htab->tga_group->stub_sec->size = 24 * 4;
+      if (!emit_tga_desc (htab))
+	return FALSE;
+      if (htab->glink_eh_frame != NULL
+	  && htab->glink_eh_frame->size != 0)
+	{
+	  size_t align = 4;
+
+	  p = htab->glink_eh_frame->contents;
+	  p += (sizeof (glink_eh_frame_cie) + align - 1) & -align;
+	  p += 17;
+	  htab->tga_group->eh_size = emit_tga_desc_eh_frame (htab, p) - p;
+	}
+    }
+
   /* Build .glink global entry stubs, and PLT relocs for globals.  */
   elf_link_hash_traverse (&htab->elf, build_global_entry_stubs_and_plt, info);
 
diff --git a/ld/testsuite/ld-powerpc/powerpc.exp b/ld/testsuite/ld-powerpc/powerpc.exp
index d50846c806..94b2fac6fb 100644
--- a/ld/testsuite/ld-powerpc/powerpc.exp
+++ b/ld/testsuite/ld-powerpc/powerpc.exp
@@ -280,6 +280,12 @@ set ppc64elftests {
     {"TLSdesc2" "-melf64ppc -shared --hash-style=both --no-plt-localentry tmpdir/tlsdll.so" "" "-a64"  {tlsdesc.s}
      {{objdump -dr tlsdesc2.d} {readelf -wf tlsdesc2.wf}}
      "tlsdesc2"}
+    {"TLSdesc3" "-melf64ppc --no-tls-optimize tmpdir/tlsdll.o" "" "-a64"  {tlsdesc.s}
+     {{objdump -dr tlsdesc3.d} {readelf -wf tlsdesc3.wf}}
+     "tlsdesc3"}
+    {"TLSdesc4" "-melf64ppc --no-tls-optimize tmpdir/tlsdll.o" "" "-a64"  {tlsdesc4.s}
+     {{objdump -dr tlsdesc4.d} {readelf -wf tlsdesc4.wf}}
+     "tlsdesc4"}
     {"sym@tocbase" "-shared -melf64ppc" "" "-a64" {symtocbase-1.s symtocbase-2.s}
 	{{objdump -dj.data symtocbase.d}} "symtocbase.so"}
     {"TOC opt" "-melf64ppc" "" "-a64"  {tocopt.s}
diff --git a/ld/testsuite/ld-powerpc/tlsdesc3.d b/ld/testsuite/ld-powerpc/tlsdesc3.d
new file mode 100644
index 0000000000..360dcff37c
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/tlsdesc3.d
@@ -0,0 +1,38 @@
+
+.*:     file format .*
+
+Disassembly of section \.text:
+
+0+10000100 <__tls_get_addr_desc>:
+.*:	(7c 08 02 a6|a6 02 08 7c) 	mflr    r0
+.*:	(f8 01 00 10|10 00 01 f8) 	std     r0,16\(r1\)
+.*:	(f8 81 ff c0|c0 ff 81 f8) 	std     r4,-64\(r1\)
+.*:	(f8 a1 ff c8|c8 ff a1 f8) 	std     r5,-56\(r1\)
+.*:	(f8 c1 ff d0|d0 ff c1 f8) 	std     r6,-48\(r1\)
+.*:	(f8 e1 ff d8|d8 ff e1 f8) 	std     r7,-40\(r1\)
+.*:	(f9 01 ff e0|e0 ff 01 f9) 	std     r8,-32\(r1\)
+.*:	(f9 21 ff e8|e8 ff 21 f9) 	std     r9,-24\(r1\)
+.*:	(f9 41 ff f0|f0 ff 41 f9) 	std     r10,-16\(r1\)
+.*:	(f9 61 ff f8|f8 ff 61 f9) 	std     r11,-8\(r1\)
+.*:	(f8 21 ff a1|a1 ff 21 f8) 	stdu    r1,-96\(r1\)
+.*:	(48 00 00 35|35 00 00 48) 	bl      .* <__tls_get_addr>
+.*:	(e8 81 00 20|20 00 81 e8) 	ld      r4,32\(r1\)
+.*:	(e8 a1 00 28|28 00 a1 e8) 	ld      r5,40\(r1\)
+.*:	(e8 c1 00 30|30 00 c1 e8) 	ld      r6,48\(r1\)
+.*:	(e8 e1 00 38|38 00 e1 e8) 	ld      r7,56\(r1\)
+.*:	(e9 01 00 40|40 00 01 e9) 	ld      r8,64\(r1\)
+.*:	(e9 21 00 48|48 00 21 e9) 	ld      r9,72\(r1\)
+.*:	(e9 41 00 50|50 00 41 e9) 	ld      r10,80\(r1\)
+.*:	(e9 61 00 58|58 00 61 e9) 	ld      r11,88\(r1\)
+.*:	(38 21 00 60|60 00 21 38) 	addi    r1,r1,96
+.*:	(e8 01 00 10|10 00 01 e8) 	ld      r0,16\(r1\)
+.*:	(7c 08 03 a6|a6 03 08 7c) 	mtlr    r0
+.*:	(4e 80 00 20|20 00 80 4e) 	blr
+
+0+10000160 <__tls_get_addr>:
+.*:	(4e 80 00 20|20 00 80 4e) 	blr
+
+0+10000164 <_start>:
+.*:	(38 62 80 08|08 80 62 38) 	addi    r3,r2,-32760
+.*:	(4b ff ff 99|99 ff ff 4b) 	bl      .* <__tls_get_addr_desc>
+.*:	(60 00 00 00|00 00 00 60) 	nop
diff --git a/ld/testsuite/ld-powerpc/tlsdesc3.wf b/ld/testsuite/ld-powerpc/tlsdesc3.wf
new file mode 100644
index 0000000000..af5a6b494c
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/tlsdesc3.wf
@@ -0,0 +1,43 @@
+Contents of the \.eh_frame section:
+
+0+ 0+10 0+ CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 4
+  Data alignment factor: -8
+  Return address column: 65
+  Augmentation data:     1b
+  DW_CFA_def_cfa: r1 ofs 0
+
+0+14 0+34 0+18 FDE cie=0+ pc=0+10000100\.\.0+10000160
+  DW_CFA_advance_loc: 44 to 0+1000012c
+  DW_CFA_def_cfa_offset: 96
+  DW_CFA_offset_extended_sf: r65 at cfa\+16
+  DW_CFA_offset: r4 at cfa-64
+  DW_CFA_offset: r5 at cfa-56
+  DW_CFA_offset: r6 at cfa-48
+  DW_CFA_offset: r7 at cfa-40
+  DW_CFA_offset: r8 at cfa-32
+  DW_CFA_offset: r9 at cfa-24
+  DW_CFA_offset: r10 at cfa-16
+  DW_CFA_offset: r11 at cfa-8
+  DW_CFA_advance_loc: 40 to 0+10000154
+  DW_CFA_def_cfa_offset: 0
+  DW_CFA_restore: r4
+  DW_CFA_restore: r5
+  DW_CFA_restore: r6
+  DW_CFA_restore: r7
+  DW_CFA_restore: r8
+  DW_CFA_restore: r9
+  DW_CFA_restore: r10
+  DW_CFA_restore: r11
+  DW_CFA_advance_loc: 8 to 0+1000015c
+  DW_CFA_restore_extended: r65
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+4c 0+10 0+50 FDE cie=0+ pc=0+10000164\.\.0+10000170
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
diff --git a/ld/testsuite/ld-powerpc/tlsdesc4.d b/ld/testsuite/ld-powerpc/tlsdesc4.d
new file mode 100644
index 0000000000..eb162bb784
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/tlsdesc4.d
@@ -0,0 +1,46 @@
+
+.*:     file format .*
+
+Disassembly of section \.text:
+
+0+10000100 <__tls_get_addr_desc>:
+.*:	(7c 08 02 a6|a6 02 08 7c) 	mflr    r0
+.*:	(f8 01 00 10|10 00 01 f8) 	std     r0,16\(r1\)
+.*:	(f8 81 ff c0|c0 ff 81 f8) 	std     r4,-64\(r1\)
+.*:	(f8 a1 ff c8|c8 ff a1 f8) 	std     r5,-56\(r1\)
+.*:	(f8 c1 ff d0|d0 ff c1 f8) 	std     r6,-48\(r1\)
+.*:	(f8 e1 ff d8|d8 ff e1 f8) 	std     r7,-40\(r1\)
+.*:	(f9 01 ff e0|e0 ff 01 f9) 	std     r8,-32\(r1\)
+.*:	(f9 21 ff e8|e8 ff 21 f9) 	std     r9,-24\(r1\)
+.*:	(f9 41 ff f0|f0 ff 41 f9) 	std     r10,-16\(r1\)
+.*:	(f9 61 ff f8|f8 ff 61 f9) 	std     r11,-8\(r1\)
+.*:	(f8 21 ff a1|a1 ff 21 f8) 	stdu    r1,-96\(r1\)
+.*:	(48 00 00 35|35 00 00 48) 	bl      .* <__tls_get_addr>
+.*:	(e8 81 00 20|20 00 81 e8) 	ld      r4,32\(r1\)
+.*:	(e8 a1 00 28|28 00 a1 e8) 	ld      r5,40\(r1\)
+.*:	(e8 c1 00 30|30 00 c1 e8) 	ld      r6,48\(r1\)
+.*:	(e8 e1 00 38|38 00 e1 e8) 	ld      r7,56\(r1\)
+.*:	(e9 01 00 40|40 00 01 e9) 	ld      r8,64\(r1\)
+.*:	(e9 21 00 48|48 00 21 e9) 	ld      r9,72\(r1\)
+.*:	(e9 41 00 50|50 00 41 e9) 	ld      r10,80\(r1\)
+.*:	(e9 61 00 58|58 00 61 e9) 	ld      r11,88\(r1\)
+.*:	(38 21 00 60|60 00 21 38) 	addi    r1,r1,96
+.*:	(e8 01 00 10|10 00 01 e8) 	ld      r0,16\(r1\)
+.*:	(7c 08 03 a6|a6 03 08 7c) 	mtlr    r0
+.*:	(4e 80 00 20|20 00 80 4e) 	blr
+
+0+10000160 <__tls_get_addr>:
+.*:	(4e 80 00 20|20 00 80 4e) 	blr
+
+0+10000164 <_start>:
+.*:	(38 62 80 08|08 80 62 38) 	addi    r3,r2,-32760
+.*:	(4b ff ff 99|99 ff ff 4b) 	bl      .* <__tls_get_addr_desc>
+.*:	(60 00 00 00|00 00 00 60) 	nop
+	\.\.\.
+
+0+12000100 <.*\.long_branch\.__tls_get_addr_desc>:
+.*:	(4a 00 00 00|00 00 00 4a) 	b       .* <__tls_get_addr_desc>
+	\.\.\.
+.*:	(38 62 80 08|08 80 62 38) 	addi    r3,r2,-32760
+.*:	(4b ff ff dd|dd ff ff 4b) 	bl      .* <.*\.long_branch\.__tls_get_addr_desc>
+.*:	(60 00 00 00|00 00 00 60) 	nop
diff --git a/ld/testsuite/ld-powerpc/tlsdesc4.s b/ld/testsuite/ld-powerpc/tlsdesc4.s
new file mode 100644
index 0000000000..6dc3082724
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/tlsdesc4.s
@@ -0,0 +1,18 @@
+ .text
+ .globl _start
+_start:
+ .cfi_startproc
+ addi 3,2,gd@got@tlsgd
+ bl __tls_get_addr_desc(gd@tlsgd)
+ nop
+ .cfi_endproc
+
+ .section .text.pad1,"ax"
+ .space 0x1ffff90
+
+ .section .text.far,"ax"
+ .cfi_startproc
+ addi 3,2,gd@got@tlsgd
+ bl __tls_get_addr_desc(gd@tlsgd)
+ nop
+ .cfi_endproc
diff --git a/ld/testsuite/ld-powerpc/tlsdesc4.wf b/ld/testsuite/ld-powerpc/tlsdesc4.wf
new file mode 100644
index 0000000000..8d24979810
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/tlsdesc4.wf
@@ -0,0 +1,49 @@
+Contents of the \.eh_frame section:
+
+0+ 0+10 0+ CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 4
+  Data alignment factor: -8
+  Return address column: 65
+  Augmentation data:     1b
+  DW_CFA_def_cfa: r1 ofs 0
+
+0+14 0+34 0+18 FDE cie=0+ pc=0+10000100..0+10000160
+  DW_CFA_advance_loc: 44 to 0+1000012c
+  DW_CFA_def_cfa_offset: 96
+  DW_CFA_offset_extended_sf: r65 at cfa\+16
+  DW_CFA_offset: r4 at cfa-64
+  DW_CFA_offset: r5 at cfa-56
+  DW_CFA_offset: r6 at cfa-48
+  DW_CFA_offset: r7 at cfa-40
+  DW_CFA_offset: r8 at cfa-32
+  DW_CFA_offset: r9 at cfa-24
+  DW_CFA_offset: r10 at cfa-16
+  DW_CFA_offset: r11 at cfa-8
+  DW_CFA_advance_loc: 40 to 0+10000154
+  DW_CFA_def_cfa_offset: 0
+  DW_CFA_restore: r4
+  DW_CFA_restore: r5
+  DW_CFA_restore: r6
+  DW_CFA_restore: r7
+  DW_CFA_restore: r8
+  DW_CFA_restore: r9
+  DW_CFA_restore: r10
+  DW_CFA_restore: r11
+  DW_CFA_advance_loc: 8 to 0+1000015c
+  DW_CFA_restore_extended: r65
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+4c 0+10 0+50 FDE cie=0+ pc=0+10000164..0+10000170
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+60 0+10 0+64 FDE cie=0+ pc=0+12000120..0+1200012c
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+

-- 
Alan Modra
Australia Development Lab, IBM



More information about the Binutils mailing list