This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: PATCH: Check x86-64 TLS transitions


On Wed, Aug 22, 2007 at 12:34:07PM -0700, H.J. Lu wrote:
> This patch moves all x86-64 TLS transition checks into a single
> function and issues an error if a transition fails. If there is no
> objection, I will make similar change for IA32 and add a few
> testcases.
> 
> 

I will check in this patch today.


H.J.
----
bfd/

2007-08-23  H.J. Lu  <hongjiu.lu@intel.com>

	* elf32-i386.c: Include "bfd_stdint.h".
	(elf_i386_rtype_to_howto): New function.
	(elf_i386_info_to_howto_rel): Use it.
	(x86_64_opcode16): New union type.
	(elf_i386_check_tls_transition): New function.
	(elf_i386_tls_transition): Updated to check transition and
	issue an error if a transition isn't supported.
	(elf_i386_check_relocs): Return FALSE if
	elf_i386_tls_transition returns FALSE.
	(elf_i386_gc_sweep_hook): Likewise.
	(elf_i386_relocate_section): Likewise. Remove BFD_ASSERT
	on TLS transitions.

	* elf64-x86-64.c: Include "bfd_stdint.h".
	(x86_64_opcode16): New union type.
	(x86_64_opcode32): Likewise.
	(elf64_x86_64_check_tls_transition): New function.
	(elf64_x86_64_tls_transition): Updated to check transition and
	issue an error if a transition isn't supported.
	(elf64_x86_64_check_relocs): Return FALSE if
	elf64_x86_64_tls_transition returns FALSE.
	(elf64_x86_64_gc_sweep_hook): Likewise.
	(elf64_x86_64_relocate_section): Likewise. Remove BFD_ASSERT
	on TLS transitions.

ld/testsuite/

2007-08-23  H.J. Lu  <hongjiu.lu@intel.com>

	* ld-i386/tlsbinpic.s: Add a new GD -> IE test.

	* ld-i386/tlsgd1.s: Add 2 new GD -> LE tests.

	* ld-i386/tlsbin.dd: Updated.
	* ld-i386/tlsbin.rd: Likewise.
	* ld-i386/tlsgd1.dd: Likewise.

--- binutils/bfd/elf32-i386.c.trans	2007-08-22 21:37:06.000000000 -0700
+++ binutils/bfd/elf32-i386.c	2007-08-23 06:22:54.000000000 -0700
@@ -25,6 +25,7 @@
 #include "libbfd.h"
 #include "elf-bfd.h"
 #include "elf-vxworks.h"
+#include "bfd_stdint.h"
 
 /* 386 uses REL relocations instead of RELA.  */
 #define USE_REL	1
@@ -345,12 +346,9 @@ elf_i386_reloc_name_lookup (bfd *abfd AT
   return NULL;
 }
 
-static void
-elf_i386_info_to_howto_rel (bfd *abfd ATTRIBUTE_UNUSED,
-			    arelent *cache_ptr,
-			    Elf_Internal_Rela *dst)
+static reloc_howto_type *
+elf_i386_rtype_to_howto (bfd *abfd, unsigned r_type)
 {
-  unsigned int r_type = ELF32_R_TYPE (dst->r_info);
   unsigned int indx;
 
   if ((indx = r_type) >= R_386_standard
@@ -365,7 +363,17 @@ elf_i386_info_to_howto_rel (bfd *abfd AT
 			     abfd, (int) r_type);
       indx = R_386_NONE;
     }
-  cache_ptr->howto = &elf_howto_table[indx];
+  BFD_ASSERT (elf_howto_table [indx].type == r_type);
+  return &elf_howto_table[indx];
+}
+
+static void
+elf_i386_info_to_howto_rel (bfd *abfd ATTRIBUTE_UNUSED,
+			    arelent *cache_ptr,
+			    Elf_Internal_Rela *dst)
+{
+  unsigned int r_type = ELF32_R_TYPE (dst->r_info);
+  cache_ptr->howto = elf_i386_rtype_to_howto (abfd, r_type);
 }
 
 /* Return whether a symbol name implies a local label.  The UnixWare
@@ -906,32 +914,318 @@ elf_i386_copy_indirect_symbol (struct bf
     _bfd_elf_link_hash_copy_indirect (info, dir, ind);
 }
 
-static int
-elf_i386_tls_transition (struct bfd_link_info *info, int r_type,
+typedef union 
+  {
+    unsigned char c[2];
+    uint16_t i;
+  }
+i386_opcode16;
+
+/* Return TRUE if the TLS access code sequence support transition
+   from R_TYPE.  */
+
+static bfd_boolean
+elf_i386_check_tls_transition (bfd *abfd, asection *sec,
+			       bfd_byte *contents,
+			       Elf_Internal_Shdr *symtab_hdr,
+			       struct elf_link_hash_entry **sym_hashes,
+			       unsigned int r_type,
+			       bfd_boolean check_gd_2_ie,
+			       const Elf_Internal_Rela *rel,
+			       const Elf_Internal_Rela *relend)
+{
+  unsigned int val, type;
+  unsigned long r_symndx;
+  struct elf_link_hash_entry *h;
+  bfd_vma offset;
+
+  /* Get the section contents.  */
+  if (contents == NULL)
+    {
+      if (elf_section_data (sec)->this_hdr.contents != NULL)
+	contents = elf_section_data (sec)->this_hdr.contents;
+      else
+	{
+	  /* FIXME: How to better handle error condition?  */
+	  if (!bfd_malloc_and_get_section (abfd, sec, &contents))
+	    return FALSE;
+
+	  /* Cache the section contents for elf_link_input_bfd.  */
+	  elf_section_data (sec)->this_hdr.contents = contents;
+	}
+    }
+
+  offset = rel->r_offset;
+  switch (r_type)
+    {
+    case R_386_TLS_GD:
+    case R_386_TLS_LDM:
+      if (offset < 2
+	  || (offset + 9) > sec->size
+	  || (rel + 1) >= relend)
+	return FALSE;
+
+      type = bfd_get_8 (abfd, contents + offset - 2);
+      if (r_type == R_386_TLS_GD)
+	{
+	  /* Check transition from LD access model.  Only
+		leal foo@tlsgd(,%reg,1), %eax; call ___tls_get_addr
+		leal foo@tlsgd(%reg), %eax; call ___tls_get_addr
+	     can transit to different access model.  */
+	  if (type != 0x8d && type != 0x04)
+	    return FALSE;
+
+	  val = bfd_get_8 (abfd, contents + offset - 1);
+	  if (type == 0x04)
+	    {
+	      /* leal foo@tlsgd(,%reg,1), %eax; call ___tls_get_addr */
+	      if (offset < 3)
+		return FALSE;
+
+	      if (bfd_get_8 (abfd, contents + offset - 3) != 0x8d)
+		return FALSE;
+
+	      if ((val & 0xc7) != 0x05 || val == (4 << 3))
+		return FALSE;
+	    }
+	  else
+	    {
+	      /* leal foo@tlsgd(%reg), %eax; call ___tls_get_addr  */
+	      if ((val & 0xf8) != 0x80 || (val & 7) == 4)
+		return FALSE;
+
+	      if (check_gd_2_ie)
+		{
+		  /* We need to turn
+			leal foo@tlsgd(%reg), %eax;
+			call ___tls_get_addr;
+			nop
+		     to
+			movl %gs:0, %eax;
+			subl $foo@gottpoff(%reg), %eax
+		     */
+		  if ((offset + 10) > sec->size)
+		      return FALSE;
+
+		  if (bfd_get_8 (abfd, contents + offset + 9) != 0x90)
+		    return FALSE;
+		}
+	    }
+	}
+      else
+	{
+	  /* Check transition from LD access model.  Only
+		leal foo@tlsgd(%reg), %eax; call ___tls_get_addr
+	     can transit to different access model.  */
+
+	  if (type != 0x8d)
+	    return FALSE;
+
+	  val = bfd_get_8 (abfd, contents + offset - 1);
+	  if ((val & 0xf8) != 0x80 || (val & 7) == 4)
+	    return FALSE;
+	}
+
+      if (bfd_get_8 (abfd, contents + offset + 4) != 0xe8)
+	return FALSE;
+
+      r_symndx = ELF32_R_SYM (rel[1].r_info);
+      if (r_symndx < symtab_hdr->sh_info)
+	return FALSE;
+
+      h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+      return (h != NULL
+	      && h->root.root.string != NULL
+	      && (ELF32_R_TYPE (rel[1].r_info) == R_386_PC32
+		  || ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32)
+	      && (strcmp (h->root.root.string, "___tls_get_addr") == 0));
+
+    case R_386_TLS_IE:
+      /* Check transition from IE access model:
+		movl foo@indntpoff(%rip), %eax
+		movl foo@indntpoff(%rip), %reg
+		addl foo@indntpoff(%rip), %reg
+       */
+
+      if (offset < 1 || (offset + 4) > sec->size)
+	return FALSE;
+
+      /* Check "movl foo@tpoff(%rip), %eax" first.  */
+      val = bfd_get_8 (abfd, contents + offset - 1);
+      if (val == 0xa1)
+	return TRUE;
+
+      if (offset < 2)
+	return FALSE;
+
+      /* Check movl|addl foo@tpoff(%rip), %reg.   */
+      type = bfd_get_8 (abfd, contents + offset - 2);
+      return ((type == 0x8b || type == 0x03)
+	      && (val & 0xc7) == 0x05);
+
+    case R_386_TLS_GOTIE:
+    case R_386_TLS_IE_32:
+      /* Check transition from {IE_32,GOTIE} access model:
+		subl foo@{tpoff,gontoff}(%reg1), %reg2
+		movl foo@{tpoff,gontoff}(%reg1), %reg2
+		addl foo@{tpoff,gontoff}(%reg1), %reg2
+       */
+
+      if (offset < 2 || (offset + 4) > sec->size)
+	return FALSE;
+
+      val = bfd_get_8 (abfd, contents + offset - 1);
+      if ((val & 0xc0) != 0x80 || (val & 7) == 4)
+	return FALSE;
+
+      type = bfd_get_8 (abfd, contents + offset - 2);
+      return type == 0x8b || type == 0x2b || type == 0x03;
+
+    case R_386_TLS_GOTDESC:
+      /* Check transition from GDesc access model:
+		leal x@tlsdesc(%ebx), %eax
+
+	 Make sure it's a leal adding ebx to a 32-bit offset
+	 into any register, although it's probably almost always
+	 going to be eax.  */
+
+      if (offset < 2 || (offset + 4) > sec->size)
+	return FALSE;
+
+      if (bfd_get_8 (abfd, contents + offset - 2) != 0x8d)
+	return FALSE;
+
+      val = bfd_get_8 (abfd, contents + offset - 1);
+      return (val & 0xc7) == 0x83;
+
+    case R_386_TLS_DESC_CALL:
+      /* Check transition from GDesc access model:
+		call *x@tlsdesc(%rax)
+       */
+      if (offset + 2 <= sec->size)
+	{
+	  /* Make sure that it's a call *x@tlsdesc(%rax).  */
+	  static i386_opcode16 call = { { 0xff, 0x10 } };
+	  return bfd_get_16 (abfd, contents + offset) == call.i;
+	}
+
+      return FALSE;
+
+    default:
+      abort ();
+    }
+}
+
+/* Return TRUE if the TLS access transition is OK or no transition
+   will be performed.  Update R_TYPE if there is a transition.  */
+
+static bfd_boolean
+elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
+			 asection *sec, bfd_byte *contents,
+			 Elf_Internal_Shdr *symtab_hdr,
+			 struct elf_link_hash_entry **sym_hashes,
+			 unsigned int *r_type, int tls_type,
+			 const Elf_Internal_Rela *rel,
+			 const Elf_Internal_Rela *relend,
 			 struct elf_link_hash_entry *h)
 {
-  if (info->shared)
-    return r_type;
+  unsigned int from_type = *r_type;
+  unsigned int to_type = from_type;
+  bfd_boolean check = TRUE, check_gd_2_ie = FALSE;
 
-  switch (r_type)
+  switch (from_type)
     {
     case R_386_TLS_GD:
     case R_386_TLS_GOTDESC:
     case R_386_TLS_DESC_CALL:
     case R_386_TLS_IE_32:
-      if (h == NULL)
-	return R_386_TLS_LE_32;
-      return R_386_TLS_IE_32;
     case R_386_TLS_IE:
     case R_386_TLS_GOTIE:
-      if (h == NULL)
-	return R_386_TLS_LE_32;
-      return r_type;
+      if (!info->shared)
+	{
+	  if (h == NULL)
+	    to_type = R_386_TLS_LE_32;
+	  else if (from_type != R_386_TLS_IE
+		   && from_type != R_386_TLS_GOTIE)
+	    to_type = R_386_TLS_IE_32;
+	}
+
+      /* When we are called from elf_i386_relocate_section, CONTENTS
+	 isn't NULL and there may be additional transitions based on
+	 TLS_TYPE.  */
+      if (contents != NULL)
+	{
+	  unsigned int new_to_type = to_type;
+
+	  if (!info->shared
+	      && h != NULL
+	      && h->dynindx == -1
+	      && (tls_type & GOT_TLS_IE))
+	    new_to_type = R_386_TLS_LE_32;
+
+	  if (to_type == R_386_TLS_GD
+	      || to_type == R_386_TLS_GOTDESC
+	      || to_type == R_386_TLS_DESC_CALL)
+	    {
+	      if (tls_type == GOT_TLS_IE_POS)
+		new_to_type = R_386_TLS_GOTIE;
+	      else if (tls_type & GOT_TLS_IE)
+		new_to_type = R_386_TLS_IE_32;
+	    }
+
+	  /* We checked the transition before when we were called from
+	     elf_i386_check_relocs.  We only want to check the new
+	     transition which hasn't been checked before.  */
+	  check = new_to_type != to_type;
+	  to_type = new_to_type;
+
+	  /* We only check transition to IE when we are called from
+	     elf_i386_relocate_section since GD->IE needs one more
+	     byte than GD->LE.  We may optimize GD->LE from GD->IE
+	     in elf_i386_relocate_section.  */
+	  if (to_type == R_386_TLS_IE_32)
+	    check_gd_2_ie = TRUE;
+	}
+
+      break;
+
     case R_386_TLS_LDM:
-      return R_386_TLS_LE_32;
+      if (!info->shared)
+	to_type = R_386_TLS_LE_32;
+      break;
+
+    default:
+      return TRUE;
     }
 
-  return r_type;
+  /* Return TRUE if there is no transition.  */
+  if (from_type == to_type)
+    return TRUE;
+
+  /* Check if the transition can be performed.  */
+  if (check
+      && ! elf_i386_check_tls_transition (abfd, sec, contents,
+					  symtab_hdr, sym_hashes,
+					  from_type, check_gd_2_ie,
+					  rel, relend))
+    {
+      const reloc_howto_type *from, *to;
+
+      from = elf_i386_rtype_to_howto (abfd, from_type);
+      to = elf_i386_rtype_to_howto (abfd, to_type);
+
+      (*_bfd_error_handler)
+	(_("%B: TLS transition from %s to %s against `%s' at 0x%lx "
+	   "in section `%A' failed"),
+	 abfd, sec, from->name, to->name,
+	 h ? h->root.root.string : "a local symbol",
+	 (unsigned long) rel->r_offset);
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
+    }
+
+  *r_type = to_type;
+  return TRUE;
 }
 
 /* Look through the relocs for a section during the first phase, and
@@ -988,7 +1282,11 @@ elf_i386_check_relocs (bfd *abfd,
 	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
 	}
 
-      r_type = elf_i386_tls_transition (info, r_type, h);
+      if (! elf_i386_tls_transition (info, abfd, sec, NULL,
+				     symtab_hdr, sym_hashes,
+				     &r_type, GOT_UNKNOWN,
+				     rel, rel_end, h)) 
+	return FALSE;
 
       switch (r_type)
 	{
@@ -1377,7 +1675,12 @@ elf_i386_gc_sweep_hook (bfd *abfd,
 	}
 
       r_type = ELF32_R_TYPE (rel->r_info);
-      r_type = elf_i386_tls_transition (info, r_type, h);
+      if (! elf_i386_tls_transition (info, abfd, sec, NULL,
+				     symtab_hdr, sym_hashes,
+				     &r_type, GOT_UNKNOWN,
+				     rel, relend, h)) 
+	return FALSE;
+
       switch (r_type)
 	{
 	case R_386_TLS_LDM:
@@ -2644,77 +2947,43 @@ elf_i386_relocate_section (bfd *output_b
 	case R_386_TLS_DESC_CALL:
 	case R_386_TLS_IE_32:
 	case R_386_TLS_GOTIE:
-	  r_type = elf_i386_tls_transition (info, r_type, h);
 	  tls_type = GOT_UNKNOWN;
 	  if (h == NULL && local_got_offsets)
 	    tls_type = elf_i386_local_got_tls_type (input_bfd) [r_symndx];
 	  else if (h != NULL)
-	    {
-	      tls_type = elf_i386_hash_entry(h)->tls_type;
-	      if (!info->shared && h->dynindx == -1 && (tls_type & GOT_TLS_IE))
-		r_type = R_386_TLS_LE_32;
-	    }
+	    tls_type = elf_i386_hash_entry(h)->tls_type;
 	  if (tls_type == GOT_TLS_IE)
 	    tls_type = GOT_TLS_IE_NEG;
-	  if (r_type == R_386_TLS_GD
-	      || r_type == R_386_TLS_GOTDESC
-	      || r_type == R_386_TLS_DESC_CALL)
-	    {
-	      if (tls_type == GOT_TLS_IE_POS)
-		r_type = R_386_TLS_GOTIE;
-	      else if (tls_type & GOT_TLS_IE)
-		r_type = R_386_TLS_IE_32;
-	    }
+
+	  if (! elf_i386_tls_transition (info, input_bfd,
+					 input_section, contents,
+					 symtab_hdr, sym_hashes,
+					 &r_type, tls_type, rel,
+					 relend, h))
+	    return FALSE;
 
 	  if (r_type == R_386_TLS_LE_32)
 	    {
 	      BFD_ASSERT (! unresolved_reloc);
 	      if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_GD)
 		{
-		  unsigned int val, type;
+		  unsigned int type;
 		  bfd_vma roff;
-		  unsigned long tls_r_symndx;
-		  struct elf_link_hash_entry *tls_h;
 
 		  /* GD->LE transition.  */
