From abd242a90836b535b2f98be634e16c70012a3c5d Mon Sep 17 00:00:00 2001
From: "David S. Miller" <davem@redhat.com>
Date: Wed, 19 Oct 2011 00:32:25 +0000
Subject: Fix sparc TLS call relaxation when the delay slot sets up %o0.

bfd/

	PR binutils/13301
	* elfxx-sparc.c (sparc_elf_find_reloc_at_ofs): New function.
	(_bfd_sparc_elf_relocate_section): Always move the __tls_get_addr
	call delay slot instruction forward 4 bytes when performing
	relaxation.

gold/

	PR binutils/13301
	* sparc.cc (Target_sparc::Relocate::reloc_adjust_addr_): New
	member to track relocation locations that have moved during TLS
	reloc optimizations.
	(Target_sparc::Relocate::Relocate): Initialize to NULL.
	(Target_sparc::Relocate::relocate): Adjust view down by 4
	bytes if it matches reloc_adjust_addr_.
	(Target_sparc::Relocate::relocate_tls): Always move the
	__tls_get_addr call delay slot instruction forward 4 bytes when
	performing relaxation.

ld/testsuite/

	* ld-sparc/tlssunbin32.dd: Update for TLS call relaxation fix
	for PR 13301.
	* ld-sparc/tlssunbin64.dd: Likewise.
	* ld-sparc/tlssunpic32.dd: Likewise.
	* ld-sparc/tlssunpic64.dd: Likewise.
---
 bfd/ChangeLog     |  8 ++++++++
 bfd/elfxx-sparc.c | 36 +++++++++++++++++++++++++++++++++++-
 2 files changed, 43 insertions(+), 1 deletion(-)

(limited to 'bfd')

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 8f9b9be..3a0c0eb 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,11 @@
+2011-10-18  David S. Miller  <davem@davemloft.net>
+
+	PR binutils/13301
+	* elfxx-sparc.c (sparc_elf_find_reloc_at_ofs): New function.
+	(_bfd_sparc_elf_relocate_section): Always move the __tls_get_addr
+	call delay slot instruction forward 4 bytes when performing
+	relaxation.
+
 2011-10-17  Alan Modra  <amodra@gmail.com>
 
 	PR ld/12975
diff --git a/bfd/elfxx-sparc.c b/bfd/elfxx-sparc.c
index 438b7f5..9a15124 100644
--- a/bfd/elfxx-sparc.c
+++ b/bfd/elfxx-sparc.c
@@ -1830,6 +1830,20 @@ _bfd_sparc_elf_gc_mark_hook (asection *sec,
   return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
 }
 
+static Elf_Internal_Rela *
+sparc_elf_find_reloc_at_ofs (Elf_Internal_Rela *rel,
+			     Elf_Internal_Rela *relend,
+			     bfd_vma offset)
+{
+  while (rel < relend)
+    {
+      if (rel->r_offset == offset)
+	return rel;
+      rel++;
+    }
+  return NULL;
+}
+
 /* Update the got entry reference counts for the section being removed.  */
 bfd_boolean
 _bfd_sparc_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
@@ -3676,6 +3690,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 	  if (! info->shared
 	      || (r_type == R_SPARC_TLS_GD_CALL && tls_type == GOT_TLS_IE))
 	    {
+	      Elf_Internal_Rela *rel2;
 	      bfd_vma insn;
 
 	      if (!info->shared && (h == NULL || h->dynindx == -1))
@@ -3711,7 +3726,26 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 		  continue;
 		}
 
-	      bfd_put_32 (output_bfd, 0x9001c008, contents + rel->r_offset);
+	      /* We cannot just overwrite the delay slot instruction,
+		 as it might be what puts the %o0 argument to
+		 __tls_get_addr into place.  So we have to transpose
+		 the delay slot with the add we patch in.  */
+	      insn = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+	      bfd_put_32 (output_bfd, insn,
+			  contents + rel->r_offset);
+	      bfd_put_32 (output_bfd, 0x9001c008,
+			  contents + rel->r_offset + 4);
+
+	      rel2 = rel;
+	      while ((rel2 = sparc_elf_find_reloc_at_ofs (rel2 + 1, relend,
+							  rel->r_offset + 4))
+		     != NULL)
+		{
+		  /* If the instruction we moved has a relocation attached to
+		     it, adjust the offset so that it will apply to the correct
+		     instruction.  */
+		  rel2->r_offset -= 4;
+		}
 	      continue;
 	    }
 
-- 
cgit v1.1