-		  BFD_ASSERT (rel->r_offset >= 2);
 		  type = bfd_get_8 (input_bfd, contents + rel->r_offset - 2);
-		  BFD_ASSERT (type == 0x8d || type == 0x04);
-		  BFD_ASSERT (rel->r_offset + 9 <= input_section->size);
-		  BFD_ASSERT (bfd_get_8 (input_bfd,
-					 contents + rel->r_offset + 4)
-			      == 0xe8);
-		  BFD_ASSERT (rel + 1 < relend);
-		  tls_r_symndx = ELF32_R_SYM (rel[1].r_info);
-		  BFD_ASSERT (tls_r_symndx >= symtab_hdr->sh_info);
-		  tls_h = sym_hashes[tls_r_symndx - symtab_hdr->sh_info];
-		  BFD_ASSERT (tls_h != NULL
-			      && tls_h->root.root.string != NULL
-			      && strcmp (tls_h->root.root.string,
-					 "___tls_get_addr") == 0);
-		  BFD_ASSERT ((! info->shared
-			       && ELF32_R_TYPE (rel[1].r_info) == R_386_PC32)
-			      || ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32);
 		  roff = rel->r_offset + 5;
-		  val = bfd_get_8 (input_bfd,
-				   contents + rel->r_offset - 1);
 		  if (type == 0x04)
 		    {
 		      /* leal foo(,%reg,1), %eax; call ___tls_get_addr
 			 Change it into:
 			 movl %gs:0, %eax; subl $foo@tpoff, %eax
 			 (6 byte form of subl).  */
-		      BFD_ASSERT (rel->r_offset >= 3);
-		      BFD_ASSERT (bfd_get_8 (input_bfd,
-					     contents + rel->r_offset - 3)
-				  == 0x8d);
-		      BFD_ASSERT ((val & 0xc7) == 0x05 && val != (4 << 3));
 		      memcpy (contents + rel->r_offset - 3,
 			      "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
 		    }
 		  else
 		    {
-		      BFD_ASSERT ((val & 0xf8) == 0x80 && (val & 7) != 4);
 		      if (rel->r_offset + 10 <= input_section->size
 			  && bfd_get_8 (input_bfd,
 					contents + rel->r_offset + 9) == 0x90)
@@ -2753,19 +3022,11 @@ elf_i386_relocate_section (bfd *output_b
 
 		     Registers other than %eax may be set up here.  */
 
-		  unsigned int val, type;
+		  unsigned int val;
 		  bfd_vma roff;
 
-		  /* First, make sure it's a leal adding ebx to a
-		     32-bit offset into any register, although it's
-		     probably almost always going to be eax.  */
 		  roff = rel->r_offset;
-		  BFD_ASSERT (roff >= 2);
-		  type = bfd_get_8 (input_bfd, contents + roff - 2);
-		  BFD_ASSERT (type == 0x8d);
 		  val = bfd_get_8 (input_bfd, contents + roff - 1);
-		  BFD_ASSERT ((val & 0xc7) == 0x83);
-		  BFD_ASSERT (roff + 4 <= input_section->size);
 
 		  /* Now modify the instruction as appropriate.  */
 		  /* aoliva FIXME: remove the above and xor the byte
@@ -2782,28 +3043,18 @@ elf_i386_relocate_section (bfd *output_b
 		     It's originally:
 		     call *(%eax)
 		     Turn it into:
-		     nop; nop  */
+		     xchg %ax,%ax  */
 
-		  unsigned int val, type;
 		  bfd_vma roff;
-
-		  /* First, make sure it's a call *(%eax).  */
+		  
 		  roff = rel->r_offset;
-		  BFD_ASSERT (roff + 2 <= input_section->size);
-		  type = bfd_get_8 (input_bfd, contents + roff);
-		  BFD_ASSERT (type == 0xff);
-		  val = bfd_get_8 (input_bfd, contents + roff + 1);
-		  BFD_ASSERT (val == 0x10);
-
-		  /* Now modify the instruction as appropriate.  Use
-		     xchg %ax,%ax instead of 2 nops.  */
 		  bfd_put_8 (output_bfd, 0x66, contents + roff);
 		  bfd_put_8 (output_bfd, 0x90, contents + roff + 1);
 		  continue;
 		}
 	      else if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_IE)
 		{
-		  unsigned int val, type;
+		  unsigned int val;
 
 		  /* IE->LE transition:
 		     Originally it can be one of:
@@ -2814,9 +3065,7 @@ elf_i386_relocate_section (bfd *output_b
 		     movl $foo, %eax
 		     movl $foo, %reg
 		     addl $foo, %reg.  */
-		  BFD_ASSERT (rel->r_offset >= 1);
 		  val = bfd_get_8 (input_bfd, contents + rel->r_offset - 1);
-		  BFD_ASSERT (rel->r_offset + 4 <= input_section->size);
 		  if (val == 0xa1)
 		    {
 		      /* movl foo, %eax.  */
@@ -2825,14 +3074,14 @@ elf_i386_relocate_section (bfd *output_b
 		    }
 		  else
 		    {
-		      BFD_ASSERT (rel->r_offset >= 2);
+		      unsigned int type;
+
 		      type = bfd_get_8 (input_bfd,
 					contents + rel->r_offset - 2);
 		      switch (type)
 			{
 			case 0x8b:
 			  /* movl */
-			  BFD_ASSERT ((val & 0xc7) == 0x05);
 			  bfd_put_8 (output_bfd, 0xc7,
 				     contents + rel->r_offset - 2);
 			  bfd_put_8 (output_bfd,
@@ -2841,7 +3090,6 @@ elf_i386_relocate_section (bfd *output_b
 			  break;
 			case 0x03:
 			  /* addl */
-			  BFD_ASSERT ((val & 0xc7) == 0x05);
 			  bfd_put_8 (output_bfd, 0x81,
 				     contents + rel->r_offset - 2);
 			  bfd_put_8 (output_bfd,
@@ -2870,11 +3118,8 @@ elf_i386_relocate_section (bfd *output_b
 		     subl $foo, %reg2
 		     movl $foo, %reg2 (6 byte form)
 		     addl $foo, %reg2.  */
-		  BFD_ASSERT (rel->r_offset >= 2);
 		  type = bfd_get_8 (input_bfd, contents + rel->r_offset - 2);
 		  val = bfd_get_8 (input_bfd, contents + rel->r_offset - 1);
-		  BFD_ASSERT (rel->r_offset + 4 <= input_section->size);
-		  BFD_ASSERT ((val & 0xc0) == 0x80 && (val & 7) != 4);
 		  if (type == 0x8b)
 		    {
 		      /* movl */
@@ -3075,14 +3320,7 @@ elf_i386_relocate_section (bfd *output_b
 	      bfd_vma roff;
 
 	      /* GD->IE transition.  */
-	      BFD_ASSERT (rel->r_offset >= 2);
 	      type = bfd_get_8 (input_bfd, contents + rel->r_offset - 2);
-	      BFD_ASSERT (type == 0x8d || type == 0x04);
-	      BFD_ASSERT (rel->r_offset + 9 <= input_section->size);
-	      BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset + 4)
-			  == 0xe8);
-	      BFD_ASSERT (rel + 1 < relend);
-	      BFD_ASSERT (ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32);
 	      roff = rel->r_offset - 3;
 	      val = bfd_get_8 (input_bfd, contents + rel->r_offset - 1);
 	      if (type == 0x04)
@@ -3090,11 +3328,6 @@ elf_i386_relocate_section (bfd *output_b
 		  /* leal foo(,%reg,1), %eax; call ___tls_get_addr
 		     Change it into:
 		     movl %gs:0, %eax; subl $foo@gottpoff(%reg), %eax.  */
-		  BFD_ASSERT (rel->r_offset >= 3);
-		  BFD_ASSERT (bfd_get_8 (input_bfd,
-					 contents + rel->r_offset - 3)
-			      == 0x8d);
-		  BFD_ASSERT ((val & 0xc7) == 0x05 && val != (4 << 3));
 		  val >>= 3;
 		}
 	      else
@@ -3102,11 +3335,6 @@ elf_i386_relocate_section (bfd *output_b
 		  /* leal foo(%reg), %eax; call ___tls_get_addr; nop
 		     Change it into:
 		     movl %gs:0, %eax; subl $foo@gottpoff(%reg), %eax.  */
-		  BFD_ASSERT (rel->r_offset + 10 <= input_section->size);
-		  BFD_ASSERT ((val & 0xf8) == 0x80 && (val & 7) != 4);
-		  BFD_ASSERT (bfd_get_8 (input_bfd,
-					 contents + rel->r_offset + 9)
-			      == 0x90);
 		  roff = rel->r_offset - 2;
 		}
 	      memcpy (contents + roff,
@@ -3136,25 +3364,18 @@ elf_i386_relocate_section (bfd *output_b
 		 leal x@tlsdesc(%ebx), %eax
 
 		 Change it to:
-		 movl x@gotntpoff(%ebx), %eax # before nop; nop
+		 movl x@gotntpoff(%ebx), %eax # before xchg %ax,%ax
 		 or:
 		 movl x@gottpoff(%ebx), %eax # before negl %eax
 
 		 Registers other than %eax may be set up here.  */
 
-	      unsigned int val, type;
 	      bfd_vma roff;
 
 	      /* First, make sure it's a leal adding ebx to a 32-bit
 		 offset into any register, although it's probably
 		 almost always going to be eax.  */
 	      roff = rel->r_offset;
-	      BFD_ASSERT (roff >= 2);
-	      type = bfd_get_8 (input_bfd, contents + roff - 2);
-	      BFD_ASSERT (type == 0x8d);
-	      val = bfd_get_8 (input_bfd, contents + roff - 1);
-	      BFD_ASSERT ((val & 0xc7) == 0x83);
-	      BFD_ASSERT (roff + 4 <= input_section->size);
 
 	      /* Now modify the instruction as appropriate.  */
 	      /* To turn a leal into a movl in the form we use it, it
@@ -3182,22 +3403,15 @@ elf_i386_relocate_section (bfd *output_b
 		 call *(%eax)
 
 		 Change it to:
-		 nop; nop
+		 xchg %ax,%ax
 		 or
 		 negl %eax
 		 depending on how we transformed the TLS_GOTDESC above.
 	      */
 
-	      unsigned int val, type;
 	      bfd_vma roff;
 
-	      /* First, make sure it's a call *(%eax).  */
 	      roff = rel->r_offset;
-	      BFD_ASSERT (roff + 2 <= input_section->size);
-	      type = bfd_get_8 (input_bfd, contents + roff);
-	      BFD_ASSERT (type == 0xff);
-	      val = bfd_get_8 (input_bfd, contents + roff + 1);
-	      BFD_ASSERT (val == 0x10);
 
 	      /* Now modify the instruction as appropriate.  */
 	      if (tls_type != GOT_TLS_IE_NEG)
@@ -3220,35 +3434,20 @@ elf_i386_relocate_section (bfd *output_b
 	  break;
 
 	case R_386_TLS_LDM:
-	  if (! info->shared)
-	    {
-	      unsigned int val;
-	      unsigned long tls_r_symndx;
-	      struct elf_link_hash_entry *tls_h;
+	  if (! elf_i386_tls_transition (info, input_bfd,
+					 input_section, contents,
+					 symtab_hdr, sym_hashes,
+					 &r_type, GOT_UNKNOWN, rel,
+					 relend, h))
+	    return FALSE;
 
+	  if (r_type != R_386_TLS_LDM)
+	    {
 	      /* LD->LE transition:
-		 Ensure it is:
 		 leal foo(%reg), %eax; call ___tls_get_addr.
 		 We change it into:
 		 movl %gs:0, %eax; nop; leal 0(%esi,1), %esi.  */
-	      BFD_ASSERT (rel->r_offset >= 2);
-	      BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset - 2)
-			  == 0x8d);
-	      val = bfd_get_8 (input_bfd, contents + rel->r_offset - 1);
-	      BFD_ASSERT ((val & 0xf8) == 0x80 && (val & 7) != 4);
-	      BFD_ASSERT (rel->r_offset + 9 <= input_section->size);
-	      BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset + 4)
-			  == 0xe8);
-	      BFD_ASSERT (rel + 1 < relend);
-	      tls_r_symndx = ELF32_R_SYM (rel[1].r_info);
-	      BFD_ASSERT (tls_r_symndx >= symtab_hdr->sh_info);
-	      tls_h = sym_hashes[tls_r_symndx - symtab_hdr->sh_info];
-	      BFD_ASSERT (tls_h != NULL
-			  && tls_h->root.root.string != NULL
-			  && strcmp (tls_h->root.root.string,
-				     "___tls_get_addr") == 0);
-	      BFD_ASSERT (ELF32_R_TYPE (rel[1].r_info) == R_386_PC32
-			  || ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32);
+	      BFD_ASSERT (r_type == R_386_TLS_LE_32);
 	      memcpy (contents + rel->r_offset - 2,
 		      "\x65\xa1\0\0\0\0\x90\x8d\x74\x26", 11);
 	      /* Skip R_386_PC32/R_386_PLT32.  */
--- binutils/bfd/elf64-x86-64.c.trans	2007-08-22 21:37:06.000000000 -0700
+++ binutils/bfd/elf64-x86-64.c	2007-08-22 22:35:32.000000000 -0700
@@ -25,6 +25,7 @@
 #include "bfdlink.h"
 #include "libbfd.h"
 #include "elf-bfd.h"
+#include "bfd_stdint.h"
 
 #include "elf/x86-64.h"
 
@@ -725,27 +726,262 @@ elf64_x86_64_elf_object_p (bfd *abfd)
   return TRUE;
 }
 
-static int
-elf64_x86_64_tls_transition (struct bfd_link_info *info, int r_type,
+typedef union
+  {
+    unsigned char c[2];
+    uint16_t i;
+  }
+x86_64_opcode16;
+
+typedef union
+  {
+    unsigned char c[4];
+    uint32_t i;
+  }
+x86_64_opcode32;
+
+/* Return TRUE if the TLS access code sequence support transition
+   from R_TYPE.  */
+
+static bfd_boolean
+elf64_x86_64_check_tls_transition (bfd *abfd, asection *sec,
+				   bfd_byte *contents,
+				   Elf_Internal_Shdr *symtab_hdr,
+				   struct elf_link_hash_entry **sym_hashes,
+				   unsigned int r_type,
+				   const Elf_Internal_Rela *rel,
+				   const Elf_Internal_Rela *relend)
+{
+  unsigned int val;
+  unsigned long r_symndx;
+  struct elf_link_hash_entry *h;
+  bfd_vma offset;
+
+  /* Get the section contents.  */
+  if (contents == NULL)
+    {
+      if (elf_section_data (sec)->this_hdr.contents != NULL)
+	contents = elf_section_data (sec)->this_hdr.contents;
+      else
+	{
+	  /* FIXME: How to better handle error condition?  */
+	  if (!bfd_malloc_and_get_section (abfd, sec, &contents))
+	    return FALSE;
+
+	  /* Cache the section contents for elf_link_input_bfd.  */
+	  elf_section_data (sec)->this_hdr.contents = contents;
+	}
+    }
+
+  offset = rel->r_offset;
+  switch (r_type)
+    {
+    case R_X86_64_TLSGD:
+    case R_X86_64_TLSLD:
+      if ((rel + 1) >= relend)
+	return FALSE;
+
+      if (r_type == R_X86_64_TLSGD)
+	{
+	  /* Check transition from GD access model.  Only
+		.byte 0x66; leaq foo@tlsgd(%rip), %rdi
+		.word 0x6666; rex64; call __tls_get_addr
+	     can transit to different access model.  */
+
+	  static x86_64_opcode32 leaq = { { 0x66, 0x48, 0x8d, 0x3d } },
+				 call = { { 0x66, 0x66, 0x48, 0xe8 } };
+	  if (offset < 4
+	      || (offset + 12) > sec->size
+	      || bfd_get_32 (abfd, contents + offset - 4) != leaq.i
+	      || bfd_get_32 (abfd, contents + offset + 4) != call.i)
+	    return FALSE;
+	}
+      else
+	{
+	  /* Check transition from LD access model.  Only
+		leaq foo@tlsld(%rip), %rdi;
+		call __tls_get_addr
+	     can transit to different access model.  */
+
+	  static x86_64_opcode32 ld = { { 0x48, 0x8d, 0x3d, 0xe8 } };
+	  x86_64_opcode32 op;
+
+	  if (offset < 3 || (offset + 9) > sec->size)
+	    return FALSE;
+
+	  op.i = bfd_get_32 (abfd, contents + offset - 3);
+	  op.c[3] = bfd_get_8 (abfd, contents + offset + 4);
+	  if (op.i != ld.i)
+	    return FALSE;
+	}
+
+      r_symndx = ELF64_R_SYM (rel[1].r_info);
+      if (r_symndx < symtab_hdr->sh_info)
+	return FALSE;
+
+      h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+      return (h != NULL
+	      && h->root.root.string != NULL
+	      && (ELF64_R_TYPE (rel[1].r_info) == R_X86_64_PC32
+		  || ELF64_R_TYPE (rel[1].r_info) == R_X86_64_PLT32)
+	      && (strcmp (h->root.root.string, "__tls_get_addr") == 0));
+
+    case R_X86_64_GOTTPOFF:
+      /* Check transition from IE access model:
+		movq foo@gottpoff(%rip), %reg
+		addq foo@gottpoff(%rip), %reg
+       */
+
+      if (offset < 3 || (offset + 4) > sec->size)
+	return FALSE;
+
+      val = bfd_get_8 (abfd, contents + offset - 3);
+      if (val != 0x48 && val != 0x4c)
+	return FALSE;
+
+      val = bfd_get_8 (abfd, contents + offset - 2);
+      if (val != 0x8b && val != 0x03)
+	return FALSE;
+
+      val = bfd_get_8 (abfd, contents + offset - 1);
+      return (val & 0xc7) == 5;
+
+    case R_X86_64_GOTPC32_TLSDESC:
+      /* Check transition from GDesc access model:
+		leaq x@tlsdesc(%rip), %rax
+
+	 Make sure it's a leaq adding rip to a 32-bit offset
+	 into any register, although it's probably almost always
+	 going to be rax.  */
+
+      if (offset < 3 || (offset + 4) > sec->size)
+	return FALSE;
+
+      val = bfd_get_8 (abfd, contents + offset - 3);
+      if ((val & 0xfb) != 0x48)
+	return FALSE;
+
+      if (bfd_get_8 (abfd, contents + offset - 2) != 0x8d)
+	return FALSE;
+
+      val = bfd_get_8 (abfd, contents + offset - 1);
+      return (val & 0xc7) == 0x05;
+
+    case R_X86_64_TLSDESC_CALL:
+      /* Check transition from GDesc access model:
+		call *x@tlsdesc(%rax)
+       */
+      if (offset + 2 <= sec->size)
+	{
+	  /* Make sure that it's a call *x@tlsdesc(%rax).  */
+	  static x86_64_opcode16 call = { { 0xff, 0x10 } };
+	  return bfd_get_16 (abfd, contents + offset) == call.i;
+	}
+
+      return FALSE;
+
+    default:
+      abort ();
+    }
+}
+
+/* Return TRUE if the TLS access transition is OK or no transition
+   will be performed.  Update R_TYPE if there is a transition.  */
+
+static bfd_boolean
+elf64_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd,
+			     asection *sec, bfd_byte *contents,
+			     Elf_Internal_Shdr *symtab_hdr,
+			     struct elf_link_hash_entry **sym_hashes,
+			     unsigned int *r_type, int tls_type,
+			     const Elf_Internal_Rela *rel,
+			     const Elf_Internal_Rela *relend,
 			     struct elf_link_hash_entry *h)
 {
-  if (info->shared)
-    return r_type;
+  unsigned int from_type = *r_type;
+  unsigned int to_type = from_type;
+  bfd_boolean check = TRUE;
 
-  switch (r_type)
+  switch (from_type)
     {
     case R_X86_64_TLSGD:
     case R_X86_64_GOTPC32_TLSDESC:
     case R_X86_64_TLSDESC_CALL:
     case R_X86_64_GOTTPOFF:
-      if (h == NULL)
-	return R_X86_64_TPOFF32;
-      return R_X86_64_GOTTPOFF;
+      if (!info->shared)
+	{
+	  if (h == NULL)
+	    to_type = R_X86_64_TPOFF32;
+	  else
+	    to_type = R_X86_64_GOTTPOFF;
+	}
+
+      /* When we are called from elf64_x86_64_relocate_section,
+	 CONTENTS isn't NULL and there may be additional transitions
+	 based on TLS_TYPE.  */
+      if (contents != NULL)
+	{
+	  unsigned int new_to_type = to_type;
+
+	  if (!info->shared
+	      && h != NULL
+	      && h->dynindx == -1
+	      && tls_type == GOT_TLS_IE)
+	    new_to_type = R_X86_64_TPOFF32;
+
+	  if (to_type == R_X86_64_TLSGD
+	      || to_type == R_X86_64_GOTPC32_TLSDESC
+	      || to_type == R_X86_64_TLSDESC_CALL)
+	    {
+	      if (tls_type == GOT_TLS_IE)
+		new_to_type = R_X86_64_GOTTPOFF;
+	    }
+
+	  /* We checked the transition before when we were called from
+	     elf64_x86_64_check_relocs.  We only want to check the new
+	     transition which hasn't been checked before.  */
+	  check = new_to_type != to_type && from_type == to_type;
+	  to_type = new_to_type;
+	}
+
+      break;
+
     case R_X86_64_TLSLD:
-      return R_X86_64_TPOFF32;
+      if (!info->shared)
+	to_type = R_X86_64_TPOFF32;
+      break;
+
+    default:
+      return TRUE;
+    }
+
+  /* Return TRUE if there is no transition.  */
+  if (from_type == to_type)
+    return TRUE;
+
+  /* Check if the transition can be performed.  */
+  if (check
+      && ! elf64_x86_64_check_tls_transition (abfd, sec, contents,
+					      symtab_hdr, sym_hashes,
+					      from_type, rel, relend))
+    {
+      const reloc_howto_type *from, *to;
+
+      from = elf64_x86_64_rtype_to_howto (abfd, from_type);
+      to = elf64_x86_64_rtype_to_howto (abfd, to_type);
+
+      (*_bfd_error_handler)
+	(_("%B: TLS transition from %s to %s against `%s' at 0x%lx "
+	   "in section `%A' failed"),
+	 abfd, sec, from->name, to->name,
+	 h ? h->root.root.string : "a local symbol",
+	 (unsigned long) rel->r_offset);
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
     }
 
-   return r_type;
+  *r_type = to_type;
+  return TRUE;
 }
 
 /* Look through the relocs for a section during the first phase, and
@@ -753,7 +989,8 @@ elf64_x86_64_tls_transition (struct bfd_
    linkage table, and dynamic reloc sections.  */
 
 static bfd_boolean
-elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec,
+elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
+			   asection *sec,
 			   const Elf_Internal_Rela *relocs)
 {
   struct elf64_x86_64_link_hash_table *htab;
@@ -799,7 +1036,12 @@ elf64_x86_64_check_relocs (bfd *abfd, st
 	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
 	}
 
-      r_type = elf64_x86_64_tls_transition (info, r_type, h);
+      if (! elf64_x86_64_tls_transition (info, abfd, sec, NULL,
+					 symtab_hdr, sym_hashes,
+					 &r_type, GOT_UNKNOWN,
+					 rel, rel_end, h))
+	return FALSE;
+
       switch (r_type)
 	{
 	case R_X86_64_TLSLD:
@@ -1186,7 +1428,8 @@ elf64_x86_64_gc_mark_hook (asection *sec
 
 static bfd_boolean
 elf64_x86_64_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
-			    asection *sec, const Elf_Internal_Rela *relocs)
+			    asection *sec,
+			    const Elf_Internal_Rela *relocs)
 {
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
@@ -1229,7 +1472,12 @@ elf64_x86_64_gc_sweep_hook (bfd *abfd, s
 	}
 
       r_type = ELF64_R_TYPE (rel->r_info);
-      r_type = elf64_x86_64_tls_transition (info, r_type, h);
+      if (! elf64_x86_64_tls_transition (info, abfd, sec, NULL,
+					 symtab_hdr, sym_hashes,
+					 &r_type, GOT_UNKNOWN,
+					 rel, relend, h))
+	return FALSE;
+
       switch (r_type)
 	{
 	case R_X86_64_TLSLD:
@@ -2522,67 +2770,38 @@ elf64_x86_64_relocate_section (bfd *outp
 	case R_X86_64_GOTPC32_TLSDESC:
 	case R_X86_64_TLSDESC_CALL:
 	case R_X86_64_GOTTPOFF:
-	  r_type = elf64_x86_64_tls_transition (info, r_type, h);
 	  tls_type = GOT_UNKNOWN;
 	  if (h == NULL && local_got_offsets)
 	    tls_type = elf64_x86_64_local_got_tls_type (input_bfd) [r_symndx];
 	  else if (h != NULL)
-	    {
-	      tls_type = elf64_x86_64_hash_entry (h)->tls_type;
-	      if (!info->shared && h->dynindx == -1 && tls_type == GOT_TLS_IE)
-		r_type = R_X86_64_TPOFF32;
-	    }
-	  if (r_type == R_X86_64_TLSGD
-	      || r_type == R_X86_64_GOTPC32_TLSDESC
-	      || r_type == R_X86_64_TLSDESC_CALL)
-	    {
-	      if (tls_type == GOT_TLS_IE)
-		r_type = R_X86_64_GOTTPOFF;
-	    }
+	    tls_type = elf64_x86_64_hash_entry (h)->tls_type;
+
+	  if (! elf64_x86_64_tls_transition (info, input_bfd,
+					     input_section, contents,
+					     symtab_hdr, sym_hashes,
+					     &r_type, tls_type, rel,
+					     relend, h))
+	      return FALSE;
 
 	  if (r_type == R_X86_64_TPOFF32)
 	    {
+	      bfd_vma roff = rel->r_offset;
+
 	      BFD_ASSERT (! unresolved_reloc);
+
 	      if (ELF64_R_TYPE (rel->r_info) == R_X86_64_TLSGD)
 		{
-		  unsigned int i;
-		  static unsigned char tlsgd[8]
-		    = { 0x66, 0x48, 0x8d, 0x3d, 0x66, 0x66, 0x48, 0xe8 };
-		  unsigned long tls_r_symndx;
-		  struct elf_link_hash_entry *tls_h;
-
 		  /* GD->LE transition.
 		     .byte 0x66; leaq foo@tlsgd(%rip), %rdi
 		     .word 0x6666; rex64; call __tls_get_addr
 		     Change it into:
 		     movq %fs:0, %rax
 		     leaq foo@tpoff(%rax), %rax */
-		  BFD_ASSERT (rel->r_offset >= 4);
-		  for (i = 0; i < 4; i++)
-		    BFD_ASSERT (bfd_get_8 (input_bfd,
-					   contents + rel->r_offset - 4 + i)
-				== tlsgd[i]);
-		  BFD_ASSERT (rel->r_offset + 12 <= input_section->size);
-		  for (i = 0; i < 4; i++)
-		    BFD_ASSERT (bfd_get_8 (input_bfd,
-					   contents + rel->r_offset + 4 + i)
-				== tlsgd[i+4]);
-		  BFD_ASSERT (rel + 1 < relend);
-		  tls_r_symndx = ELF64_R_SYM (rel[1].r_info);
-		  BFD_ASSERT (tls_r_symndx >= symtab_hdr->sh_info);
-		  tls_h = sym_hashes[tls_r_symndx - symtab_hdr->sh_info];
-		  BFD_ASSERT (tls_h != NULL
-			      && tls_h->root.root.string != NULL
-			      && strcmp (tls_h->root.root.string,
-					 "__tls_get_addr") == 0);
-		  BFD_ASSERT ((! info->shared
-			       && ELF64_R_TYPE (rel[1].r_info) == R_X86_64_PC32)
-			      || ELF64_R_TYPE (rel[1].r_info) == R_X86_64_PLT32);
-		  memcpy (contents + rel->r_offset - 4,
+		  memcpy (contents + roff - 4,
 			  "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x8d\x80\0\0\0",
 			  16);
 		  bfd_put_32 (output_bfd, tpoff (info, relocation),
-			      contents + rel->r_offset + 8);
+			      contents + roff + 8);
 		  /* Skip R_X86_64_PC32/R_X86_64_PLT32.  */
 		  rel++;
 		  continue;
@@ -2595,26 +2814,13 @@ elf64_x86_64_relocate_section (bfd *outp
 
 		     Change it to:
 		     movl $x@tpoff, %rax
-
-		     Registers other than %rax may be set up here.  */
+		   */
 
 		  unsigned int val, type, type2;
-		  bfd_vma roff;
 
-		  /* First, make sure it's a leaq adding rip to a
-		     32-bit offset into any register, although it's
-		     probably almost always going to be rax.  */
-		  roff = rel->r_offset;
-		  BFD_ASSERT (roff >= 3);
 		  type = bfd_get_8 (input_bfd, contents + roff - 3);
-		  BFD_ASSERT ((type & 0xfb) == 0x48);
 		  type2 = bfd_get_8 (input_bfd, contents + roff - 2);
-		  BFD_ASSERT (type2 == 0x8d);
 		  val = bfd_get_8 (input_bfd, contents + roff - 1);
-		  BFD_ASSERT ((val & 0xc7) == 0x05);
-		  BFD_ASSERT (roff + 4 <= input_section->size);
-
-		  /* Now modify the instruction as appropriate.  */
 		  bfd_put_8 (output_bfd, 0x48 | ((type >> 2) & 1),
 			     contents + roff - 3);
 		  bfd_put_8 (output_bfd, 0xc7, contents + roff - 2);
@@ -2630,29 +2836,13 @@ elf64_x86_64_relocate_section (bfd *outp
 		     It's originally:
 		     call *(%rax)
 		     Turn it into:
-		     nop; nop.  */
-
-		  unsigned int val, type;
-		  bfd_vma roff;
-
-		  /* First, make sure it's a call *(%rax).  */
-		  roff = rel->r_offset;
-		  BFD_ASSERT (roff + 2 <= input_section->size);
-		  type = bfd_get_8 (input_bfd, contents + roff);
-		  BFD_ASSERT (type == 0xff);
-		  val = bfd_get_8 (input_bfd, contents + roff + 1);
-		  BFD_ASSERT (val == 0x10);
-
-		  /* Now modify the instruction as appropriate.  Use
-		     xchg %ax,%ax instead of 2 nops.  */
+		     xchg %ax,%ax.  */
 		  bfd_put_8 (output_bfd, 0x66, contents + roff);
 		  bfd_put_8 (output_bfd, 0x90, contents + roff + 1);
 		  continue;
 		}
-	      else
+	      else if (ELF64_R_TYPE (rel->r_info) == R_X86_64_GOTTPOFF)
 		{
-		  unsigned int val, type, reg;
-
 		  /* IE->LE transition:
 		     Originally it can be one of:
 		     movq foo@gottpoff(%rip), %reg
@@ -2661,25 +2851,23 @@ elf64_x86_64_relocate_section (bfd *outp
 		     movq $foo, %reg
 		     leaq foo(%reg), %reg
 		     addq $foo, %reg.  */
-		  BFD_ASSERT (rel->r_offset >= 3);
-		  val = bfd_get_8 (input_bfd, contents + rel->r_offset - 3);
-		  BFD_ASSERT (val == 0x48 || val == 0x4c);
-		  type = bfd_get_8 (input_bfd, contents + rel->r_offset - 2);
-		  BFD_ASSERT (type == 0x8b || type == 0x03);
-		  reg = bfd_get_8 (input_bfd, contents + rel->r_offset - 1);
-		  BFD_ASSERT ((reg & 0xc7) == 5);
+
+		  unsigned int val, type, reg;
+
+		  val = bfd_get_8 (input_bfd, contents + roff - 3);
+		  type = bfd_get_8 (input_bfd, contents + roff - 2);
+		  reg = bfd_get_8 (input_bfd, contents + roff - 1);
 		  reg >>= 3;
-		  BFD_ASSERT (rel->r_offset + 4 <= input_section->size);
 		  if (type == 0x8b)
 		    {
 		      /* movq */
 		      if (val == 0x4c)
 			bfd_put_8 (output_bfd, 0x49,
-				   contents + rel->r_offset - 3);
+				   contents + roff - 3);
 		      bfd_put_8 (output_bfd, 0xc7,
-				 contents + rel->r_offset - 2);
+				 contents + roff - 2);
 		      bfd_put_8 (output_bfd, 0xc0 | reg,
-				 contents + rel->r_offset - 1);
+				 contents + roff - 1);
 		    }
 		  else if (reg == 4)
 		    {
@@ -2687,27 +2875,29 @@ elf64_x86_64_relocate_section (bfd *outp
 			 special  */
 		      if (val == 0x4c)
 			bfd_put_8 (output_bfd, 0x49,
-				   contents + rel->r_offset - 3);
+				   contents + roff - 3);
 		      bfd_put_8 (output_bfd, 0x81,
-				 contents + rel->r_offset - 2);
+				 contents + roff - 2);
 		      bfd_put_8 (output_bfd, 0xc0 | reg,
-				 contents + rel->r_offset - 1);
+				 contents + roff - 1);
 		    }
 		  else
 		    {
 		      /* addq -> leaq */
 		      if (val == 0x4c)
 			bfd_put_8 (output_bfd, 0x4d,
-				   contents + rel->r_offset - 3);
+				   contents + roff - 3);
 		      bfd_put_8 (output_bfd, 0x8d,
-				 contents + rel->r_offset - 2);
+				 contents + roff - 2);
 		      bfd_put_8 (output_bfd, 0x80 | reg | (reg << 3),
-				 contents + rel->r_offset - 1);
+				 contents + roff - 1);
 		    }
 		  bfd_put_32 (output_bfd, tpoff (info, relocation),
-			      contents + rel->r_offset);
+			      contents + roff);
 		  continue;
 		}
+	      else
+		BFD_ASSERT (FALSE);
 	    }
 
 	  if (htab->sgot == NULL)
@@ -2834,151 +3024,104 @@ elf64_x86_64_relocate_section (bfd *outp
 		  + htab->sgot->output_offset + off;
 	      unresolved_reloc = FALSE;
 	    }
-	  else if (ELF64_R_TYPE (rel->r_info) == R_X86_64_TLSGD)
-	    {
-	      unsigned int i;
-	      static unsigned char tlsgd[8]
-		= { 0x66, 0x48, 0x8d, 0x3d, 0x66, 0x66, 0x48, 0xe8 };
-
-	      /* GD->IE transition.
-		 .byte 0x66; leaq foo@tlsgd(%rip), %rdi
-		 .word 0x6666; rex64; call __tls_get_addr@plt
-		 Change it into:
-		 movq %fs:0, %rax
-		 addq foo@gottpoff(%rip), %rax */
-	      BFD_ASSERT (rel->r_offset >= 4);
-	      for (i = 0; i < 4; i++)
-		BFD_ASSERT (bfd_get_8 (input_bfd,
-				       contents + rel->r_offset - 4 + i)
-			    == tlsgd[i]);
-	      BFD_ASSERT (rel->r_offset + 12 <= input_section->size);
-	      for (i = 0; i < 4; i++)
-		BFD_ASSERT (bfd_get_8 (input_bfd,
-				       contents + rel->r_offset + 4 + i)
-			    == tlsgd[i+4]);
-	      BFD_ASSERT (rel + 1 < relend);
-	      BFD_ASSERT (ELF64_R_TYPE (rel[1].r_info) == R_X86_64_PLT32);
-	      memcpy (contents + rel->r_offset - 4,
-		      "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x03\x05\0\0\0",
-		      16);
-
-	      relocation = (htab->sgot->output_section->vma
-			    + htab->sgot->output_offset + off
-			    - rel->r_offset
-			    - input_section->output_section->vma
-			    - input_section->output_offset
-			    - 12);
-	      bfd_put_32 (output_bfd, relocation,
-			  contents + rel->r_offset + 8);
-	      /* Skip R_X86_64_PLT32.  */
-	      rel++;
-	      continue;
-	    }
-	  else if (ELF64_R_TYPE (rel->r_info) == R_X86_64_GOTPC32_TLSDESC)
+	  else
 	    {
-	      /* GDesc -> IE transition.
-		 It's originally something like:
-		 leaq x@tlsdesc(%rip), %rax
-
-		 Change it to:
-		 movq x@gottpoff(%rip), %rax # before nop; nop
-
-		 Registers other than %rax may be set up here.  */
-
-	      unsigned int val, type, type2;
-	      bfd_vma roff;
-
-	      /* First, make sure it's a leaq adding rip to a 32-bit
-		 offset into any register, although it's probably
-		 almost always going to be rax.  */
-	      roff = rel->r_offset;
-	      BFD_ASSERT (roff >= 3);
-	      type = bfd_get_8 (input_bfd, contents + roff - 3);
-	      BFD_ASSERT ((type & 0xfb) == 0x48);
-	      type2 = bfd_get_8 (input_bfd, contents + roff - 2);
-	      BFD_ASSERT (type2 == 0x8d);
-	      val = bfd_get_8 (input_bfd, contents + roff - 1);
-	      BFD_ASSERT ((val & 0xc7) == 0x05);
-	      BFD_ASSERT (roff + 4 <= input_section->size);
-
-	      /* Now modify the instruction as appropriate.  */
-	      /* To turn a leaq into a movq in the form we use it, it
-		 suffices to change the second byte from 0x8d to
-		 0x8b.  */
-	      bfd_put_8 (output_bfd, 0x8b, contents + roff - 2);
+	      bfd_vma roff = rel->r_offset;
 
-	      bfd_put_32 (output_bfd,
-			  htab->sgot->output_section->vma
-			  + htab->sgot->output_offset + off
-			  - rel->r_offset
-			  - input_section->output_section->vma
-			  - input_section->output_offset
-			  - 4,
-			  contents + roff);
-	      continue;
-	    }
-	  else if (ELF64_R_TYPE (rel->r_info) == R_X86_64_TLSDESC_CALL)
-	    {
-	      /* GDesc -> IE transition.
-		 It's originally:
-		 call *(%rax)
-
-		 Change it to:
-		 nop; nop.  */
-
-	      unsigned int val, type;
-	      bfd_vma roff;
-
-	      /* First, make sure it's a call *(%eax).  */
-	      roff = rel->r_offset;
-	      BFD_ASSERT (roff + 2 <= input_section->size);
-	      type = bfd_get_8 (input_bfd, contents + roff);
-	      BFD_ASSERT (type == 0xff);
-	      val = bfd_get_8 (input_bfd, contents + roff + 1);
-	      BFD_ASSERT (val == 0x10);
-
-	      /* Now modify the instruction as appropriate.  Use
-		 xchg %ax,%ax instead of 2 nops.  */
-	      bfd_put_8 (output_bfd, 0x66, contents + roff);
-	      bfd_put_8 (output_bfd, 0x90, contents + roff + 1);
+	      if (ELF64_R_TYPE (rel->r_info) == R_X86_64_TLSGD)
+		{
+		  /* GD->IE transition.
+		     .byte 0x66; leaq foo@tlsgd(%rip), %rdi
+		     .word 0x6666; rex64; call __tls_get_addr@plt
+		     Change it into:
+		     movq %fs:0, %rax
+		     addq foo@gottpoff(%rip), %rax */
+		  memcpy (contents + roff - 4,
+			  "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x03\x05\0\0\0",
+			  16);
 
-	      continue;
+		  relocation = (htab->sgot->output_section->vma
+				+ htab->sgot->output_offset + off
+				- roff
+				- input_section->output_section->vma
+				- input_section->output_offset
+				- 12);
+		  bfd_put_32 (output_bfd, relocation,
+			      contents + roff + 8);
+		  /* Skip R_X86_64_PLT32.  */
+		  rel++;
+		  continue;
+		}
+	      else if (ELF64_R_TYPE (rel->r_info) == R_X86_64_GOTPC32_TLSDESC)
+		{
+		  /* GDesc -> IE transition.
+		     It's originally something like:
+		     leaq x@tlsdesc(%rip), %rax
+
+		     Change it to:
+		     movq x@gottpoff(%rip), %rax # before xchg %ax,%ax
+		   */
+
+		  unsigned int val, type, type2;
+
+		  type = bfd_get_8 (input_bfd, contents + roff - 3);
+		  type2 = bfd_get_8 (input_bfd, contents + roff - 2);
+		  val = bfd_get_8 (input_bfd, contents + roff - 1);
+
+		  /* Now modify the instruction as appropriate. To
+		     turn a leaq into a movq in the form we use it, it
+		     suffices to change the second byte from 0x8d to
+		     0x8b.  */
+		  bfd_put_8 (output_bfd, 0x8b, contents + roff - 2);
+
+		  bfd_put_32 (output_bfd,
+			      htab->sgot->output_section->vma
+			      + htab->sgot->output_offset + off
+			      - rel->r_offset
+			      - input_section->output_section->vma
+			      - input_section->output_offset
+			      - 4,
+			      contents + roff);
+		  continue;
+		}
+	      else if (ELF64_R_TYPE (rel->r_info) == R_X86_64_TLSDESC_CALL)
+		{
+		  /* GDesc -> IE transition.
+		     It's originally:
+		     call *(%rax)
+
+		     Change it to:
+		     xchg %ax,%ax.  */
+
+		  unsigned int val, type;
+
+		  type = bfd_get_8 (input_bfd, contents + roff);
+		  val = bfd_get_8 (input_bfd, contents + roff + 1);
+		  bfd_put_8 (output_bfd, 0x66, contents + roff);
+		  bfd_put_8 (output_bfd, 0x90, contents + roff + 1);
+		  continue;
+		}
+	      else
+		BFD_ASSERT (FALSE);
 	    }
-	  else
-	    BFD_ASSERT (FALSE);
 	  break;
 
 	case R_X86_64_TLSLD:
-	  if (! info->shared)
-	    {
-	      unsigned long tls_r_symndx;
-	      struct elf_link_hash_entry *tls_h;
+	  if (! elf64_x86_64_tls_transition (info, input_bfd,
+					     input_section, contents,
+					     symtab_hdr, sym_hashes,
+					     &r_type, GOT_UNKNOWN,
+					     rel, relend, h))
+	    return FALSE;
 
+	  if (r_type != R_X86_64_TLSLD)
+	    {
 	      /* LD->LE transition:
-		 Ensure it is:
 		 leaq foo@tlsld(%rip), %rdi; call __tls_get_addr.
 		 We change it into:
 		 .word 0x6666; .byte 0x66; movl %fs:0, %rax.  */
-	      BFD_ASSERT (rel->r_offset >= 3);
-	      BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset - 3)
-			  == 0x48);
-	      BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset - 2)
-			  == 0x8d);
-	      BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset - 1)
-			  == 0x3d);
-	      BFD_ASSERT (rel->r_offset + 9 <= input_section->size);
-	      BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset + 4)
-			  == 0xe8);
-	      BFD_ASSERT (rel + 1 < relend);
-	      tls_r_symndx = ELF64_R_SYM (rel[1].r_info);
-	      BFD_ASSERT (tls_r_symndx >= symtab_hdr->sh_info);
-	      tls_h = sym_hashes[tls_r_symndx - symtab_hdr->sh_info];
-	      BFD_ASSERT (tls_h != NULL
-			  && tls_h->root.root.string != NULL
-			  && strcmp (tls_h->root.root.string,
-				     "__tls_get_addr") == 0);
-	      BFD_ASSERT (ELF64_R_TYPE (rel[1].r_info) == R_X86_64_PC32
-			  || ELF64_R_TYPE (rel[1].r_info) == R_X86_64_PLT32);
+
+	      BFD_ASSERT (r_type == R_X86_64_TPOFF32);
 	      memcpy (contents + rel->r_offset - 3,
 		      "\x66\x66\x66\x64\x48\x8b\x04\x25\0\0\0", 12);
 	      /* Skip R_X86_64_PC32/R_X86_64_PLT32.  */
--- binutils/ld/testsuite/ld-i386/tlsbin.dd.trans	2007-04-26 22:54:11.000000000 -0700
+++ binutils/ld/testsuite/ld-i386/tlsbin.dd	2007-08-23 06:55:08.000000000 -0700
@@ -222,235 +222,243 @@ Disassembly of section .text:
  8049170:	90[ 	]+nop *
  8049171:	90[ 	]+nop *
  8049172:	90[ 	]+nop *
- 8049173:	8b 5d fc[ 	]+mov    -0x4\(%ebp\),%ebx
- 8049176:	c9[ 	]+leave *
- 8049177:	c3[ 	]+ret *
+#  GD -> IE because variable is not defined in executable
+ 8049173:	65 a1 00 00 00 00[ 	]+mov    %gs:0x0,%eax
+ 8049179:	2b 83 f8 ff ff ff[ 	]+sub    -0x8\(%ebx\),%eax
+#				->R_386_TLS_TPOFF32	sG1
+ 804917f:	90[ 	]+nop *
+ 8049180:	90[ 	]+nop *
+ 8049181:	90[ 	]+nop *
+ 8049182:	90[ 	]+nop *
+ 8049183:	8b 5d fc[ 	]+mov    -0x4\(%ebp\),%ebx
+ 8049186:	c9[ 	]+leave *
+ 8049187:	c3[ 	]+ret *
 
-0+8049178 <_start>:
- 8049178:	55[ 	]+push   %ebp
- 8049179:	89 e5[ 	]+mov    %esp,%ebp
- 804917b:	e8 00 00 00 00[ 	]+call   8049180 <_start\+0x8>
- 8049180:	59[ 	]+pop    %ecx
- 8049181:	81 c1 a4 0f 00 00[ 	]+add    \$0xfa4,%ecx
- 8049187:	90[ 	]+nop *
- 8049188:	90[ 	]+nop *
- 8049189:	90[ 	]+nop *
- 804918a:	90[ 	]+nop *
+0+8049188 <_start>:
+ 8049188:	55[ 	]+push   %ebp
+ 8049189:	89 e5[ 	]+mov    %esp,%ebp
+ 804918b:	e8 00 00 00 00[ 	]+call   8049190 <_start\+0x8>
+ 8049190:	59[ 	]+pop    %ecx
+ 8049191:	81 c1 94 0f 00 00[ 	]+add    \$0xf94,%ecx
+ 8049197:	90[ 	]+nop *
+ 8049198:	90[ 	]+nop *
+ 8049199:	90[ 	]+nop *
+ 804919a:	90[ 	]+nop *
 #  @gottpoff IE against global var
- 804918b:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
- 8049192:	90[ 	]+nop *
- 8049193:	90[ 	]+nop *
- 8049194:	2b 91 f4 ff ff ff[ 	]+sub    -0xc\(%ecx\),%edx
+ 804919b:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
+ 80491a2:	90[ 	]+nop *
+ 80491a3:	90[ 	]+nop *
+ 80491a4:	2b 91 f4 ff ff ff[ 	]+sub    -0xc\(%ecx\),%edx
 #				->R_386_TLS_TPOFF32	sG6
- 804919a:	90[ 	]+nop *
- 804919b:	90[ 	]+nop *
- 804919c:	90[ 	]+nop *
- 804919d:	90[ 	]+nop *
-#  @indntpoff IE against global var
- 804919e:	65 a1 00 00 00 00[ 	]+mov    %gs:0x0,%eax
- 80491a4:	90[ 	]+nop *
- 80491a5:	90[ 	]+nop *
- 80491a6:	03 05 08 a1 04 08[ 	]+add    0x804a108,%eax
-#				->R_386_TLS_TPOFF	sG7
+ 80491aa:	90[ 	]+nop *
+ 80491ab:	90[ 	]+nop *
  80491ac:	90[ 	]+nop *
  80491ad:	90[ 	]+nop *
- 80491ae:	90[ 	]+nop *
- 80491af:	90[ 	]+nop *
-#  @indntpoff direct %gs access IE against global var
- 80491b0:	8b 15 20 a1 04 08[ 	]+mov    0x804a120,%edx
-#				->R_386_TLS_TPOFF	sG8
- 80491b6:	90[ 	]+nop *
- 80491b7:	90[ 	]+nop *
- 80491b8:	65 8b 02[ 	]+mov    %gs:\(%edx\),%eax
- 80491bb:	90[ 	]+nop *
+#  @indntpoff IE against global var
+ 80491ae:	65 a1 00 00 00 00[ 	]+mov    %gs:0x0,%eax
+ 80491b4:	90[ 	]+nop *
+ 80491b5:	90[ 	]+nop *
+ 80491b6:	03 05 08 a1 04 08[ 	]+add    0x804a108,%eax
+#				->R_386_TLS_TPOFF	sG7
  80491bc:	90[ 	]+nop *
  80491bd:	90[ 	]+nop *
  80491be:	90[ 	]+nop *
-#  @gottpoff IE -> LE against global var defined in exec
- 80491bf:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
+ 80491bf:	90[ 	]+nop *
+#  @indntpoff direct %gs access IE against global var
+ 80491c0:	8b 15 20 a1 04 08[ 	]+mov    0x804a120,%edx
+#				->R_386_TLS_TPOFF	sG8
  80491c6:	90[ 	]+nop *
  80491c7:	90[ 	]+nop *
- 80491c8:	81 ea 8c 0f 00 00[ 	]+sub    \$0xf8c,%edx
-#							bg6
+ 80491c8:	65 8b 02[ 	]+mov    %gs:\(%edx\),%eax
+ 80491cb:	90[ 	]+nop *
+ 80491cc:	90[ 	]+nop *
+ 80491cd:	90[ 	]+nop *
  80491ce:	90[ 	]+nop *
- 80491cf:	90[ 	]+nop *
- 80491d0:	90[ 	]+nop *
- 80491d1:	90[ 	]+nop *
-#  @indntpoff IE -> LE against global var defined in exec
- 80491d2:	65 a1 00 00 00 00[ 	]+mov    %gs:0x0,%eax
- 80491d8:	90[ 	]+nop *
- 80491d9:	90[ 	]+nop *
- 80491da:	81 c0 78 f0 ff ff[ 	]+add    \$0xfffff078,%eax
-#							bg7
+#  @gottpoff IE -> LE against global var defined in exec
+ 80491cf:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
+ 80491d6:	90[ 	]+nop *
+ 80491d7:	90[ 	]+nop *
+ 80491d8:	81 ea 8c 0f 00 00[ 	]+sub    \$0xf8c,%edx
+#							bg6
+ 80491de:	90[ 	]+nop *
+ 80491df:	90[ 	]+nop *
  80491e0:	90[ 	]+nop *
  80491e1:	90[ 	]+nop *
- 80491e2:	90[ 	]+nop *
- 80491e3:	90[ 	]+nop *
-#  @indntpoff direct %gs access IE -> LE against global var defined
-#  in exec
- 80491e4:	c7 c2 7c f0 ff ff[ 	]+mov    \$0xfffff07c,%edx
-#							bg8
- 80491ea:	90[ 	]+nop *
- 80491eb:	90[ 	]+nop *
- 80491ec:	65 8b 02[ 	]+mov    %gs:\(%edx\),%eax
- 80491ef:	90[ 	]+nop *
+#  @indntpoff IE -> LE against global var defined in exec
+ 80491e2:	65 a1 00 00 00 00[ 	]+mov    %gs:0x0,%eax
+ 80491e8:	90[ 	]+nop *
+ 80491e9:	90[ 	]+nop *
+ 80491ea:	81 c0 78 f0 ff ff[ 	]+add    \$0xfffff078,%eax
+#							bg7
  80491f0:	90[ 	]+nop *
  80491f1:	90[ 	]+nop *
  80491f2:	90[ 	]+nop *
-#  @gottpoff IE -> LE against local var
- 80491f3:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
+ 80491f3:	90[ 	]+nop *
+#  @indntpoff direct %gs access IE -> LE against global var defined
+#  in exec
+ 80491f4:	c7 c2 7c f0 ff ff[ 	]+mov    \$0xfffff07c,%edx
+#							bg8
  80491fa:	90[ 	]+nop *
  80491fb:	90[ 	]+nop *
- 80491fc:	81 ea 6c 0f 00 00[ 	]+sub    \$0xf6c,%edx
-#							bl6
+ 80491fc:	65 8b 02[ 	]+mov    %gs:\(%edx\),%eax
+ 80491ff:	90[ 	]+nop *
+ 8049200:	90[ 	]+nop *
+ 8049201:	90[ 	]+nop *
  8049202:	90[ 	]+nop *
- 8049203:	90[ 	]+nop *
- 8049204:	90[ 	]+nop *
- 8049205:	90[ 	]+nop *
-#  @indntpoff IE -> LE against local var
- 8049206:	65 a1 00 00 00 00[ 	]+mov    %gs:0x0,%eax
- 804920c:	90[ 	]+nop *
- 804920d:	90[ 	]+nop *
- 804920e:	81 c0 98 f0 ff ff[ 	]+add    \$0xfffff098,%eax
-#							bl7
+#  @gottpoff IE -> LE against local var
+ 8049203:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
+ 804920a:	90[ 	]+nop *
+ 804920b:	90[ 	]+nop *
+ 804920c:	81 ea 6c 0f 00 00[ 	]+sub    \$0xf6c,%edx
+#							bl6
+ 8049212:	90[ 	]+nop *
+ 8049213:	90[ 	]+nop *
  8049214:	90[ 	]+nop *
  8049215:	90[ 	]+nop *
- 8049216:	90[ 	]+nop *
- 8049217:	90[ 	]+nop *
-#  @indntpoff direct %gs access IE -> LE against local var
- 8049218:	c7 c2 9c f0 ff ff[ 	]+mov    \$0xfffff09c,%edx
-#							bl8
- 804921e:	90[ 	]+nop *
- 804921f:	90[ 	]+nop *
- 8049220:	65 8b 02[ 	]+mov    %gs:\(%edx\),%eax
- 8049223:	90[ 	]+nop *
+#  @indntpoff IE -> LE against local var
+ 8049216:	65 a1 00 00 00 00[ 	]+mov    %gs:0x0,%eax
+ 804921c:	90[ 	]+nop *
+ 804921d:	90[ 	]+nop *
+ 804921e:	81 c0 98 f0 ff ff[ 	]+add    \$0xfffff098,%eax
+#							bl7
  8049224:	90[ 	]+nop *
  8049225:	90[ 	]+nop *
  8049226:	90[ 	]+nop *
-#  @gottpoff IE -> LE against hidden but not local var
- 8049227:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
+ 8049227:	90[ 	]+nop *
+#  @indntpoff direct %gs access IE -> LE against local var
+ 8049228:	c7 c2 9c f0 ff ff[ 	]+mov    \$0xfffff09c,%edx
+#							bl8
  804922e:	90[ 	]+nop *
  804922f:	90[ 	]+nop *
- 8049230:	81 ea ac 0f 00 00[ 	]+sub    \$0xfac,%edx
-#							sh6
+ 8049230:	65 8b 02[ 	]+mov    %gs:\(%edx\),%eax
+ 8049233:	90[ 	]+nop *
+ 8049234:	90[ 	]+nop *
+ 8049235:	90[ 	]+nop *
  8049236:	90[ 	]+nop *
- 8049237:	90[ 	]+nop *
- 8049238:	90[ 	]+nop *
- 8049239:	90[ 	]+nop *
-#  @indntpoff IE -> LE against hidden but not local var
- 804923a:	65 a1 00 00 00 00[ 	]+mov    %gs:0x0,%eax
- 8049240:	90[ 	]+nop *
- 8049241:	90[ 	]+nop *
- 8049242:	81 c0 58 f0 ff ff[ 	]+add    \$0xfffff058,%eax
-#							sh7
+#  @gottpoff IE -> LE against hidden but not local var
+ 8049237:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
+ 804923e:	90[ 	]+nop *
+ 804923f:	90[ 	]+nop *
+ 8049240:	81 ea ac 0f 00 00[ 	]+sub    \$0xfac,%edx
+#							sh6
+ 8049246:	90[ 	]+nop *
+ 8049247:	90[ 	]+nop *
  8049248:	90[ 	]+nop *
  8049249:	90[ 	]+nop *
- 804924a:	90[ 	]+nop *
- 804924b:	90[ 	]+nop *
-#  @indntpoff direct %gs access IE -> LE against hidden but not
-#  local var
- 804924c:	c7 c2 5c f0 ff ff[ 	]+mov    \$0xfffff05c,%edx
-#							sh8
- 8049252:	90[ 	]+nop *
- 8049253:	90[ 	]+nop *
- 8049254:	65 8b 02[ 	]+mov    %gs:\(%edx\),%eax
- 8049257:	90[ 	]+nop *
+#  @indntpoff IE -> LE against hidden but not local var
+ 804924a:	65 a1 00 00 00 00[ 	]+mov    %gs:0x0,%eax
+ 8049250:	90[ 	]+nop *
+ 8049251:	90[ 	]+nop *
+ 8049252:	81 c0 58 f0 ff ff[ 	]+add    \$0xfffff058,%eax
+#							sh7
  8049258:	90[ 	]+nop *
  8049259:	90[ 	]+nop *
  804925a:	90[ 	]+nop *
-#  LE @tpoff, global var defined in exec
- 804925b:	ba 00 10 00 00[ 	]+mov    \$0x1000,%edx
-#							sg1
- 8049260:	90[ 	]+nop *
- 8049261:	90[ 	]+nop *
- 8049262:	65 a1 00 00 00 00[ 	]+mov    %gs:0x0,%eax
+ 804925b:	90[ 	]+nop *
+#  @indntpoff direct %gs access IE -> LE against hidden but not
+#  local var
+ 804925c:	c7 c2 5c f0 ff ff[ 	]+mov    \$0xfffff05c,%edx
+#							sh8
+ 8049262:	90[ 	]+nop *
+ 8049263:	90[ 	]+nop *
+ 8049264:	65 8b 02[ 	]+mov    %gs:\(%edx\),%eax
+ 8049267:	90[ 	]+nop *
  8049268:	90[ 	]+nop *
  8049269:	90[ 	]+nop *
- 804926a:	29 d0[ 	]+sub    %edx,%eax
- 804926c:	90[ 	]+nop *
- 804926d:	90[ 	]+nop *
- 804926e:	90[ 	]+nop *
- 804926f:	90[ 	]+nop *
-#  LE @tpoff, local var
- 8049270:	b8 7f 0f 00 00[ 	]+mov    \$0xf7f,%eax
-#							bl1+1
- 8049275:	90[ 	]+nop *
- 8049276:	90[ 	]+nop *
- 8049277:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
+ 804926a:	90[ 	]+nop *
+#  LE @tpoff, global var defined in exec
+ 804926b:	ba 00 10 00 00[ 	]+mov    \$0x1000,%edx
+#							sg1
+ 8049270:	90[ 	]+nop *
+ 8049271:	90[ 	]+nop *
+ 8049272:	65 a1 00 00 00 00[ 	]+mov    %gs:0x0,%eax
+ 8049278:	90[ 	]+nop *
+ 8049279:	90[ 	]+nop *
+ 804927a:	29 d0[ 	]+sub    %edx,%eax
+ 804927c:	90[ 	]+nop *
+ 804927d:	90[ 	]+nop *
  804927e:	90[ 	]+nop *
  804927f:	90[ 	]+nop *
- 8049280:	29 c2[ 	]+sub    %eax,%edx
- 8049282:	90[ 	]+nop *
- 8049283:	90[ 	]+nop *
- 8049284:	90[ 	]+nop *
+#  LE @tpoff, local var
+ 8049280:	b8 7f 0f 00 00[ 	]+mov    \$0xf7f,%eax
+#							bl1+1
  8049285:	90[ 	]+nop *
-#  LE @tpoff, hidden var defined in exec
- 8049286:	b8 bd 0f 00 00[ 	]+mov    \$0xfbd,%eax
-#							sh1+3
- 804928b:	90[ 	]+nop *
- 804928c:	90[ 	]+nop *
- 804928d:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
+ 8049286:	90[ 	]+nop *
+ 8049287:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
+ 804928e:	90[ 	]+nop *
+ 804928f:	90[ 	]+nop *
+ 8049290:	29 c2[ 	]+sub    %eax,%edx
+ 8049292:	90[ 	]+nop *
+ 8049293:	90[ 	]+nop *
  8049294:	90[ 	]+nop *
  8049295:	90[ 	]+nop *
- 8049296:	29 c2[ 	]+sub    %eax,%edx
- 8049298:	90[ 	]+nop *
- 8049299:	90[ 	]+nop *
- 804929a:	90[ 	]+nop *
+#  LE @tpoff, hidden var defined in exec
+ 8049296:	b8 bd 0f 00 00[ 	]+mov    \$0xfbd,%eax
+#							sh1+3
  804929b:	90[ 	]+nop *
-#  LE @ntpoff, global var defined in exec
- 804929c:	65 a1 00 00 00 00[ 	]+mov    %gs:0x0,%eax
- 80492a2:	90[ 	]+nop *
- 80492a3:	90[ 	]+nop *
- 80492a4:	8d 90 04 f0 ff ff[ 	]+lea    -0xffc\(%eax\),%edx
-#							sg2
+ 804929c:	90[ 	]+nop *
+ 804929d:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
+ 80492a4:	90[ 	]+nop *
+ 80492a5:	90[ 	]+nop *
+ 80492a6:	29 c2[ 	]+sub    %eax,%edx
+ 80492a8:	90[ 	]+nop *
+ 80492a9:	90[ 	]+nop *
  80492aa:	90[ 	]+nop *
  80492ab:	90[ 	]+nop *
- 80492ac:	90[ 	]+nop *
- 80492ad:	90[ 	]+nop *
-#  LE @ntpoff, local var, non-canonical sequence
- 80492ae:	b8 86 f0 ff ff[ 	]+mov    \$0xfffff086,%eax
-#							bl2+2
+#  LE @ntpoff, global var defined in exec
+ 80492ac:	65 a1 00 00 00 00[ 	]+mov    %gs:0x0,%eax
+ 80492b2:	90[ 	]+nop *
  80492b3:	90[ 	]+nop *
- 80492b4:	90[ 	]+nop *
- 80492b5:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
+ 80492b4:	8d 90 04 f0 ff ff[ 	]+lea    -0xffc\(%eax\),%edx
+#							sg2
+ 80492ba:	90[ 	]+nop *
+ 80492bb:	90[ 	]+nop *
  80492bc:	90[ 	]+nop *
  80492bd:	90[ 	]+nop *
- 80492be:	01 c2[ 	]+add    %eax,%edx
- 80492c0:	90[ 	]+nop *
- 80492c1:	90[ 	]+nop *
- 80492c2:	90[ 	]+nop *
+#  LE @ntpoff, local var, non-canonical sequence
+ 80492be:	b8 86 f0 ff ff[ 	]+mov    \$0xfffff086,%eax
+#							bl2+2
  80492c3:	90[ 	]+nop *
-#  LE @ntpoff, hidden var defined in exec, non-canonical sequence
- 80492c4:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
- 80492cb:	90[ 	]+nop *
+ 80492c4:	90[ 	]+nop *
+ 80492c5:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
  80492cc:	90[ 	]+nop *
- 80492cd:	81 c2 45 f0 ff ff[ 	]+add    \$0xfffff045,%edx
-#							sh2+1
+ 80492cd:	90[ 	]+nop *
+ 80492ce:	01 c2[ 	]+add    %eax,%edx
+ 80492d0:	90[ 	]+nop *
+ 80492d1:	90[ 	]+nop *
+ 80492d2:	90[ 	]+nop *
  80492d3:	90[ 	]+nop *
- 80492d4:	90[ 	]+nop *
- 80492d5:	90[ 	]+nop *
- 80492d6:	90[ 	]+nop *
+#  LE @ntpoff, hidden var defined in exec, non-canonical sequence
+ 80492d4:	65 8b 15 00 00 00 00[ 	]+mov    %gs:0x0,%edx
+ 80492db:	90[ 	]+nop *
+ 80492dc:	90[ 	]+nop *
+ 80492dd:	81 c2 45 f0 ff ff[ 	]+add    \$0xfffff045,%edx
+#							sh2+1
+ 80492e3:	90[ 	]+nop *
+ 80492e4:	90[ 	]+nop *
+ 80492e5:	90[ 	]+nop *
+ 80492e6:	90[ 	]+nop *
 #  LE @ntpoff, global var defined in exec
- 80492d7:	65 a1 08 f0 ff ff[ 	]+mov    %gs:0xfffff008,%eax
+ 80492e7:	65 a1 08 f0 ff ff[ 	]+mov    %gs:0xfffff008,%eax
 #							sg3
- 80492dd:	90[ 	]+nop *
- 80492de:	90[ 	]+nop *
- 80492df:	90[ 	]+nop *
- 80492e0:	90[ 	]+nop *
+ 80492ed:	90[ 	]+nop *
+ 80492ee:	90[ 	]+nop *
+ 80492ef:	90[ 	]+nop *
+ 80492f0:	90[ 	]+nop *
 #  LE @ntpoff, local var
- 80492e1:	65 8b 15 8b f0 ff ff[ 	]+mov    %gs:0xfffff08b,%edx
+ 80492f1:	65 8b 15 8b f0 ff ff[ 	]+mov    %gs:0xfffff08b,%edx
 #							bl3+3
- 80492e8:	90[ 	]+nop *
- 80492e9:	90[ 	]+nop *
- 80492ea:	90[ 	]+nop *
- 80492eb:	90[ 	]+nop *
+ 80492f8:	90[ 	]+nop *
+ 80492f9:	90[ 	]+nop *
+ 80492fa:	90[ 	]+nop *
+ 80492fb:	90[ 	]+nop *
 #  LE @ntpoff, hidden var defined in exec
- 80492ec:	65 8b 15 49 f0 ff ff[ 	]+mov    %gs:0xfffff049,%edx
+ 80492fc:	65 8b 15 49 f0 ff ff[ 	]+mov    %gs:0xfffff049,%edx
 #							sh3+1
- 80492f3:	90[ 	]+nop *
- 80492f4:	90[ 	]+nop *
- 80492f5:	90[ 	]+nop *
- 80492f6:	90[ 	]+nop *
- 80492f7:	8b 5d fc[ 	]+mov    -0x4\(%ebp\),%ebx
- 80492fa:	c9[ 	]+leave *
- 80492fb:	c3[ 	]+ret *
+ 8049303:	90[ 	]+nop *
+ 8049304:	90[ 	]+nop *
+ 8049305:	90[ 	]+nop *
+ 8049306:	90[ 	]+nop *
+ 8049307:	8b 5d fc[ 	]+mov    -0x4\(%ebp\),%ebx
+ 804930a:	c9[ 	]+leave *
+ 804930b:	c3[ 	]+ret *
--- binutils/ld/testsuite/ld-i386/tlsbin.rd.trans	2006-10-22 11:44:58.000000000 -0700
+++ binutils/ld/testsuite/ld-i386/tlsbin.rd	2007-08-23 06:30:23.000000000 -0700
@@ -32,7 +32,7 @@ Key to Flags:
 .*
 
 Elf file type is EXEC \(Executable file\)
-Entry point 0x8049178
+Entry point 0x8049188
 There are 6 program headers, starting at offset [0-9]+
 
 Program Headers:
@@ -137,7 +137,7 @@ Symbol table '.symtab' contains 70 entri
  +[0-9]+: 00000058 +0 TLS +GLOBAL HIDDEN +9 sh7
  +[0-9]+: 0000005c +0 TLS +GLOBAL HIDDEN +9 sh8
  +[0-9]+: 0+ +0 TLS +GLOBAL DEFAULT +9 sg1
- +[0-9]+: 0+8049178 +0 FUNC +GLOBAL DEFAULT +8 _start
+ +[0-9]+: 0+8049188 +0 FUNC +GLOBAL DEFAULT +8 _start
  +[0-9]+: 0000004c +0 TLS +GLOBAL HIDDEN +9 sh4
  +[0-9]+: 00000078 +0 TLS +GLOBAL DEFAULT +10 bg7
  +[0-9]+: 00000050 +0 TLS +GLOBAL HIDDEN +9 sh5
--- binutils/ld/testsuite/ld-i386/tlsbinpic.s.trans	2004-05-11 10:08:36.000000000 -0700
+++ binutils/ld/testsuite/ld-i386/tlsbinpic.s	2007-08-23 06:44:11.000000000 -0700
@@ -162,6 +162,11 @@ fn2:
 	movl	%gs:(%edx), %edx
 	nop;nop;nop;nop
 
+	/* GD -> IE because variable is not defined in executable */
+	leal	sG1@tlsgd(%ebx), %eax
+	call	___tls_get_addr@plt
+	nop;nop;nop;nop;nop
+
 	movl    -4(%ebp), %ebx
 	leave
 	ret
--- binutils/ld/testsuite/ld-i386/tlsgd1.dd.trans	2007-08-14 12:48:20.000000000 -0700
+++ binutils/ld/testsuite/ld-i386/tlsgd1.dd	2007-08-23 06:26:28.000000000 -0700
@@ -11,4 +11,8 @@ Disassembly of section .text:
 [a-f0-9]+ <_start>:
 [ 	]*[a-f0-9]+:	65 a1 00 00 00 00    	mov    %gs:0x0,%eax
 [ 	]*[a-f0-9]+:	81 e8 04 00 00 00    	sub    \$0x4,%eax
+[ 	]*[a-f0-9]+:	65 a1 00 00 00 00    	mov    %gs:0x0,%eax
+[ 	]*[a-f0-9]+:	2d 04 00 00 00       	sub    \$0x4,%eax
+[ 	]*[a-f0-9]+:	65 a1 00 00 00 00    	mov    %gs:0x0,%eax
+[ 	]*[a-f0-9]+:	81 e8 04 00 00 00    	sub    \$0x4,%eax
 #pass
--- binutils/ld/testsuite/ld-i386/tlsgd1.s.trans	2007-08-14 12:48:20.000000000 -0700
+++ binutils/ld/testsuite/ld-i386/tlsgd1.s	2007-08-23 06:16:55.000000000 -0700
@@ -3,6 +3,11 @@
 _start:
 	leal	foo@TLSGD(,%ebx,1), %eax
 	call	___tls_get_addr
+	leal	foo@TLSGD(%ebx), %eax
+	call	___tls_get_addr
+	leal	foo@TLSGD(%ebx), %eax
+	call	___tls_get_addr
+	nop
 	.globl foo
 	.section	.tdata,"awT",@progbits
 	.align 4


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]