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]

[Xtensa] add TLS support


This patch adds TLS support for Xtensa. Committed on the trunk. GCC support should follow soon....

2008-08-20 Bob Wilson <bob.wilson@acm.org>

bfd/
        * elf-bfd.h (elf_object_id): Add XTENSA_ELF_TDATA.
        * elf32-xtensa.c (elf_howto_table): Add TLS relocations.
        (elf_xtensa_reloc_type_lookup): Likewise.
        (TCB_SIZE): Define.
        (elf_xtensa_link_hash_entry): New.
        (GOT_UNKNOWN, GOT_NORMAL, GOT_TLS_GD, GOT_TLS_IE, GOT_TLS_ANY): Define.
        (elf_xtensa_hash_entry): Define.
        (elf_xtensa_obj_tdata): New.
        (elf_xtensa_tdata): Define.
        (elf_xtensa_local_got_tls_type): Define.
        (elf_xtensa_local_tlsfunc_refcounts): Define.
        (is_xtensa_elf): Define.
        (elf_xtensa_mkobject): New.
        (elf_xtensa_link_hash_table): Add tlsbase field.
        (elf_xtensa_link_hash_newfunc): New.
        (elf_xtensa_link_hash_table_create): Use elf_xtensa_link_hash_newfunc.
        Create an entry for "_TLS_MODULE_BASE_" and save it in tlsbase field.
        (elf_xtensa_copy_indirect_symbol): New.
        (elf_xtensa_check_relocs): Rewrite to handle TLS relocations.
        (elf_xtensa_gc_sweep_hook): Likewise.
        (elf_xtensa_allocate_dynrelocs): Optimize away GOT entries for
        TLSDESC_FN relocations when an IE reference is seen.
        (elf_xtensa_allocate_local_got_size): Likewise.
        (elf_xtensa_always_size_sections): New.
        (dtpoff_base, tpoff): New.
        (elf_xtensa_do_reloc): Handle TLS relocations.
        (replace_tls_insn): New.
        (IS_XTENSA_TLS_RELOC): Define.
        (elf_xtensa_relocate_section): Handle TLS relocations.
        (get_indirect_call_dest_reg): New.
        (bfd_elf32_mkobject): Define.
        (elf_backend_always_size_sections): New.
        (elf_backend_copy_indirect_symbol): New.
        * reloc.c (BFD_RELOC_XTENSA_TLSDESC_FN, BFD_RELOC_XTENSA_TLSDESC_ARG)
        (BFD_RELOC_XTENSA_TLS_DTPOFF, BFD_RELOC_XTENSA_TLS_TPOFF)
        (BFD_RELOC_XTENSA_TLS_FUNC, BFD_RELOC_XTENSA_TLS_ARG)
        (BFD_RELOC_XTENSA_TLS_CALL): New.
        * bfd-in2.h: Regenerate.
        * libbfd.h: Regenerate.

gas/
        * config/tc-xtensa.c (O_tlsfunc, O_tlsarg, O_tlscall): Define.
        (O_tpoff, O_dtpoff): Define.
        (suffix_relocs): Add entries for TLS suffixes.
        (xtensa_elf_cons): Check for invalid use of TLS relocations.
        (map_operator_to_reloc): Add is_literal parameter and use it to
        control translating TLS instruction relocations to the corresponding
        literal relocations.
        (xg_valid_literal_expression): Allow TLS operators.
        (xg_build_to_insn): Copy TLS operators from pseudo-instruction
        operands to generated literals.
        (xg_assemble_literal): Handle TLS operators.  Update call to
        map_operator_to_reloc.
        (md_assemble): Handle CALLXn.TLS pseudo-instruction.
        (md_apply_fix): Handle TLS relocations.
        (emit_single_op): Handle TLS operators.
        (convert_frag_immed): Update call to map_operator_to_reloc.
        (vinsn_to_insnbuf): Emit relocations for TLS-related instructions.
        * config/xtensa-istack.h (tinsn_struct): Add tls_reloc field.
        * config/xtensa-relax.c (append_literal_op): Add src_op parameter
        to initialize the op_data field of the BuildOp.
        (build_transition): Use it here to record the source operand
        corresponding to a generated literal.
        * config/xtensa-relax.h (build_op): Comment op_data use for literals.

include/elf/
        * xtensa.h (R_XTENSA_TLSDESC_FN, R_XTENSA_TLSDESC_ARG)
        (R_XTENSA_TLS_DTPOFF, R_XTENSA_TLS_TPOFF, R_XTENSA_TLS_FUNC)
        (R_XTENSA_TLS_ARG, R_XTENSA_TLS_CALL): New.

ld/testsuite/
        * ld-xtensa/tlsbin.dd, ld-xtensa/tlsbin.rd, ld-xtensa/tlsbin.s,
        ld-xtensa/tlsbin.sd, ld-xtensa/tlsbin.td, ld-xtensa/tlslib.s,
        ld-xtensa/tlspic.dd, ld-xtensa/tlspic.rd, ld-xtensa/tlspic.sd,
        ld-xtensa/tlspic.td, ld-xtensa/tlspic1.s, ld-xtensa/tlspic2.s: New.
        * ld-xtensa/xtensa.exp: Run them.
Index: bfd/elf-bfd.h
===================================================================
RCS file: /cvs/src/src/bfd/elf-bfd.h,v
retrieving revision 1.266
diff -u -p -r1.266 elf-bfd.h
--- bfd/elf-bfd.h	7 Aug 2008 20:04:32 -0000	1.266
+++ bfd/elf-bfd.h	20 Aug 2008 23:14:34 -0000
@@ -1418,6 +1418,7 @@ enum elf_object_id
   SH_ELF_TDATA,
   SPARC_ELF_TDATA,
   X86_64_ELF_TDATA,
+  XTENSA_ELF_TDATA,
   GENERIC_ELF_TDATA
 };
 
Index: bfd/elf32-xtensa.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-xtensa.c,v
retrieving revision 1.110
diff -u -p -r1.110 elf32-xtensa.c
--- bfd/elf32-xtensa.c	7 May 2008 23:13:08 -0000	1.110
+++ bfd/elf32-xtensa.c	20 Aug 2008 23:14:38 -0000
@@ -288,6 +288,29 @@ static reloc_howto_type elf_howto_table[
 	 bfd_elf_xtensa_reloc, "R_XTENSA_SLOT13_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT14_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
 	 bfd_elf_xtensa_reloc, "R_XTENSA_SLOT14_ALT", FALSE, 0, 0, TRUE),
+
+  /* TLS relocations.  */
+  HOWTO (R_XTENSA_TLSDESC_FN, 0, 2, 32, FALSE, 0, complain_overflow_dont,
+	 bfd_elf_xtensa_reloc, "R_XTENSA_TLSDESC_FN",
+	 FALSE, 0, 0xffffffff, FALSE),
+  HOWTO (R_XTENSA_TLSDESC_ARG, 0, 2, 32, FALSE, 0, complain_overflow_dont,
+	 bfd_elf_xtensa_reloc, "R_XTENSA_TLSDESC_ARG",
+	 FALSE, 0, 0xffffffff, FALSE),
+  HOWTO (R_XTENSA_TLS_DTPOFF, 0, 2, 32, FALSE, 0, complain_overflow_dont,
+	 bfd_elf_xtensa_reloc, "R_XTENSA_TLS_DTPOFF",
+	 FALSE, 0, 0xffffffff, FALSE),
+  HOWTO (R_XTENSA_TLS_TPOFF, 0, 2, 32, FALSE, 0, complain_overflow_dont,
+	 bfd_elf_xtensa_reloc, "R_XTENSA_TLS_TPOFF",
+	 FALSE, 0, 0xffffffff, FALSE),
+  HOWTO (R_XTENSA_TLS_FUNC, 0, 0, 0, FALSE, 0, complain_overflow_dont,
+	 bfd_elf_xtensa_reloc, "R_XTENSA_TLS_FUNC",
+	 FALSE, 0, 0, FALSE),
+  HOWTO (R_XTENSA_TLS_ARG, 0, 0, 0, FALSE, 0, complain_overflow_dont,
+	 bfd_elf_xtensa_reloc, "R_XTENSA_TLS_ARG",
+	 FALSE, 0, 0, FALSE),
+  HOWTO (R_XTENSA_TLS_CALL, 0, 0, 0, FALSE, 0, complain_overflow_dont,
+	 bfd_elf_xtensa_reloc, "R_XTENSA_TLS_CALL",
+	 FALSE, 0, 0, FALSE),
 };
 
 #if DEBUG_GEN_RELOC
@@ -375,6 +398,34 @@ elf_xtensa_reloc_type_lookup (bfd *abfd 
       TRACE ("BFD_RELOC_VTABLE_ENTRY");
       return &elf_howto_table[(unsigned) R_XTENSA_GNU_VTENTRY ];
 
+    case BFD_RELOC_XTENSA_TLSDESC_FN:
+      TRACE ("BFD_RELOC_XTENSA_TLSDESC_FN");
+      return &elf_howto_table[(unsigned) R_XTENSA_TLSDESC_FN ];
+
+    case BFD_RELOC_XTENSA_TLSDESC_ARG:
+      TRACE ("BFD_RELOC_XTENSA_TLSDESC_ARG");
+      return &elf_howto_table[(unsigned) R_XTENSA_TLSDESC_ARG ];
+
+    case BFD_RELOC_XTENSA_TLS_DTPOFF:
+      TRACE ("BFD_RELOC_XTENSA_TLS_DTPOFF");
+      return &elf_howto_table[(unsigned) R_XTENSA_TLS_DTPOFF ];
+
+    case BFD_RELOC_XTENSA_TLS_TPOFF:
+      TRACE ("BFD_RELOC_XTENSA_TLS_TPOFF");
+      return &elf_howto_table[(unsigned) R_XTENSA_TLS_TPOFF ];
+
+    case BFD_RELOC_XTENSA_TLS_FUNC:
+      TRACE ("BFD_RELOC_XTENSA_TLS_FUNC");
+      return &elf_howto_table[(unsigned) R_XTENSA_TLS_FUNC ];
+
+    case BFD_RELOC_XTENSA_TLS_ARG:
+      TRACE ("BFD_RELOC_XTENSA_TLS_ARG");
+      return &elf_howto_table[(unsigned) R_XTENSA_TLS_ARG ];
+
+    case BFD_RELOC_XTENSA_TLS_CALL:
+      TRACE ("BFD_RELOC_XTENSA_TLS_CALL");
+      return &elf_howto_table[(unsigned) R_XTENSA_TLS_CALL ];
+
     default:
       if (code >= BFD_RELOC_XTENSA_SLOT0_OP
 	  && code <= BFD_RELOC_XTENSA_SLOT14_OP)
@@ -479,6 +530,56 @@ static const bfd_byte elf_xtensa_le_plt_
   0			/* unused */
 };
 
+/* The size of the thread control block.  */
+#define TCB_SIZE	8
+
+struct elf_xtensa_link_hash_entry
+{
+  struct elf_link_hash_entry elf;
+
+  bfd_signed_vma tlsfunc_refcount;
+
+#define GOT_UNKNOWN	0
+#define GOT_NORMAL	1
+#define GOT_TLS_GD	2	/* global or local dynamic */
+#define GOT_TLS_IE	4	/* initial or local exec */
+#define GOT_TLS_ANY	(GOT_TLS_GD | GOT_TLS_IE)
+  unsigned char tls_type;
+};
+
+#define elf_xtensa_hash_entry(ent) ((struct elf_xtensa_link_hash_entry *)(ent))
+
+struct elf_xtensa_obj_tdata
+{
+  struct elf_obj_tdata root;
+
+  /* tls_type for each local got entry.  */
+  char *local_got_tls_type;
+
+  bfd_signed_vma *local_tlsfunc_refcounts;
+};
+
+#define elf_xtensa_tdata(abfd) \
+  ((struct elf_xtensa_obj_tdata *) (abfd)->tdata.any)
+
+#define elf_xtensa_local_got_tls_type(abfd) \
+  (elf_xtensa_tdata (abfd)->local_got_tls_type)
+
+#define elf_xtensa_local_tlsfunc_refcounts(abfd) \
+  (elf_xtensa_tdata (abfd)->local_tlsfunc_refcounts)
+
+#define is_xtensa_elf(bfd) \
+  (bfd_get_flavour (bfd) == bfd_target_elf_flavour \
+   && elf_tdata (bfd) != NULL \
+   && elf_object_id (bfd) == XTENSA_ELF_TDATA)
+
+static bfd_boolean
+elf_xtensa_mkobject (bfd *abfd)
+{
+  return bfd_elf_allocate_object (abfd, sizeof (struct elf_xtensa_obj_tdata),
+				  XTENSA_ELF_TDATA);
+}
+
 /* Xtensa ELF linker hash table.  */
 
 struct elf_xtensa_link_hash_table
@@ -501,6 +602,8 @@ struct elf_xtensa_link_hash_table
      needed.  It is OK if this count is an overestimate, e.g., some
      relocations may be removed by GC.  */
   int plt_reloc_count;
+
+  struct elf_xtensa_link_hash_entry *tlsbase;
 };
 
 /* Get the Xtensa ELF linker hash table from a link_info structure.  */
@@ -508,11 +611,41 @@ struct elf_xtensa_link_hash_table
 #define elf_xtensa_hash_table(p) \
   ((struct elf_xtensa_link_hash_table *) ((p)->hash))
 
+/* Create an entry in an Xtensa ELF linker hash table.  */
+
+static struct bfd_hash_entry *
+elf_xtensa_link_hash_newfunc (struct bfd_hash_entry *entry,
+			      struct bfd_hash_table *table,
+			      const char *string)
+{
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (entry == NULL)
+    {
+      entry = bfd_hash_allocate (table,
+				 sizeof (struct elf_xtensa_link_hash_entry));
+      if (entry == NULL)
+	return entry;
+    }
+
+  /* Call the allocation method of the superclass.  */
+  entry = _bfd_elf_link_hash_newfunc (entry, table, string);
+  if (entry != NULL)
+    {
+      struct elf_xtensa_link_hash_entry *eh = elf_xtensa_hash_entry (entry);
+      eh->tlsfunc_refcount = 0;
+      eh->tls_type = GOT_UNKNOWN;
+    }
+
+  return entry;
+}
+
 /* Create an Xtensa ELF linker hash table.  */
 
 static struct bfd_link_hash_table *
 elf_xtensa_link_hash_table_create (bfd *abfd)
 {
+  struct elf_link_hash_entry *tlsbase;
   struct elf_xtensa_link_hash_table *ret;
   bfd_size_type amt = sizeof (struct elf_xtensa_link_hash_table);
 
@@ -521,8 +654,8 @@ elf_xtensa_link_hash_table_create (bfd *
     return NULL;
 
   if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd,
-				      _bfd_elf_link_hash_newfunc,
-				      sizeof (struct elf_link_hash_entry)))
+				      elf_xtensa_link_hash_newfunc,
+				      sizeof (struct elf_xtensa_link_hash_entry)))
     {
       free (ret);
       return NULL;
@@ -538,9 +671,46 @@ elf_xtensa_link_hash_table_create (bfd *
 
   ret->plt_reloc_count = 0;
 
+  /* Create a hash entry for "_TLS_MODULE_BASE_" to speed up checking
+     for it later.  */
+  tlsbase = elf_link_hash_lookup (&ret->elf, "_TLS_MODULE_BASE_",
+				  TRUE, FALSE, FALSE);
+  tlsbase->root.type = bfd_link_hash_new;
+  tlsbase->root.u.undef.abfd = NULL;
+  tlsbase->non_elf = 0;
+  ret->tlsbase = elf_xtensa_hash_entry (tlsbase);
+  ret->tlsbase->tls_type = GOT_UNKNOWN;
+
   return &ret->elf.root;
 }
 
+/* Copy the extra info we tack onto an elf_link_hash_entry.  */
+
+static void
+elf_xtensa_copy_indirect_symbol (struct bfd_link_info *info,
+				 struct elf_link_hash_entry *dir,
+				 struct elf_link_hash_entry *ind)
+{
+  struct elf_xtensa_link_hash_entry *edir, *eind;
+
+  edir = elf_xtensa_hash_entry (dir);
+  eind = elf_xtensa_hash_entry (ind);
+
+  if (ind->root.type == bfd_link_hash_indirect)
+    {
+      edir->tlsfunc_refcount += eind->tlsfunc_refcount;
+      eind->tlsfunc_refcount = 0;
+
+      if (dir->got.refcount <= 0)
+	{
+	  edir->tls_type = eind->tls_type;
+	  eind->tls_type = GOT_UNKNOWN;
+	}
+    }
+
+  _bfd_elf_link_hash_copy_indirect (info, dir, ind);
+}
+
 static inline bfd_boolean
 elf_xtensa_dynamic_symbol_p (struct elf_link_hash_entry *h,
 			     struct bfd_link_info *info)
@@ -800,9 +970,11 @@ elf_xtensa_check_relocs (bfd *abfd,
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
 
-  if (info->relocatable)
+  if (info->relocatable || (sec->flags & SEC_ALLOC) == 0)
     return TRUE;
 
+  BFD_ASSERT (is_xtensa_elf (abfd));
+
   htab = elf_xtensa_hash_table (info);
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
@@ -812,7 +984,12 @@ elf_xtensa_check_relocs (bfd *abfd,
     {
       unsigned int r_type;
       unsigned long r_symndx;
-      struct elf_link_hash_entry *h;
+      struct elf_link_hash_entry *h = NULL;
+      struct elf_xtensa_link_hash_entry *eh;
+      int tls_type, old_tls_type;
+      bfd_boolean is_got = FALSE;
+      bfd_boolean is_plt = FALSE;
+      bfd_boolean is_tlsfunc = FALSE;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
       r_type = ELF32_R_TYPE (rel->r_info);
@@ -824,46 +1001,94 @@ elf_xtensa_check_relocs (bfd *abfd,
 	  return FALSE;
 	}
 
-      if (r_symndx < symtab_hdr->sh_info)
-	h = NULL;
-      else
+      if (r_symndx >= symtab_hdr->sh_info)
 	{
 	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
 	  while (h->root.type == bfd_link_hash_indirect
 		 || h->root.type == bfd_link_hash_warning)
 	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
 	}
+      eh = elf_xtensa_hash_entry (h);
 
       switch (r_type)
 	{
-	case R_XTENSA_32:
-	  if (h == NULL)
-	    goto local_literal;
+	case R_XTENSA_TLSDESC_FN:
+	  if (info->shared)
+	    {
+	      tls_type = GOT_TLS_GD;
+	      is_got = TRUE;
+	      is_tlsfunc = TRUE;
+	    }
+	  else
+	    tls_type = GOT_TLS_IE;
+	  break;
 
-	  if ((sec->flags & SEC_ALLOC) != 0)
+	case R_XTENSA_TLSDESC_ARG:
+	  if (info->shared)
 	    {
-	      if (h->got.refcount <= 0)
-		h->got.refcount = 1;
-	      else
-		h->got.refcount += 1;
+	      tls_type = GOT_TLS_GD;
+	      is_got = TRUE;
+	    }
+	  else
+	    {
+	      tls_type = GOT_TLS_IE;
+	      if (h && elf_xtensa_hash_entry (h) != htab->tlsbase)
+		is_got = TRUE;
 	    }
 	  break;
 
+	case R_XTENSA_TLS_DTPOFF:
+	  if (info->shared)
+	    tls_type = GOT_TLS_GD;
+	  else
+	    tls_type = GOT_TLS_IE;
+	  break;
+
+	case R_XTENSA_TLS_TPOFF:
+	  tls_type = GOT_TLS_IE;
+	  if (info->shared)
+	    info->flags |= DF_STATIC_TLS;
+	  if (info->shared || h)
+	    is_got = TRUE;
+	  break;
+
+	case R_XTENSA_32:
+	  tls_type = GOT_NORMAL;
+	  is_got = TRUE;
+	  break;
+
 	case R_XTENSA_PLT:
-	  /* If this relocation is against a local symbol, then it's
-	     exactly the same as a normal local GOT entry.  */
-	  if (h == NULL)
-	    goto local_literal;
+	  tls_type = GOT_NORMAL;
+	  is_plt = TRUE;
+	  break;
+
+	case R_XTENSA_GNU_VTINHERIT:
+	  /* This relocation describes the C++ object vtable hierarchy.
+	     Reconstruct it for later use during GC.  */
+	  if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
+	    return FALSE;
+	  continue;
 
-	  if ((sec->flags & SEC_ALLOC) != 0)
+	case R_XTENSA_GNU_VTENTRY:
+	  /* This relocation describes which C++ vtable entries are actually
+	     used.  Record for later use during GC.  */
+	  BFD_ASSERT (h != NULL);
+	  if (h != NULL
+	      && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+	    return FALSE;
+	  continue;
+
+	default:
+	  /* Nothing to do for any other relocations.  */
+	  continue;
+	}
+
+      if (h)
+	{
+	  if (is_plt)
 	    {
-	      if (h->plt.refcount <= 0)
-		{
-		  h->needs_plt = 1;
-		  h->plt.refcount = 1;
-		}
-	      else
-		h->plt.refcount += 1;
+	      h->plt.refcount += 1;
+	      h->needs_plt = 1;
 
 	      /* Keep track of the total PLT relocation count even if we
 		 don't yet know whether the dynamic sections will be
@@ -876,49 +1101,77 @@ elf_xtensa_check_relocs (bfd *abfd,
 		    return FALSE;
 		}
 	    }
-	  break;
+	  else if (is_got)
+	    h->got.refcount += 1;
+
+	  if (is_tlsfunc)
+	    eh->tlsfunc_refcount += 1;
 
-	local_literal:
-	  if ((sec->flags & SEC_ALLOC) != 0)
+	  old_tls_type = eh->tls_type;
+	}
+      else
+	{
+	  /* Allocate storage the first time.  */
+	  if (elf_local_got_refcounts (abfd) == NULL)
 	    {
-	      bfd_signed_vma *local_got_refcounts;
+	      bfd_size_type size = symtab_hdr->sh_info;
+	      void *mem;
 
-	      /* This is a global offset table entry for a local symbol.  */
-	      local_got_refcounts = elf_local_got_refcounts (abfd);
-	      if (local_got_refcounts == NULL)
-		{
-		  bfd_size_type size;
+	      mem = bfd_zalloc (abfd, size * sizeof (bfd_signed_vma));
+	      if (mem == NULL)
+		return FALSE;
+	      elf_local_got_refcounts (abfd) = (bfd_signed_vma *) mem;
 
-		  size = symtab_hdr->sh_info;
-		  size *= sizeof (bfd_signed_vma);
-		  local_got_refcounts =
-		    (bfd_signed_vma *) bfd_zalloc (abfd, size);
-		  if (local_got_refcounts == NULL)
-		    return FALSE;
-		  elf_local_got_refcounts (abfd) = local_got_refcounts;
-		}
-	      local_got_refcounts[r_symndx] += 1;
+	      mem = bfd_zalloc (abfd, size);
+	      if (mem == NULL)
+		return FALSE;
+	      elf_xtensa_local_got_tls_type (abfd) = (char *) mem;
+
+	      mem = bfd_zalloc (abfd, size * sizeof (bfd_signed_vma));
+	      if (mem == NULL)
+		return FALSE;
+	      elf_xtensa_local_tlsfunc_refcounts (abfd)
+		= (bfd_signed_vma *) mem;
 	    }
-	  break;
 
-	case R_XTENSA_GNU_VTINHERIT:
-	  /* This relocation describes the C++ object vtable hierarchy.
-	     Reconstruct it for later use during GC.  */
-	  if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
-	    return FALSE;
-	  break;
+	  /* This is a global offset table entry for a local symbol.  */
+	  if (is_got || is_plt)
+	    elf_local_got_refcounts (abfd) [r_symndx] += 1;
 
-	case R_XTENSA_GNU_VTENTRY:
-	  /* This relocation describes which C++ vtable entries are actually
-	     used.  Record for later use during GC.  */
-	  BFD_ASSERT (h != NULL);
-	  if (h != NULL
-	      && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
-	    return FALSE;
-	  break;
+	  if (is_tlsfunc)
+	    elf_xtensa_local_tlsfunc_refcounts (abfd) [r_symndx] += 1;
 
-	default:
-	  break;
+	  old_tls_type = elf_xtensa_local_got_tls_type (abfd) [r_symndx];
+	}
+
+      if ((old_tls_type & GOT_TLS_IE) && (tls_type & GOT_TLS_IE))
+	tls_type |= old_tls_type;
+      /* If a TLS symbol is accessed using IE at least once,
+	 there is no point to use a dynamic model for it.  */
+      else if (old_tls_type != tls_type && old_tls_type != GOT_UNKNOWN
+	       && ((old_tls_type & GOT_TLS_GD) == 0
+		   || (tls_type & GOT_TLS_IE) == 0))
+	{
+	  if ((old_tls_type & GOT_TLS_IE) && (tls_type & GOT_TLS_GD))
+	    tls_type = old_tls_type;
+	  else if ((old_tls_type & GOT_TLS_GD) && (tls_type & GOT_TLS_GD))
+	    tls_type |= old_tls_type;
+	  else
+	    {
+	      (*_bfd_error_handler)
+		(_("%B: `%s' accessed both as normal and thread local symbol"),
+		 abfd,
+		 h ? h->root.root.string : "<local>");
+	      return FALSE;
+	    }
+	}
+
+      if (old_tls_type != tls_type)
+	{
+	  if (eh)
+	    eh->tls_type = tls_type;
+	  else
+	    elf_xtensa_local_got_tls_type (abfd) [r_symndx] = tls_type;
 	}
     }
 
@@ -1004,14 +1257,16 @@ elf_xtensa_gc_mark_hook (asection *sec,
 
 static bfd_boolean
 elf_xtensa_gc_sweep_hook (bfd *abfd,
-			  struct bfd_link_info *info ATTRIBUTE_UNUSED,
+			  struct bfd_link_info *info,
 			  asection *sec,
 			  const Elf_Internal_Rela *relocs)
 {
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
-  bfd_signed_vma *local_got_refcounts;
   const Elf_Internal_Rela *rel, *relend;
+  struct elf_xtensa_link_hash_table *htab;
+
+  htab = elf_xtensa_hash_table (info);
 
   if (info->relocatable)
     return TRUE;
@@ -1021,7 +1276,6 @@ elf_xtensa_gc_sweep_hook (bfd *abfd,
 
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
-  local_got_refcounts = elf_local_got_refcounts (abfd);
 
   relend = relocs + sec->reloc_count;
   for (rel = relocs; rel < relend; rel++)
@@ -1029,6 +1283,10 @@ elf_xtensa_gc_sweep_hook (bfd *abfd,
       unsigned long r_symndx;
       unsigned int r_type;
       struct elf_link_hash_entry *h = NULL;
+      struct elf_xtensa_link_hash_entry *eh;
+      bfd_boolean is_got = FALSE;
+      bfd_boolean is_plt = FALSE;
+      bfd_boolean is_tlsfunc = FALSE;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
       if (r_symndx >= symtab_hdr->sh_info)
@@ -1038,31 +1296,80 @@ elf_xtensa_gc_sweep_hook (bfd *abfd,
 		 || h->root.type == bfd_link_hash_warning)
 	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
 	}
+      eh = elf_xtensa_hash_entry (h);
 
       r_type = ELF32_R_TYPE (rel->r_info);
       switch (r_type)
 	{
-	case R_XTENSA_32:
-	  if (h == NULL)
-	    goto local_literal;
-	  if (h->got.refcount > 0)
-	    h->got.refcount--;
+	case R_XTENSA_TLSDESC_FN:
+	  if (info->shared)
+	    {
+	      is_got = TRUE;
+	      is_tlsfunc = TRUE;
+	    }
 	  break;
 
-	case R_XTENSA_PLT:
-	  if (h == NULL)
-	    goto local_literal;
-	  if (h->plt.refcount > 0)
-	    h->plt.refcount--;
+	case R_XTENSA_TLSDESC_ARG:
+	  if (info->shared)
+	    is_got = TRUE;
+	  else
+	    {
+	      if (h && elf_xtensa_hash_entry (h) != htab->tlsbase)
+		is_got = TRUE;
+	    }
 	  break;
 
-	local_literal:
-	  if (local_got_refcounts[r_symndx] > 0)
-	    local_got_refcounts[r_symndx] -= 1;
+	case R_XTENSA_TLS_TPOFF:
+	  if (info->shared || h)
+	    is_got = TRUE;
 	  break;
 
-	default:
+	case R_XTENSA_32:
+	  is_got = TRUE;
+	  break;
+
+	case R_XTENSA_PLT:
+	  is_plt = TRUE;
 	  break;
+
+	default:
+	  continue;
+	}
+
+      if (h)
+	{
+	  if (is_plt)
+	    {
+	      if (h->plt.refcount > 0)
+		h->plt.refcount--;
+	    }
+	  else if (is_got)
+	    {
+	      if (h->got.refcount > 0)
+		h->got.refcount--;
+	    }
+	  if (is_tlsfunc)
+	    {
+	      if (eh->tlsfunc_refcount > 0)
+		eh->tlsfunc_refcount--;
+	    }
+	}
+      else
+	{
+	  if (is_got || is_plt)
+	    {
+	      bfd_signed_vma *got_refcount
+		= &elf_local_got_refcounts (abfd) [r_symndx];
+	      if (*got_refcount > 0)
+		*got_refcount -= 1;
+	    }
+	  if (is_tlsfunc)
+	    {
+	      bfd_signed_vma *tlsfunc_refcount
+		= &elf_xtensa_local_tlsfunc_refcounts (abfd) [r_symndx];
+	      if (*tlsfunc_refcount > 0)
+		*tlsfunc_refcount -= 1;
+	    }
 	}
     }
 
@@ -1200,7 +1507,7 @@ elf_xtensa_allocate_dynrelocs (struct el
 {
   struct bfd_link_info *info;
   struct elf_xtensa_link_hash_table *htab;
-  bfd_boolean is_dynamic;
+  struct elf_xtensa_link_hash_entry *eh = elf_xtensa_hash_entry (h);
 
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
@@ -1211,9 +1518,15 @@ elf_xtensa_allocate_dynrelocs (struct el
   info = (struct bfd_link_info *) arg;
   htab = elf_xtensa_hash_table (info);
 
-  is_dynamic = elf_xtensa_dynamic_symbol_p (h, info);
+  /* If we saw any use of an IE model for this symbol, we can then optimize
+     away GOT entries for any TLSDESC_FN relocs.  */
+  if ((eh->tls_type & GOT_TLS_IE) != 0)
+    {
+      BFD_ASSERT (h->got.refcount >= eh->tlsfunc_refcount);
+      h->got.refcount -= eh->tlsfunc_refcount;
+    }
 
-  if (! is_dynamic)
+  if (! elf_xtensa_dynamic_symbol_p (h, info))
     elf_xtensa_make_sym_local (info, h);
 
   if (h->plt.refcount > 0)
@@ -1249,6 +1562,16 @@ elf_xtensa_allocate_local_got_size (stru
 
       for (j = 0; j < cnt; ++j)
 	{
+	  /* If we saw any use of an IE model for this symbol, we can
+	     then optimize away GOT entries for any TLSDESC_FN relocs.  */
+	  if ((elf_xtensa_local_got_tls_type (i) [j] & GOT_TLS_IE) != 0)
+	    {
+	      bfd_signed_vma *tlsfunc_refcount
+		= &elf_xtensa_local_tlsfunc_refcounts (i) [j];
+	      BFD_ASSERT (local_got_refcounts[j] >= *tlsfunc_refcount);
+	      local_got_refcounts[j] -= *tlsfunc_refcount;
+	    }
+
 	  if (local_got_refcounts[j] > 0)
 	    htab->srelgot->size += (local_got_refcounts[j]
 				    * sizeof (Elf32_External_Rela));
@@ -1498,7 +1821,66 @@ elf_xtensa_size_dynamic_sections (bfd *o
   return TRUE;
 }
 
+static bfd_boolean
+elf_xtensa_always_size_sections (bfd *output_bfd,
+				 struct bfd_link_info *info)
+{
+  struct elf_xtensa_link_hash_table *htab;
+  asection *tls_sec;
+
+  htab = elf_xtensa_hash_table (info);
+  tls_sec = htab->elf.tls_sec;
+
+  if (tls_sec && (htab->tlsbase->tls_type & GOT_TLS_ANY) != 0)
+    {
+      struct elf_link_hash_entry *tlsbase = &htab->tlsbase->elf;
+      struct bfd_link_hash_entry *bh = &tlsbase->root;
+      const struct elf_backend_data *bed = get_elf_backend_data (output_bfd);
+
+      tlsbase->type = STT_TLS;
+      if (!(_bfd_generic_link_add_one_symbol
+	    (info, output_bfd, "_TLS_MODULE_BASE_", BSF_LOCAL,
+	     tls_sec, 0, NULL, FALSE,
+	     bed->collect, &bh)))
+	return FALSE;
+      tlsbase->def_regular = 1;
+      tlsbase->other = STV_HIDDEN;
+      (*bed->elf_backend_hide_symbol) (info, tlsbase, TRUE);
+    }
+
+  return TRUE;
+}
+
 
+/* Return the base VMA address which should be subtracted from real addresses
+   when resolving @dtpoff relocation.
+   This is PT_TLS segment p_vaddr.  */
+
+static bfd_vma
+dtpoff_base (struct bfd_link_info *info)
+{
+  /* If tls_sec is NULL, we should have signalled an error already.  */
+  if (elf_hash_table (info)->tls_sec == NULL)
+    return 0;
+  return elf_hash_table (info)->tls_sec->vma;
+}
+
+/* Return the relocation value for @tpoff relocation
+   if STT_TLS virtual address is ADDRESS.  */
+
+static bfd_vma
+tpoff (struct bfd_link_info *info, bfd_vma address)
+{
+  struct elf_link_hash_table *htab = elf_hash_table (info);
+  bfd_vma base;
+
+  /* If tls_sec is NULL, we should have signalled an error already.  */
+  if (htab->tls_sec == NULL)
+    return 0;
+  base = align_power ((bfd_vma) TCB_SIZE, htab->tls_sec->alignment_power);
+  return address - htab->tls_sec->vma + base;
+}
+
 /* Perform the specified relocation.  The instruction at (contents + address)
    is modified to set one operand to represent the value in "relocation".  The
    operand position is determined by the relocation type recorded in the
@@ -1546,6 +1928,9 @@ elf_xtensa_do_reloc (reloc_howto_type *h
     case R_XTENSA_DIFF8:
     case R_XTENSA_DIFF16:
     case R_XTENSA_DIFF32:
+    case R_XTENSA_TLS_FUNC:
+    case R_XTENSA_TLS_ARG:
+    case R_XTENSA_TLS_CALL:
       return bfd_reloc_ok;
 
     case R_XTENSA_ASM_EXPAND:
@@ -1585,7 +1970,6 @@ elf_xtensa_do_reloc (reloc_howto_type *h
       break;
 
     case R_XTENSA_32:
-    case R_XTENSA_PLT:
       {
 	bfd_vma x;
 	x = bfd_get_32 (abfd, contents + address);
@@ -1597,6 +1981,14 @@ elf_xtensa_do_reloc (reloc_howto_type *h
     case R_XTENSA_32_PCREL:
       bfd_put_32 (abfd, relocation - self_address, contents + address);
       return bfd_reloc_ok;
+
+    case R_XTENSA_PLT:
+    case R_XTENSA_TLSDESC_FN:
+    case R_XTENSA_TLSDESC_ARG:
+    case R_XTENSA_TLS_DTPOFF:
+    case R_XTENSA_TLS_TPOFF:
+      bfd_put_32 (abfd, relocation, contents + address);
+      return bfd_reloc_ok;
     }
 
   /* Only instruction slot-specific relocations handled below.... */
@@ -1936,6 +2328,188 @@ elf_xtensa_create_plt_entry (struct bfd_
 }
 
 
+static bfd_boolean get_indirect_call_dest_reg (xtensa_opcode, unsigned *);
+
+static bfd_boolean
+replace_tls_insn (Elf_Internal_Rela *rel,
+		  bfd *abfd,
+		  asection *input_section,
+		  bfd_byte *contents,
+		  bfd_boolean is_ld_model,
+		  char **error_message)
+{
+  static xtensa_insnbuf ibuff = NULL;
+  static xtensa_insnbuf sbuff = NULL;
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_format fmt;
+  xtensa_opcode old_op, new_op;
+  bfd_size_type input_size;
+  int r_type;
+  unsigned dest_reg, src_reg;
+
+  if (ibuff == NULL)
+    {
+      ibuff = xtensa_insnbuf_alloc (isa);
+      sbuff = xtensa_insnbuf_alloc (isa);
+    }
+
+  input_size = bfd_get_section_limit (abfd, input_section);
+
+  /* Read the instruction into a buffer and decode the opcode.  */
+  xtensa_insnbuf_from_chars (isa, ibuff, contents + rel->r_offset,
+			     input_size - rel->r_offset);
+  fmt = xtensa_format_decode (isa, ibuff);
+  if (fmt == XTENSA_UNDEFINED)
+    {
+      *error_message = "cannot decode instruction format";
+      return FALSE;
+    }
+
+  BFD_ASSERT (xtensa_format_num_slots (isa, fmt) == 1);
+  xtensa_format_get_slot (isa, fmt, 0, ibuff, sbuff);
+
+  old_op = xtensa_opcode_decode (isa, fmt, 0, sbuff);
+  if (old_op == XTENSA_UNDEFINED)
+    {
+      *error_message = "cannot decode instruction opcode";
+      return FALSE;
+    }
+
+  r_type = ELF32_R_TYPE (rel->r_info);
+  switch (r_type)
+    {
+    case R_XTENSA_TLS_FUNC:
+    case R_XTENSA_TLS_ARG:
+      if (old_op != get_l32r_opcode ()
+	  || xtensa_operand_get_field (isa, old_op, 0, fmt, 0,
+				       sbuff, &dest_reg) != 0)
+	{
+	  *error_message = "cannot extract L32R destination for TLS access";
+	  return FALSE;
+	}
+      break;
+
+    case R_XTENSA_TLS_CALL:
+      if (! get_indirect_call_dest_reg (old_op, &dest_reg)
+	  || xtensa_operand_get_field (isa, old_op, 0, fmt, 0,
+				       sbuff, &src_reg) != 0)
+	{
+	  *error_message = "cannot extract CALLXn operands for TLS access";
+	  return FALSE;
+	}
+      break;
+
+    default:
+      abort ();
+    }
+
+  if (is_ld_model)
+    {
+      switch (r_type)
+	{
+	case R_XTENSA_TLS_FUNC:
+	case R_XTENSA_TLS_ARG:
+	  /* Change the instruction to a NOP (or "OR a1, a1, a1" for older
+	     versions of Xtensa).  */
+	  new_op = xtensa_opcode_lookup (isa, "nop");
+	  if (new_op == XTENSA_UNDEFINED)
+	    {
+	      new_op = xtensa_opcode_lookup (isa, "or");
+	      if (new_op == XTENSA_UNDEFINED
+		  || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
+		  || xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
+					       sbuff, 1) != 0
+		  || xtensa_operand_set_field (isa, new_op, 1, fmt, 0,
+					       sbuff, 1) != 0
+		  || xtensa_operand_set_field (isa, new_op, 2, fmt, 0,
+					       sbuff, 1) != 0)
+		{
+		  *error_message = "cannot encode OR for TLS access";
+		  return FALSE;
+		}
+	    }
+	  else
+	    {
+	      if (xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0)
+		{
+		  *error_message = "cannot encode NOP for TLS access";
+		  return FALSE;
+		}
+	    }
+	  break;
+
+	case R_XTENSA_TLS_CALL:
+	  /* Read THREADPTR into the CALLX's return value register.  */
+	  new_op = xtensa_opcode_lookup (isa, "rur.threadptr");
+	  if (new_op == XTENSA_UNDEFINED
+	      || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
+	      || xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
+					   sbuff, dest_reg + 2) != 0)
+	    {
+	      *error_message = "cannot encode RUR.THREADPTR for TLS access";
+	      return FALSE;
+	    }
+	  break;
+	}
+    }
+  else
+    {
+      switch (r_type)
+	{
+	case R_XTENSA_TLS_FUNC:
+	  new_op = xtensa_opcode_lookup (isa, "rur.threadptr");
+	  if (new_op == XTENSA_UNDEFINED
+	      || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
+	      || xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
+					   sbuff, dest_reg) != 0)
+	    {
+	      *error_message = "cannot encode RUR.THREADPTR for TLS access";
+	      return FALSE;
+	    }
+	  break;
+
+	case R_XTENSA_TLS_ARG:
+	  /* Nothing to do.  Keep the original L32R instruction.  */
+	  return TRUE;
+
+	case R_XTENSA_TLS_CALL:
+	  /* Add the CALLX's src register (holding the THREADPTR value)
+	     to the first argument register (holding the offset) and put
+	     the result in the CALLX's return value register.  */
+	  new_op = xtensa_opcode_lookup (isa, "add");
+	  if (new_op == XTENSA_UNDEFINED
+	      || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
+	      || xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
+					   sbuff, dest_reg + 2) != 0
+	      || xtensa_operand_set_field (isa, new_op, 1, fmt, 0,
+					   sbuff, dest_reg + 2) != 0
+	      || xtensa_operand_set_field (isa, new_op, 2, fmt, 0,
+					   sbuff, src_reg) != 0)
+	    {
+	      *error_message = "cannot encode ADD for TLS access";
+	      return FALSE;
+	    }
+	  break;
+	}
+    }
+
+  xtensa_format_set_slot (isa, fmt, 0, ibuff, sbuff);
+  xtensa_insnbuf_to_chars (isa, ibuff, contents + rel->r_offset,
+                           input_size - rel->r_offset);
+
+  return TRUE;
+}
+
+
+#define IS_XTENSA_TLS_RELOC(R_TYPE) \
+  ((R_TYPE) == R_XTENSA_TLSDESC_FN \
+   || (R_TYPE) == R_XTENSA_TLSDESC_ARG \
+   || (R_TYPE) == R_XTENSA_TLS_DTPOFF \
+   || (R_TYPE) == R_XTENSA_TLS_TPOFF \
+   || (R_TYPE) == R_XTENSA_TLS_FUNC \
+   || (R_TYPE) == R_XTENSA_TLS_ARG \
+   || (R_TYPE) == R_XTENSA_TLS_CALL)
+
 /* Relocate an Xtensa ELF section.  This is invoked by the linker for
    both relocatable and final links.  */
 
@@ -1956,15 +2530,20 @@ elf_xtensa_relocate_section (bfd *output
   struct elf_link_hash_entry **sym_hashes;
   property_table_entry *lit_table = 0;
   int ltblsize = 0;
+  char *local_got_tls_types;
   char *error_message = NULL;
   bfd_size_type input_size;
+  int tls_type;
 
   if (!xtensa_default_isa)
     xtensa_default_isa = xtensa_isa_init (0, 0);
 
+  BFD_ASSERT (is_xtensa_elf (input_bfd));
+
   htab = elf_xtensa_hash_table (info);
   symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (input_bfd);
+  local_got_tls_types = elf_xtensa_local_got_tls_type (input_bfd);
 
   if (elf_hash_table (info)->dynamic_sections_created)
     {
@@ -1986,12 +2565,15 @@ elf_xtensa_relocate_section (bfd *output
       unsigned long r_symndx;
       struct elf_link_hash_entry *h;
       Elf_Internal_Sym *sym;
+      char sym_type;
+      const char *name;
       asection *sec;
       bfd_vma relocation;
       bfd_reloc_status_type r;
       bfd_boolean is_weak_undef;
       bfd_boolean unresolved_reloc;
       bfd_boolean warned;
+      bfd_boolean dynamic_symbol;
 
       r_type = ELF32_R_TYPE (rel->r_info);
       if (r_type == (int) R_XTENSA_GNU_VTINHERIT
@@ -2027,6 +2609,7 @@ elf_xtensa_relocate_section (bfd *output
       if (r_symndx < symtab_hdr->sh_info)
 	{
 	  sym = local_syms + r_symndx;
+	  sym_type = ELF32_ST_TYPE (sym->st_info);
 	  sec = local_sections[r_symndx];
 	  relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
 	}
@@ -2041,6 +2624,8 @@ elf_xtensa_relocate_section (bfd *output
 	      && !unresolved_reloc
 	      && h->root.type == bfd_link_hash_undefweak)
 	    is_weak_undef = TRUE;
+
+	  sym_type = h->type;
 	}
 
       if (sec != NULL && elf_discarded_section (sec))
@@ -2152,27 +2737,49 @@ elf_xtensa_relocate_section (bfd *output
 	  return FALSE;
 	}
 
-      /* Generate dynamic relocations.  */
-      if (elf_hash_table (info)->dynamic_sections_created)
+      if (h != NULL)
+	name = h->root.root.string;
+      else
 	{
-	  bfd_boolean dynamic_symbol = elf_xtensa_dynamic_symbol_p (h, info);
+	  name = (bfd_elf_string_from_elf_section
+		  (input_bfd, symtab_hdr->sh_link, sym->st_name));
+	  if (name == NULL || *name == '\0')
+	    name = bfd_section_name (input_bfd, sec);
+	}
 
-	  if (dynamic_symbol && (is_operand_relocation (r_type)
-				 || r_type == R_XTENSA_32_PCREL))
-	    {
-	      const char *name = h->root.root.string;
-	      error_message =
-		vsprint_msg ("invalid relocation for dynamic symbol", ": %s",
-			     strlen (name) + 2, name);
-	      if (!((*info->callbacks->reloc_dangerous)
-		    (info, error_message, input_bfd, input_section,
-		     rel->r_offset)))
-		return FALSE;
-	      continue;
-	    }
-	  else if ((r_type == R_XTENSA_32 || r_type == R_XTENSA_PLT)
-		   && (input_section->flags & SEC_ALLOC) != 0
-		   && (dynamic_symbol || info->shared))
+      if (r_symndx != 0
+	  && r_type != R_XTENSA_NONE
+	  && (h == NULL
+	      || h->root.type == bfd_link_hash_defined
+	      || h->root.type == bfd_link_hash_defweak)
+	  && IS_XTENSA_TLS_RELOC (r_type) != (sym_type == STT_TLS))
+	{
+	  (*_bfd_error_handler)
+	    ((sym_type == STT_TLS
+	      ? _("%B(%A+0x%lx): %s used with TLS symbol %s")
+	      : _("%B(%A+0x%lx): %s used with non-TLS symbol %s")),
+	     input_bfd,
+	     input_section,
+	     (long) rel->r_offset,
+	     howto->name,
+	     name);
+	}
+
+      dynamic_symbol = elf_xtensa_dynamic_symbol_p (h, info);
+
+      tls_type = GOT_UNKNOWN;
+      if (h)
+	tls_type = elf_xtensa_hash_entry (h)->tls_type;
+      else if (local_got_tls_types)
+	tls_type = local_got_tls_types [r_symndx];
+
+      switch (r_type)
+	{
+	case R_XTENSA_32:
+	case R_XTENSA_PLT:
+	  if (elf_hash_table (info)->dynamic_sections_created
+	      && (input_section->flags & SEC_ALLOC) != 0
+	      && (dynamic_symbol || info->shared))
 	    {
 	      Elf_Internal_Rela outrel;
 	      bfd_byte *loc;
@@ -2256,6 +2863,151 @@ elf_xtensa_relocate_section (bfd *output
 		 Just ignore these relocations.  */
 	      continue;
 	    }
+	  break;
+
+	case R_XTENSA_TLS_TPOFF:
+	  /* Switch to LE model for local symbols in an executable.  */
+	  if (! info->shared && ! dynamic_symbol)
+	    {
+	      relocation = tpoff (info, relocation);
+	      break;
+	    }
+	  /* fall through */
+
+	case R_XTENSA_TLSDESC_FN:
+	case R_XTENSA_TLSDESC_ARG:
+	  {
+	    if (r_type == R_XTENSA_TLSDESC_FN)
+	      {
+		if (! info->shared || (tls_type & GOT_TLS_IE) != 0)
+		  r_type = R_XTENSA_NONE;
+	      }
+	    else if (r_type == R_XTENSA_TLSDESC_ARG)
+	      {
+		if (info->shared)
+		  {
+		    if ((tls_type & GOT_TLS_IE) != 0)
+		      r_type = R_XTENSA_TLS_TPOFF;
+		  }
+		else
+		  {
+		    r_type = R_XTENSA_TLS_TPOFF;
+		    if (! dynamic_symbol)
+		      {
+			relocation = tpoff (info, relocation);
+			break;
+		      }
+		  }
+	      }
+
+	    if (r_type == R_XTENSA_NONE)
+	      /* Nothing to do here; skip to the next reloc.  */
+	      continue;
+
+	    if (! elf_hash_table (info)->dynamic_sections_created)
+	      {
+		error_message =
+		  _("TLS relocation invalid without dynamic sections");
+		if (!((*info->callbacks->reloc_dangerous)
+		      (info, error_message, input_bfd, input_section,
+		       rel->r_offset)))
+		  return FALSE;
+	      }
+	    else
+	      {
+		Elf_Internal_Rela outrel;
+		bfd_byte *loc;
+		asection *srel = htab->srelgot;
+		int indx;
+
+		outrel.r_offset = (input_section->output_section->vma
+				   + input_section->output_offset
+				   + rel->r_offset);
+
+		/* Complain if the relocation is in a read-only section
+		   and not in a literal pool.  */
+		if ((input_section->flags & SEC_READONLY) != 0
+		    && ! elf_xtensa_in_literal_pool (lit_table, ltblsize,
+						     outrel.r_offset))
+		  {
+		    error_message =
+		      _("dynamic relocation in read-only section");
+		    if (!((*info->callbacks->reloc_dangerous)
+			  (info, error_message, input_bfd, input_section,
+			   rel->r_offset)))
+		      return FALSE;
+		  }
+
+		indx = h && h->dynindx != -1 ? h->dynindx : 0;
+		if (indx == 0)
+		  outrel.r_addend = relocation - dtpoff_base (info);
+		else
+		  outrel.r_addend = 0;
+		rel->r_addend = 0;
+
+		outrel.r_info = ELF32_R_INFO (indx, r_type);
+		relocation = 0;
+		unresolved_reloc = FALSE;
+
+		BFD_ASSERT (srel);
+		loc = (srel->contents
+		       + srel->reloc_count++ * sizeof (Elf32_External_Rela));
+		bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
+		BFD_ASSERT (sizeof (Elf32_External_Rela) * srel->reloc_count
+			    <= srel->size);
+	      }
+	  }
+	  break;
+
+	case R_XTENSA_TLS_DTPOFF:
+	  if (! info->shared)
+	    /* Switch from LD model to LE model.  */
+	    relocation = tpoff (info, relocation);
+	  else
+	    relocation -= dtpoff_base (info);
+	  break;
+
+	case R_XTENSA_TLS_FUNC:
+	case R_XTENSA_TLS_ARG:
+	case R_XTENSA_TLS_CALL:
+	  /* Check if optimizing to IE or LE model.  */
+	  if ((tls_type & GOT_TLS_IE) != 0)
+	    {
+	      bfd_boolean is_ld_model =
+		(h && elf_xtensa_hash_entry (h) == htab->tlsbase);
+	      if (! replace_tls_insn (rel, input_bfd, input_section, contents,
+				      is_ld_model, &error_message))
+		{
+		  if (!((*info->callbacks->reloc_dangerous)
+			(info, error_message, input_bfd, input_section,
+			 rel->r_offset)))
+		    return FALSE;
+		}
+
+	      if (r_type != R_XTENSA_TLS_ARG || is_ld_model)
+		{
+		  /* Skip subsequent relocations on the same instruction.  */
+		  while (rel + 1 < relend && rel[1].r_offset == rel->r_offset)
+		    rel++;
+		}
+	    }
+	  continue;
+
+	default:
+	  if (elf_hash_table (info)->dynamic_sections_created
+	      && dynamic_symbol && (is_operand_relocation (r_type)
+				    || r_type == R_XTENSA_32_PCREL))
+	    {
+	      error_message =
+		vsprint_msg ("invalid relocation for dynamic symbol", ": %s",
+			     strlen (name) + 2, name);
+	      if (!((*info->callbacks->reloc_dangerous)
+		    (info, error_message, input_bfd, input_section,
+		     rel->r_offset)))
+		return FALSE;
+	      continue;
+	    }
+	  break;
 	}
 
       /* Dynamic relocs are not propagated for SEC_DEBUGGING sections
@@ -2271,10 +3023,13 @@ elf_xtensa_relocate_section (bfd *output
 	     input_section,
 	     (long) rel->r_offset,
 	     howto->name,
-	     h->root.root.string);
+	     name);
 	  return FALSE;
 	}
 
+      /* TLS optimizations may have changed r_type; update "howto".  */
+      howto = &elf_howto_table[r_type];
+
       /* There's no point in calling bfd_perform_relocation here.
 	 Just go directly to our "special function".  */
       r = elf_xtensa_do_reloc (howto, input_bfd, input_section,
@@ -2284,30 +3039,16 @@ elf_xtensa_relocate_section (bfd *output
 
       if (r != bfd_reloc_ok && !warned)
 	{
-	  const char *name;
-
 	  BFD_ASSERT (r == bfd_reloc_dangerous || r == bfd_reloc_other);
 	  BFD_ASSERT (error_message != NULL);
 
-	  if (h)
-	    name = h->root.root.string;
+	  if (rel->r_addend == 0)
+	    error_message = vsprint_msg (error_message, ": %s",
+					 strlen (name) + 2, name);
 	  else
-	    {
-	      name = bfd_elf_string_from_elf_section
-		(input_bfd, symtab_hdr->sh_link, sym->st_name);
-	      if (name && *name == '\0')
-		name = bfd_section_name (input_bfd, sec);
-	    }
-	  if (name)
-	    {
-	      if (rel->r_addend == 0)
-		error_message = vsprint_msg (error_message, ": %s",
-					     strlen (name) + 2, name);
-	      else
-		error_message = vsprint_msg (error_message, ": (%s+0x%x)",
-					     strlen (name) + 22,
-					     name, (int)rel->r_addend);
-	    }
+	    error_message = vsprint_msg (error_message, ": (%s+0x%x)",
+					 strlen (name) + 22,
+					 name, (int) rel->r_addend);
 
 	  if (!((*info->callbacks->reloc_dangerous)
 		(info, error_message, input_bfd, input_section,
@@ -3111,6 +3852,29 @@ is_windowed_call_opcode (xtensa_opcode o
 }
 
 
+static bfd_boolean
+get_indirect_call_dest_reg (xtensa_opcode opcode, unsigned *pdst)
+{
+  unsigned dst = (unsigned) -1;
+
+  init_call_opcodes ();
+  if (opcode == callx0_op)
+    dst = 0;
+  else if (opcode == callx4_op)
+    dst = 4;
+  else if (opcode == callx8_op)
+    dst = 8;
+  else if (opcode == callx12_op)
+    dst = 12;
+
+  if (dst == (unsigned) -1)
+    return FALSE;
+
+  *pdst = dst;
+  return TRUE;
+}
+
+
 static xtensa_opcode
 get_const16_opcode (void)
 {
@@ -9965,6 +10729,8 @@ static const struct bfd_elf_special_sect
 
 #define elf_info_to_howto		     elf_xtensa_info_to_howto_rela
 
+#define bfd_elf32_mkobject		     elf_xtensa_mkobject
+
 #define bfd_elf32_bfd_merge_private_bfd_data elf_xtensa_merge_private_bfd_data
 #define bfd_elf32_new_section_hook	     elf_xtensa_new_section_hook
 #define bfd_elf32_bfd_print_private_bfd_data elf_xtensa_print_private_bfd_data
@@ -9992,9 +10758,11 @@ static const struct bfd_elf_special_sect
 #define elf_backend_reloc_type_class	     elf_xtensa_reloc_type_class
 #define elf_backend_relocate_section	     elf_xtensa_relocate_section
 #define elf_backend_size_dynamic_sections    elf_xtensa_size_dynamic_sections
+#define elf_backend_always_size_sections     elf_xtensa_always_size_sections
 #define elf_backend_omit_section_dynsym \
   ((bfd_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true)
 #define elf_backend_special_sections	     elf_xtensa_special_sections
 #define elf_backend_action_discarded	     elf_xtensa_action_discarded
+#define elf_backend_copy_indirect_symbol     elf_xtensa_copy_indirect_symbol
 
 #include "elf32-target.h"
Index: bfd/reloc.c
===================================================================
RCS file: /cvs/src/src/bfd/reloc.c,v
retrieving revision 1.176
diff -u -p -r1.176 reloc.c
--- bfd/reloc.c	8 Aug 2008 19:24:48 -0000	1.176
+++ bfd/reloc.c	20 Aug 2008 23:14:39 -0000
@@ -1,6 +1,6 @@
 /* BFD support for handling relocation entries.
    Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
    Free Software Foundation, Inc.
    Written by Cygnus Support.
 
@@ -5015,6 +5015,22 @@ ENUMDOC
   assembler-expanded instructions.  This is commonly used
   internally by the linker after analysis of a
   BFD_RELOC_XTENSA_ASM_EXPAND.
+ENUM
+  BFD_RELOC_XTENSA_TLSDESC_FN
+ENUMX
+  BFD_RELOC_XTENSA_TLSDESC_ARG
+ENUMX
+  BFD_RELOC_XTENSA_TLS_DTPOFF
+ENUMX
+  BFD_RELOC_XTENSA_TLS_TPOFF
+ENUMX
+  BFD_RELOC_XTENSA_TLS_FUNC
+ENUMX
+  BFD_RELOC_XTENSA_TLS_ARG
+ENUMX
+  BFD_RELOC_XTENSA_TLS_CALL
+ENUMDOC
+  Xtensa TLS relocations.
 
 ENUM
   BFD_RELOC_Z80_DISP8
Index: gas/config/tc-xtensa.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-xtensa.c,v
retrieving revision 1.102
diff -u -p -r1.102 tc-xtensa.c
--- gas/config/tc-xtensa.c	8 Aug 2008 18:21:26 -0000	1.102
+++ gas/config/tc-xtensa.c	20 Aug 2008 23:14:43 -0000
@@ -356,6 +356,11 @@ op_placement_info_table op_placement_tab
 #define O_hi16		O_md2	/* use high 16 bits of symbolic value */
 #define O_lo16		O_md3	/* use low 16 bits of symbolic value */
 #define O_pcrel		O_md4	/* value is a PC-relative offset */
+#define O_tlsfunc	O_md5	/* TLS_FUNC/TLSDESC_FN relocation */
+#define O_tlsarg	O_md6	/* TLS_ARG/TLSDESC_ARG relocation */
+#define O_tlscall	O_md7	/* TLS_CALL relocation */
+#define O_tpoff		O_md8	/* TPOFF relocation */
+#define O_dtpoff	O_md9	/* DTPOFF relocation */
 
 struct suffix_reloc_map
 {
@@ -373,6 +378,11 @@ static struct suffix_reloc_map suffix_re
   SUFFIX_MAP ("h",	BFD_RELOC_HI16,			O_hi16),
   SUFFIX_MAP ("plt",	BFD_RELOC_XTENSA_PLT,		O_pltrel),
   SUFFIX_MAP ("pcrel",	BFD_RELOC_32_PCREL,		O_pcrel),
+  SUFFIX_MAP ("tlsfunc", BFD_RELOC_XTENSA_TLS_FUNC,	O_tlsfunc),
+  SUFFIX_MAP ("tlsarg",	BFD_RELOC_XTENSA_TLS_ARG,	O_tlsarg),
+  SUFFIX_MAP ("tlscall", BFD_RELOC_XTENSA_TLS_CALL,	O_tlscall),
+  SUFFIX_MAP ("tpoff",	BFD_RELOC_XTENSA_TLS_TPOFF,	O_tpoff),
+  SUFFIX_MAP ("dtpoff",	BFD_RELOC_XTENSA_TLS_DTPOFF,	O_dtpoff),
   { (char *) 0, 0,	BFD_RELOC_UNUSED,		0 }
 };
 
@@ -1553,6 +1563,10 @@ xtensa_elf_cons (int nbytes)
 	  else if (nbytes != (int) bfd_get_reloc_size (reloc_howto))
 	    as_bad (_("%s relocations do not fit in %d bytes"),
 		    reloc_howto->name, nbytes);
+	  else if (reloc == BFD_RELOC_XTENSA_TLS_FUNC
+		   || reloc == BFD_RELOC_XTENSA_TLS_ARG
+		   || reloc == BFD_RELOC_XTENSA_TLS_CALL)
+	    as_bad (_("invalid use of %s relocation"), reloc_howto->name);
 	  else
 	    {
 	      char *p = frag_more ((int) nbytes);
@@ -1665,7 +1679,7 @@ map_suffix_reloc_to_operator (bfd_reloc_
 
 /* Find the matching reloc type.  */
 static bfd_reloc_code_real_type
-map_operator_to_reloc (unsigned char operator)
+map_operator_to_reloc (unsigned char operator, bfd_boolean is_literal)
 {
   struct suffix_reloc_map *sfx;
   bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED;
@@ -1679,6 +1693,14 @@ map_operator_to_reloc (unsigned char ope
 	}
     }
 
+  if (is_literal)
+    {
+      if (reloc == BFD_RELOC_XTENSA_TLS_FUNC)
+	return BFD_RELOC_XTENSA_TLSDESC_FN;
+      else if (reloc == BFD_RELOC_XTENSA_TLS_ARG)
+	return BFD_RELOC_XTENSA_TLSDESC_ARG;
+    }
+
   if (reloc == BFD_RELOC_UNUSED)
     return BFD_RELOC_32;
 
@@ -3149,6 +3171,10 @@ xg_valid_literal_expression (const expre
     case O_subtract:
     case O_pltrel:
     case O_pcrel:
+    case O_tlsfunc:
+    case O_tlsarg:
+    case O_tpoff:
+    case O_dtpoff:
       return TRUE;
     default:
       return FALSE;
@@ -3347,6 +3373,9 @@ xg_build_to_insn (TInsn *targ, TInsn *in
 	    case OP_LITERAL:
 	      sym = get_special_literal_symbol ();
 	      set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
+	      if (insn->tok[op_data].X_op == O_tlsfunc
+		  || insn->tok[op_data].X_op == O_tlsarg)
+		copy_expr (&targ->tls_reloc, &insn->tok[op_data]);
 	      break;
 	    case OP_LABEL:
 	      sym = get_special_label_symbol ();
@@ -4062,9 +4091,13 @@ xg_assemble_literal (/* const */ TInsn *
       pcrel = TRUE;
       /* fall through */
     case O_pltrel:
+    case O_tlsfunc:
+    case O_tlsarg:
+    case O_tpoff:
+    case O_dtpoff:
       p = frag_more (litsize);
       xtensa_set_frag_assembly_state (frag_now);
-      reloc = map_operator_to_reloc (emit_val->X_op);
+      reloc = map_operator_to_reloc (emit_val->X_op, TRUE);
       if (emit_val->X_add_symbol)
 	emit_val->X_op = O_symbol;
       else
@@ -5310,8 +5343,58 @@ md_assemble (char *str)
   orig_insn.insn_type = ITYPE_INSN;
   orig_insn.ntok = 0;
   orig_insn.is_specific_opcode = (has_underbar || !use_transform ());
-
   orig_insn.opcode = xtensa_opcode_lookup (isa, opname);
+
+  /* Special case: Check for "CALLXn.TLS" psuedo op.  If found, grab its
+     extra argument and set the opcode to "CALLXn".  */
+  if (orig_insn.opcode == XTENSA_UNDEFINED
+      && strncasecmp (opname, "callx", 5) == 0)
+    {
+      unsigned long window_size;
+      char *suffix;
+
+      window_size = strtoul (opname + 5, &suffix, 10);
+      if (suffix != opname + 5
+	  && (window_size == 0
+	      || window_size == 4
+	      || window_size == 8
+	      || window_size == 12)
+	  && strcasecmp (suffix, ".tls") == 0)
+	{
+	  switch (window_size)
+	    {
+	    case 0: orig_insn.opcode = xtensa_callx0_opcode; break;
+	    case 4: orig_insn.opcode = xtensa_callx4_opcode; break;
+	    case 8: orig_insn.opcode = xtensa_callx8_opcode; break;
+	    case 12: orig_insn.opcode = xtensa_callx12_opcode; break;
+	    }
+
+	  if (num_args != 2)
+	    as_bad (_("wrong number of operands for '%s'"), opname);
+	  else
+	    {
+	      bfd_reloc_code_real_type reloc;
+	      char *old_input_line_pointer;
+	      expressionS *tok = &orig_insn.tls_reloc;
+	      segT t;
+
+	      old_input_line_pointer = input_line_pointer;
+	      input_line_pointer = arg_strings[num_args - 1];
+
+	      t = expression (tok);
+	      if (tok->X_op == O_symbol
+		  && ((reloc = xtensa_elf_suffix (&input_line_pointer, tok))
+		      == BFD_RELOC_XTENSA_TLS_CALL))
+		tok->X_op = map_suffix_reloc_to_operator (reloc);
+	      else
+		as_bad (_("bad relocation expression for '%s'"), opname);
+
+	      input_line_pointer = old_input_line_pointer;
+	      num_args -= 1;
+	    }
+	}
+    }
+
   if (orig_insn.opcode == XTENSA_UNDEFINED)
     {
       xtensa_format fmt = xtensa_format_lookup (isa, opname);
@@ -5726,6 +5809,15 @@ md_apply_fix (fixS *fixP, valueT *valP, 
       fixP->fx_no_overflow = 0; /* Use the standard overflow check.  */
       break;
 
+    case BFD_RELOC_XTENSA_TLSDESC_FN:
+    case BFD_RELOC_XTENSA_TLSDESC_ARG:
+    case BFD_RELOC_XTENSA_TLS_TPOFF:
+    case BFD_RELOC_XTENSA_TLS_DTPOFF:
+      S_SET_THREAD_LOCAL (fixP->fx_addsy);
+      md_number_to_chars (fixpos, 0, fixP->fx_size);
+      fixP->fx_no_overflow = 0; /* Use the standard overflow check.  */
+      break;
+
     case BFD_RELOC_XTENSA_SLOT0_OP:
     case BFD_RELOC_XTENSA_SLOT1_OP:
     case BFD_RELOC_XTENSA_SLOT2_OP:
@@ -5766,6 +5858,9 @@ md_apply_fix (fixS *fixP, valueT *valP, 
       break;
 
     case BFD_RELOC_XTENSA_ASM_EXPAND:
+    case BFD_RELOC_XTENSA_TLS_FUNC:
+    case BFD_RELOC_XTENSA_TLS_ARG:
+    case BFD_RELOC_XTENSA_TLS_CALL:
     case BFD_RELOC_XTENSA_SLOT0_ALT:
     case BFD_RELOC_XTENSA_SLOT1_ALT:
     case BFD_RELOC_XTENSA_SLOT2_ALT:
@@ -6663,7 +6758,11 @@ emit_single_op (TInsn *orig_insn)
        || orig_insn->opcode == xtensa_movi_n_opcode)
       && !cur_vinsn.inside_bundle
       && (orig_insn->tok[1].X_op == O_symbol
-	  || orig_insn->tok[1].X_op == O_pltrel)
+	  || orig_insn->tok[1].X_op == O_pltrel
+	  || orig_insn->tok[1].X_op == O_tlsfunc
+	  || orig_insn->tok[1].X_op == O_tlsarg
+	  || orig_insn->tok[1].X_op == O_tpoff
+	  || orig_insn->tok[1].X_op == O_dtpoff)
       && !orig_insn->is_specific_opcode && use_transform ())
     xg_assembly_relax (&istack, orig_insn, now_seg, frag_now, 0, 1, 0);
   else
@@ -9527,7 +9626,7 @@ convert_frag_immed (segT segP,
 	      /* Add a fixup.  */
 	      target_seg = S_GET_SEGMENT (lit_sym);
 	      assert (target_seg);
-	      reloc_type = map_operator_to_reloc (tinsn->tok[0].X_op);
+	      reloc_type = map_operator_to_reloc (tinsn->tok[0].X_op, TRUE);
 	      fix_new_exp_in_seg (target_seg, 0, lit_frag, 0, 4,
 				  &tinsn->tok[0], FALSE, reloc_type);
 	      break;
@@ -11527,12 +11626,29 @@ vinsn_to_insnbuf (vliw_insn *vinsn,
   for (slot = 0; slot < vinsn->num_slots; slot++)
     {
       TInsn *tinsn = &vinsn->slots[slot];
+      expressionS *tls_reloc = &tinsn->tls_reloc;
       bfd_boolean tinsn_has_fixup =
 	tinsn_to_slotbuf (vinsn->format, slot, tinsn,
 			  vinsn->slotbuf[slot]);
 
       xtensa_format_set_slot (isa, fmt, slot,
 			      insnbuf, vinsn->slotbuf[slot]);
+      if (tls_reloc->X_op != O_illegal)
+	{
+	  if (vinsn->num_slots != 1)
+	    as_bad (_("TLS relocation not allowed in FLIX bundle"));
+	  else if (record_fixup)
+	    /* Instructions that generate TLS relocations should always be
+	       relaxed in the front-end.  If "record_fixup" is set, then this
+	       function is being called during back-end relaxation, so flag
+	       the unexpected behavior as an error.  */
+	    as_bad (_("unexpected TLS relocation"));
+	  else
+	    fix_new (fragP, frag_offset - fragP->fr_literal,
+		     xtensa_format_length (isa, fmt),
+		     tls_reloc->X_add_symbol, tls_reloc->X_add_number,
+		     FALSE, map_operator_to_reloc (tls_reloc->X_op, FALSE));
+	}
       if (tinsn_has_fixup)
 	{
 	  int i;
Index: gas/config/xtensa-istack.h
===================================================================
RCS file: /cvs/src/src/gas/config/xtensa-istack.h,v
retrieving revision 1.11
diff -u -p -r1.11 xtensa-istack.h
--- gas/config/xtensa-istack.h	19 Nov 2007 19:40:55 -0000	1.11
+++ gas/config/xtensa-istack.h	20 Aug 2008 23:14:43 -0000
@@ -1,5 +1,5 @@
 /* Declarations for stacks of tokenized Xtensa instructions.
-   Copyright (C) 2003, 2004, 2007 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2007, 2008 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
@@ -49,6 +49,8 @@ typedef struct tinsn_struct
   bfd_boolean loc_directive_seen;
   struct dwarf2_line_info debug_line;
 
+  expressionS tls_reloc;
+
   /* Filled out by relaxation_requirements:  */
   enum xtensa_relax_statesE subtype;
   int literal_space;
Index: gas/config/xtensa-relax.c
===================================================================
RCS file: /cvs/src/src/gas/config/xtensa-relax.c,v
retrieving revision 1.16
diff -u -p -r1.16 xtensa-relax.c
--- gas/config/xtensa-relax.c	3 Jul 2007 11:01:05 -0000	1.16
+++ gas/config/xtensa-relax.c	20 Aug 2008 23:14:44 -0000
@@ -1,5 +1,5 @@
 /* Table of relaxations for Xtensa assembly.
-   Copyright 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
+   Copyright 2003, 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
@@ -645,13 +645,13 @@ append_op (BuildInstr *bi, BuildOp *b_op
 
 
 static void
-append_literal_op (BuildInstr *bi, unsigned op1)
+append_literal_op (BuildInstr *bi, unsigned op1, unsigned src_op)
 {
   BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp));
 
   b_op->op_num = op1;
   b_op->typ = OP_LITERAL;
-  b_op->op_data = 0;
+  b_op->op_data = src_op;
   b_op->next = NULL;
   append_op (bi, b_op);
 }
@@ -1594,6 +1594,7 @@ build_transition (insn_pattern *initial_
   TransitionRule *tr = NULL;
   xtensa_opcode opcode;
   xtensa_isa isa = xtensa_default_isa;
+  BuildInstr *literal_bi;
 
   opname_map_e *op1;
   opname_map_e *op2;
@@ -1706,6 +1707,7 @@ build_transition (insn_pattern *initial_
      can be used before they are defined.  Also there are a number of
      special operands (e.g., HI24S).  */
 
+  literal_bi = NULL;
   for (r = replace_insns->head; r != NULL; r = r->next)
     {
       BuildInstr *bi;
@@ -1730,6 +1732,7 @@ build_transition (insn_pattern *initial_
 	  bi->typ = INSTR_LITERAL_DEF;
 	  if (operand_count != 1)
 	    as_fatal (_("expected one operand for generated literal"));
+	  literal_bi = bi;
 	}
       else if (strcmp (opcode_name, "LABEL") == 0)
 	{
@@ -1768,7 +1771,13 @@ build_transition (insn_pattern *initial_
 	  if (op_is_constant (op))
 	    append_constant_op (bi, op->operand_num, op_get_constant (op));
 	  else if (strcmp (op->operand_name, "%LITERAL") == 0)
-	    append_literal_op (bi, op->operand_num);
+	    {
+	      if (! literal_bi || ! literal_bi->ops || literal_bi->ops->next)
+		as_fatal (_("opcode '%s': cannot find literal definition"),
+			  opcode_name);
+	      append_literal_op (bi, op->operand_num,
+				 literal_bi->ops->op_data);
+	    }
 	  else if (strcmp (op->operand_name, "%LABEL") == 0)
 	    append_label_op (bi, op->operand_num);
 	  else if (op->operand_name[0] == 'a'
Index: gas/config/xtensa-relax.h
===================================================================
RCS file: /cvs/src/src/gas/config/xtensa-relax.h,v
retrieving revision 1.7
diff -u -p -r1.7 xtensa-relax.h
--- gas/config/xtensa-relax.h	3 Jul 2007 11:01:05 -0000	1.7
+++ gas/config/xtensa-relax.h	20 Aug 2008 23:14:44 -0000
@@ -1,5 +1,5 @@
 /* Table of relaxations for Xtensa assembly.
-   Copyright 2003, 2004, 2007 Free Software Foundation, Inc.
+   Copyright 2003, 2004, 2007, 2008 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
@@ -135,7 +135,9 @@ struct build_op
 				   OPERAND: op_data is the field in the
 				   source instruction to take the value from
 				   and encode in the op_num field here.
-				   LITERAL or LABEL: unused.  */
+				   LITERAL: op_data is field in the source
+				   instruction that is stored in the literal.
+				   LABEL: unused.  */
   BuildOp *next;
 };
 
Index: include/elf/xtensa.h
===================================================================
RCS file: /cvs/src/src/include/elf/xtensa.h,v
retrieving revision 1.6
diff -u -p -r1.6 xtensa.h
--- include/elf/xtensa.h	7 Dec 2007 22:52:08 -0000	1.6
+++ include/elf/xtensa.h	20 Aug 2008 23:14:44 -0000
@@ -1,5 +1,5 @@
 /* Xtensa ELF support for BFD.
-   Copyright 2003, 2004, 2007 Free Software Foundation, Inc.
+   Copyright 2003, 2004, 2007, 2008 Free Software Foundation, Inc.
    Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
 
    This file is part of BFD, the Binary File Descriptor library.
@@ -76,6 +76,13 @@ START_RELOC_NUMBERS (elf_xtensa_reloc_ty
      RELOC_NUMBER (R_XTENSA_SLOT12_ALT, 47)
      RELOC_NUMBER (R_XTENSA_SLOT13_ALT, 48)
      RELOC_NUMBER (R_XTENSA_SLOT14_ALT, 49)
+     RELOC_NUMBER (R_XTENSA_TLSDESC_FN, 50)
+     RELOC_NUMBER (R_XTENSA_TLSDESC_ARG, 51)
+     RELOC_NUMBER (R_XTENSA_TLS_DTPOFF, 52)
+     RELOC_NUMBER (R_XTENSA_TLS_TPOFF, 53)
+     RELOC_NUMBER (R_XTENSA_TLS_FUNC, 54)
+     RELOC_NUMBER (R_XTENSA_TLS_ARG, 55)
+     RELOC_NUMBER (R_XTENSA_TLS_CALL, 56)
 END_RELOC_NUMBERS (R_XTENSA_max)
 
 /* Processor-specific flags for the ELF header e_flags field.  */
Index: ld/testsuite/ld-xtensa/tlsbin.dd
===================================================================
RCS file: ld/testsuite/ld-xtensa/tlsbin.dd
diff -N ld/testsuite/ld-xtensa/tlsbin.dd
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-xtensa/tlsbin.dd	20 Aug 2008 23:17:01 -0000
@@ -0,0 +1,65 @@
+#source: tlsbin.s
+#as:
+#ld: -melf32xtensa
+#objdump: -drj.text --start-address=0x400238
+#target: xtensa*-*-linux*
+
+.*: +file format elf32-xtensa-.e
+
+Disassembly of section \.text:
+
+0+400238 <_start>:
+  [0-9a-f]+:	[0-9a-f]+[ 	]+entry	a1, 32
+#  GD -> IE because variable is not defined in executable
+  [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a8
+  [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a10, 4001ec <.*>
+  [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a10, a10, a8
+#  GD -> IE because variable is not defined in executable where
+#  the variable is referenced through IE too
+  [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a8
+  [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a10, 4001f4 <.*>
+  [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a10, a10, a8
+#  GD -> LE with global variable defined in executable
+  [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a8
+  [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a10, 4001fc <.*>
+  [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a10, a10, a8
+#  GD -> LE with local variable defined in executable
+  [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a8
+  [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a10, 400204 <.*>
+  [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a10, a10, a8
+#  GD -> LE with hidden variable defined in executable
+  [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a8
+  [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a10, 40020c <.*>
+  [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a10, a10, a8
+#  LD -> LE
+  [0-9a-f]+:	[0-9a-f]+[ 	]+nop.*
+  [0-9a-f]+:	[0-9a-f]+[ 	]+nop.*
+  [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a10
+  [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a12, 400218 <.*>
+  [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a12, a12, a10
+  [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a13, 40021c <.*>
+  [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a13, a13, a10
+#  LD -> LE against hidden variables
+  [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a12, 400220 <.*>
+  [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a12, a12, a10
+  [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a13, 400224 <.*>
+  [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a13, a13, a10
+#
+#  IE against global var
+  [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a2
+  [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a3, 400228 <.*>
+  [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a3, a3, a2
+#  IE -> LE against global var defined in exec
+  [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a4
+  [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a5, 40022c <.*>
+  [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a5, a5, a4
+#  IE -> LE against local var
+  [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a6
+  [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a7, 400230 <.*>
+  [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a7, a7, a6
+#  IE -> LE against hidden var
+  [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a8
+  [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a9, 400234 <.*>
+  [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a9, a9, a8
+#
+  [0-9a-f]+:	[0-9a-f]+[ 	]+retw.*
Index: ld/testsuite/ld-xtensa/tlsbin.rd
===================================================================
RCS file: ld/testsuite/ld-xtensa/tlsbin.rd
diff -N ld/testsuite/ld-xtensa/tlsbin.rd
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-xtensa/tlsbin.rd	20 Aug 2008 23:17:01 -0000
@@ -0,0 +1,118 @@
+#source: tlsbin.s
+#as:
+#ld: -melf32xtensa
+#readelf: -WSsrl
+#target: xtensa*-*-linux*
+
+There are [0-9]+ section headers, starting at offset 0x[0-9a-f]+:
+
+Section Headers:
+  \[Nr\] Name +Type +Addr +Off +Size +ES Flg Lk Inf Al
+ +\[[ 0-9]+\] +NULL +0+ 0+ 0+ 00 +0 +0 +0
+ +\[[ 0-9]+\] .interp +.*
+ +\[[ 0-9]+\] .hash +.*
+ +\[[ 0-9]+\] .dynsym +.*
+ +\[[ 0-9]+\] .dynstr +.*
+ +\[[ 0-9]+\] .rela.dyn +.*
+ +\[[ 0-9]+\] .text +PROGBITS +[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 +AX +0 +0 +4
+ +\[[ 0-9]+\] .got.loc +PROGBITS +[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 +A +0 +0 +4
+ +\[[ 0-9]+\] .tdata +PROGBITS +[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 +WAT +0 +0 +4
+ +\[[ 0-9]+\] .dynamic +DYNAMIC +[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 08 +WA +4 +0 +4
+ +\[[ 0-9]+\] .got +PROGBITS +[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 +WA +0 +0 +4
+ +\[[ 0-9]+\] .xt.lit +PROGBITS +0+ .*
+ +\[[ 0-9]+\] .xt.prop +PROGBITS +0+ .*
+ +\[[ 0-9]+\] .xtensa.info +NOTE +0+ .*
+ +\[[ 0-9]+\] .shstrtab +.*
+ +\[[ 0-9]+\] .symtab +.*
+ +\[[ 0-9]+\] .strtab +.*
+Key to Flags:
+.*
+.*
+.*
+
+Elf file type is EXEC \(Executable file\)
+Entry point 0x[0-9a-f]+
+There are [0-9]+ program headers, starting at offset [0-9]+
+
+Program Headers:
+  Type +Offset +VirtAddr +PhysAddr +FileSiz +MemSiz +Flg Align
+  PHDR.*
+  INTERP.*
+.*Requesting program interpreter.*
+  LOAD +0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ R E 0x1000
+  LOAD +0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ RW  0x1000
+  DYNAMIC +0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ RW +0x4
+  TLS +0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ R +0x4
+
+ Section to Segment mapping:
+  Segment Sections...
+   00 *
+   01 +.interp *
+   02 +.interp .hash .dynsym .dynstr .rela.dyn .text .got.loc *
+   03 +.tdata .dynamic .got *
+   04 +.dynamic *
+   05 +.tdata *
+
+Relocation section '.rela.dyn' at offset 0x[0-9a-f]+ contains 3 entries:
+ +Offset +Info +Type +Sym\. Value  Symbol's Name \+ Addend
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLS_TPOFF +0+ +sG1 \+ 0
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLS_TPOFF +0+ +sG2 \+ 0
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLS_TPOFF +0+ +sG2 \+ 0
+
+Symbol table '\.dynsym' contains [0-9]+ entries:
+ +Num: +Value +Size Type +Bind +Vis +Ndx Name
+ +[0-9]+: 0+ +0 NOTYPE  LOCAL  DEFAULT  UND *
+ +[0-9]+: 0+ +0 TLS +GLOBAL DEFAULT  UND sG2
+ +[0-9]+: 0+[0-9a-f]+ +0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
+ +[0-9]+: 0+ +0 TLS +GLOBAL DEFAULT  UND sG1
+ +[0-9]+: 0+[0-9a-f]+ +0 NOTYPE  GLOBAL DEFAULT  ABS _edata
+ +[0-9]+: 0+[0-9a-f]+ +0 NOTYPE  GLOBAL DEFAULT  ABS _end
+
+Symbol table '\.symtab' contains [0-9]+ entries:
+ +Num: +Value +Size Type +Bind +Vis +Ndx Name
+ +[0-9]+: 0+ +0 NOTYPE  LOCAL  DEFAULT  UND *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +1 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +2 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +3 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +4 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +5 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +6 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +7 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +8 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +9 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +10 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +11 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +12 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +13 *
+ +[0-9]+: [0-9a-f]+ +0 TLS +LOCAL  DEFAULT +8 sl1
+ +[0-9]+: [0-9a-f]+ +0 TLS +LOCAL  DEFAULT +8 sl2
+ +[0-9]+: [0-9a-f]+ +0 TLS +LOCAL  DEFAULT +8 sl3
+ +[0-9]+: [0-9a-f]+ +0 TLS +LOCAL  DEFAULT +8 sl4
+ +[0-9]+: [0-9a-f]+ +0 TLS +LOCAL  DEFAULT +8 sl5
+ +[0-9]+: [0-9a-f]+ +0 TLS +LOCAL  DEFAULT +8 sl6
+ +[0-9]+: [0-9a-f]+ +0 TLS +LOCAL  DEFAULT +8 sl7
+ +[0-9]+: [0-9a-f]+ +0 TLS +LOCAL  DEFAULT +8 sl8
+ +[0-9]+: 0+ +0 TLS +LOCAL  HIDDEN +8 _TLS_MODULE_BASE_
+ +[0-9]+: [0-9a-f]+ +0 OBJECT +LOCAL +HIDDEN +9 _DYNAMIC
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL DEFAULT +8 sg8
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL DEFAULT +8 sg3
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL HIDDEN +8 sh3
+ +[0-9]+: 0+ +0 TLS +GLOBAL DEFAULT  UND sG2
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL DEFAULT +8 sg4
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL DEFAULT +8 sg5
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL HIDDEN +8 sh7
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL HIDDEN +8 sh8
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL DEFAULT +8 sg1
+ +[0-9]+: [0-9a-f]+ +0 FUNC +GLOBAL DEFAULT +6 _start
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL HIDDEN +8 sh4
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL HIDDEN +8 sh5
+ +[0-9]+: [0-9a-f]+ +0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL DEFAULT +8 sg2
+ +[0-9]+: 0+ +0 TLS +GLOBAL DEFAULT  UND sG1
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL HIDDEN +8 sh1
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL DEFAULT +8 sg6
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL DEFAULT +8 sg7
+ +[0-9]+: [0-9a-f]+ +0 NOTYPE  GLOBAL DEFAULT  ABS _edata
+ +[0-9]+: [0-9a-f]+ +0 NOTYPE  GLOBAL DEFAULT  ABS _end
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL HIDDEN +8 sh2
+ +[0-9]+: [0-9a-f]+ +0 TLS +GLOBAL HIDDEN +8 sh6
Index: ld/testsuite/ld-xtensa/tlsbin.s
===================================================================
RCS file: ld/testsuite/ld-xtensa/tlsbin.s
diff -N ld/testsuite/ld-xtensa/tlsbin.s
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-xtensa/tlsbin.s	20 Aug 2008 23:17:01 -0000
@@ -0,0 +1,98 @@
+	.section ".tdata", "awT", @progbits
+	.global sg1, sg2, sg3, sg4, sg5, sg6, sg7, sg8
+	.global sh1, sh2, sh3, sh4, sh5, sh6, sh7, sh8
+	.hidden sh1, sh2, sh3, sh4, sh5, sh6, sh7, sh8
+	.align	4
+sg1:	.long	17
+sg2:	.long	18
+sg3:	.long	19
+sg4:	.long	20
+sg5:	.long	21
+sg6:	.long	22
+sg7:	.long	23
+sg8:	.long	24
+sl1:	.long	65
+sl2:	.long	66
+sl3:	.long	67
+sl4:	.long	68
+sl5:	.long	69
+sl6:	.long	70
+sl7:	.long	71
+sl8:	.long	72
+sh1:	.long	157
+sh2:	.long	158
+sh3:	.long	159
+sh4:	.long	160
+sh5:	.long	161
+sh6:	.long	162
+sh7:	.long	163
+sh8:	.long	164
+
+	.text
+	.global	_start
+	.type	_start, @function
+_start:
+	entry	sp, 32
+
+	/* GD -> IE because variable is not defined in executable */
+	movi	a8, sG1@tlsfunc
+	movi	a10, sG1@tlsarg
+	callx8.tls a8, sG1@tlscall
+	
+	/* GD -> IE because variable is not defined in executable where
+	   the variable is referenced through IE too */
+	movi	a8, sG2@tlsfunc
+	movi	a10, sG2@tlsarg
+	callx8.tls a8, sG2@tlscall
+
+	/* GD -> LE with global variable defined in executable */
+	movi	a8, sg1@tlsfunc
+	movi	a10, sg1@tlsarg
+	callx8.tls a8, sg1@tlscall
+
+	/* GD -> LE with local variable defined in executable */
+	movi	a8, sl1@tlsfunc
+	movi	a10, sl1@tlsarg
+	callx8.tls a8, sl1@tlscall
+
+	/* GD -> LE with hidden variable defined in executable */
+	movi	a8, sh1@tlsfunc
+	movi	a10, sh1@tlsarg
+	callx8.tls a8, sh1@tlscall
+
+	/* LD -> LE */
+	movi   a8, _TLS_MODULE_BASE_@tlsfunc
+	movi   a10, _TLS_MODULE_BASE_@tlsarg
+	callx8.tls a8, _TLS_MODULE_BASE_@tlscall
+	movi   a12, 1+sl1@dtpoff
+	add    a12, a12, a10
+	movi   a13, sl2@dtpoff+2
+	add    a13, a13, a10
+	
+	/* LD -> LE against hidden variables */
+	movi   a12, sh1@dtpoff
+	add    a12, a12, a10
+	movi   a13, 3+sh2@dtpoff
+	add    a13, a13, a10
+
+	/* IE against global var */
+	rur	a2, THREADPTR
+	movi	a3, sG2@tpoff
+	add	a3, a3, a2
+
+	/* IE -> LE against global var defined in exec */
+	rur	a4, THREADPTR
+	movi	a5, sg1@tpoff
+	add	a5, a5, a4
+
+	/* IE -> LE against local var */
+	rur	a6, THREADPTR
+	movi	a7, sl1@tpoff
+	add	a7, a7, a6
+
+	/* IE -> LE against hidden var */
+	rur	a8, THREADPTR
+	movi	a9, sh1@tpoff
+	add	a9, a9, a8
+
+	retw
Index: ld/testsuite/ld-xtensa/tlsbin.sd
===================================================================
RCS file: ld/testsuite/ld-xtensa/tlsbin.sd
diff -N ld/testsuite/ld-xtensa/tlsbin.sd
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-xtensa/tlsbin.sd	20 Aug 2008 23:17:01 -0000
@@ -0,0 +1,14 @@
+#source: tlsbin.s
+#as:
+#ld: -melf32xtensa
+#objdump: -sj.text --stop-address=0x400238
+#target: xtensa*-*-linux*
+
+.*: +file format elf32-xtensa-.e
+
+Contents of section .text:
+ *[0-9a-f]+ 0+ 0+ 0+ 0+  .*
+ *[0-9a-f]+ 0+ 0*080* 0+ 0*280*  .*
+ *[0-9a-f]+ 0+ 0*480* 0+ 0*080*  .*
+ *[0-9a-f]+ 0*290* 0*2e0* 0*480* 0*4f0*  .*
+ *[0-9a-f]+ 0+ 0*080* 0*280* 0*480*  .*
Index: ld/testsuite/ld-xtensa/tlsbin.td
===================================================================
RCS file: ld/testsuite/ld-xtensa/tlsbin.td
diff -N ld/testsuite/ld-xtensa/tlsbin.td
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-xtensa/tlsbin.td	20 Aug 2008 23:17:01 -0000
@@ -0,0 +1,14 @@
+#source: tlsbin.s
+#ld: -melf32xtensa
+#objdump: -sj.tdata
+#target: xtensa*-*-linux*
+
+.*: +file format elf32-xtensa-.e
+
+Contents of section .tdata:
+ *[0-9a-f]+ 0*110* 0*120* 0*130* 0*140*  .*
+ *[0-9a-f]+ 0*150* 0*160* 0*170* 0*180*  .*
+ *[0-9a-f]+ 0*410* 0*420* 0*430* 0*440*  .*
+ *[0-9a-f]+ 0*450* 0*460* 0*470* 0*480*  .*
+ *[0-9a-f]+ 0*9d0* 0*9e0* 0*9f0* 0*a00*  .*
+ *[0-9a-f]+ 0*a10* 0*a20* 0*a30* 0*a40*  .*
Index: ld/testsuite/ld-xtensa/tlslib.s
===================================================================
RCS file: ld/testsuite/ld-xtensa/tlslib.s
diff -N ld/testsuite/ld-xtensa/tlslib.s
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-xtensa/tlslib.s	20 Aug 2008 23:17:01 -0000
@@ -0,0 +1,18 @@
+	.section ".tdata", "awT", @progbits
+	.global	sG1, sG2, sG3, sG4, sG5, sG6, sG7, sG8
+sG1:	.long	513
+sG2:	.long	514
+sG3:	.long	515
+sG4:	.long	516
+sG5:	.long	517
+sG6:	.long	518
+sG7:	.long	519
+sG8:	.long	520
+
+	.text
+	/* Dummy.  */
+	.global	__tls_get_addr
+	.type	__tls_get_addr, @function
+__tls_get_addr:
+	entry	sp, 16
+	retw
Index: ld/testsuite/ld-xtensa/tlspic.dd
===================================================================
RCS file: ld/testsuite/ld-xtensa/tlspic.dd
diff -N ld/testsuite/ld-xtensa/tlspic.dd
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-xtensa/tlspic.dd	20 Aug 2008 23:17:01 -0000
@@ -0,0 +1,81 @@
+#source: tlspic1.s
+#source: tlspic2.s
+#as:
+#ld: -shared -melf32xtensa
+#objdump: -drj.text --start-address=0x350
+#target: xtensa*-*-linux*
+
+.*: +file format elf32-xtensa-.e
+
+Disassembly of section \.text:
+
+0+350 <_start>:
+ [0-9a-f]+:	[0-9a-f]+[ 	]+entry	a1, 32
+#  GD
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a8, 2e0 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a10, 2e4 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+callx8	a8
+#  GD -> IE because variable is referenced through IE too
+ [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a8
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a10, 2ec <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a10, a10, a8
+#  GD against local variable
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a8, 2f0 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a10, 2f4 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+callx8	a8
+#  GD -> IE against local variable referenced through IE too
+ [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a8
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a10, 2fc <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a10, a10, a8
+#  GD against hidden and local variable
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a8, 300 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a10, 304 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+callx8	a8
+#  GD -> IE against hidden and local variable referenced through IE too
+ [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a8
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a10, 30c <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a10, a10, a8
+#  GD against hidden but not local variable
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a8, 310 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a10, 314 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+callx8	a8
+#  GD -> IE against hidden but not local variable referenced through IE too
+ [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a8
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a10, 31c <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a10, a10, a8
+#  LD
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a8, 320 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a10, 324 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+callx8	a8
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a12, 328 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a12, a12, a10
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a13, 32c <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a13, a13, a10
+#  LD against hidden and local variables
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a12, 330 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a12, a12, a10
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a13, 334 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a13, a13, a10
+#  LD against hidden but not local variables
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a12, 338 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a12, a12, a10
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a13, 33c <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a13, a13, a10
+#  IE against global var
+ [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a2
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a3, 340 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a3, a3, a2
+#  IE against local var
+ [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a4
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a5, 344 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a5, a5, a4
+#  IE against hidden and local var
+ [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a6
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a7, 348 <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a7, a7, a6
+#  IE against hidden but not local var
+ [0-9a-f]+:	[0-9a-f]+[ 	]+rur.threadptr	a8
+ [0-9a-f]+:	[0-9a-f]+[ 	]+l32r	a9, 34c <.*>
+ [0-9a-f]+:	[0-9a-f]+[ 	]+add.*	a9, a9, a8
+#
+ [0-9a-f]+:	[0-9a-f]+[ 	]+retw.*
Index: ld/testsuite/ld-xtensa/tlspic.rd
===================================================================
RCS file: ld/testsuite/ld-xtensa/tlspic.rd
diff -N ld/testsuite/ld-xtensa/tlspic.rd
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-xtensa/tlspic.rd	20 Aug 2008 23:17:01 -0000
@@ -0,0 +1,142 @@
+#source: tlspic1.s
+#source: tlspic2.s
+#as:
+#ld: -shared -melf32xtensa
+#readelf: -WSsrl
+#target: xtensa*-*-linux*
+
+There are [0-9]+ section headers, starting at offset 0x[0-9a-f]+:
+
+Section Headers:
+  \[Nr\] Name +Type +Addr +Off +Size +ES Flg Lk Inf Al
+ +\[[ 0-9]+\] +NULL +0+ 0+ 0+ 00 +0 +0 +0
+ +\[[ 0-9]+\] .hash +.*
+ +\[[ 0-9]+\] .dynsym +.*
+ +\[[ 0-9]+\] .dynstr +.*
+ +\[[ 0-9]+\] .rela.dyn +.*
+ +\[[ 0-9]+\] .text +PROGBITS +[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 +AX +0 +0 +4
+ +\[[ 0-9]+\] .got.loc +PROGBITS +[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 +A +0 +0 +4
+ +\[[ 0-9]+\] .tdata +PROGBITS +[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 +WAT +0 +0 +4
+ +\[[ 0-9]+\] .tbss +NOBITS +[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 +WAT +0 +0 +4
+ +\[[ 0-9]+\] .dynamic +DYNAMIC +[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 08 +WA +3 +0 +4
+ +\[[ 0-9]+\] .got +PROGBITS +[0-9a-f]+ [0-9a-f]+ [0-9a-f]+ 00 +WA +0 +0 +4
+ +\[[ 0-9]+\] .xt.lit +PROGBITS +0+ .*
+ +\[[ 0-9]+\] .xt.prop +PROGBITS +0+ .*
+ +\[[ 0-9]+\] .xtensa.info +NOTE +0+ .*
+ +\[[ 0-9]+\] .shstrtab +.*
+ +\[[ 0-9]+\] .symtab +.*
+ +\[[ 0-9]+\] .strtab +.*
+Key to Flags:
+.*
+.*
+.*
+
+Elf file type is DYN \(Shared object file\)
+Entry point 0x[0-9a-f]+
+There are [0-9]+ program headers, starting at offset [0-9]+
+
+Program Headers:
+  Type +Offset +VirtAddr +PhysAddr +FileSiz +MemSiz +Flg Align
+  LOAD +0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ R E 0x1000
+  LOAD +0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ RW  0x1000
+  DYNAMIC +0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ RW +0x4
+  TLS +0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ 0x[0-9a-f]+ R +0x4
+
+ Section to Segment mapping:
+  Segment Sections...
+   00 +.hash .dynsym .dynstr .rela.dyn .text .got.loc *
+   01 +.tdata .dynamic .got *
+   02 +.dynamic *
+   03 +.tdata .tbss *
+
+Relocation section '.rela.dyn' at offset 0x[0-9a-f]+ contains 18 entries:
+ +Offset +Info +Type +Sym\. Value  Symbol's Name \+ Addend
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLSDESC_FN +0+ +sg1 \+ 0
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLSDESC_ARG +0+ +sg1 \+ 0
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLS_TPOFF +0+4 +sg2 \+ 0
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLS_TPOFF +0+4 +sg2 \+ 0
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLSDESC_FN +0+20
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLSDESC_ARG +0+20
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLS_TPOFF +0+24
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLSDESC_FN +0+40
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLSDESC_ARG +0+40
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLS_TPOFF +0+44
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLSDESC_FN +0+60
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLSDESC_ARG +0+60
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLS_TPOFF +0+64
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLSDESC_FN +0+
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLSDESC_ARG +0+
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLS_TPOFF +0+24
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLS_TPOFF +0+44
+[0-9a-f]+  [0-9a-f]+ R_XTENSA_TLS_TPOFF +0+64
+
+Symbol table '\.dynsym' contains [0-9]+ entries:
+ +Num: +Value +Size Type +Bind +Vis +Ndx Name
+ +[0-9]+: 0+ +0 NOTYPE  LOCAL  DEFAULT  UND *
+ +[0-9]+: 0+1c +0 TLS +GLOBAL DEFAULT +7 sg8
+ +[0-9]+: 0+8 +0 TLS +GLOBAL DEFAULT +7 sg3
+ +[0-9]+: 0+c +0 TLS +GLOBAL DEFAULT +7 sg4
+ +[0-9]+: 0+10 +0 TLS +GLOBAL DEFAULT +7 sg5
+ +[0-9]+: 0+ +0 TLS +GLOBAL DEFAULT +7 sg1
+ +[0-9]+: 0+350 +0 FUNC +GLOBAL DEFAULT +5 _start
+ +[0-9]+: [0-9a-f]+ +0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
+ +[0-9]+: 0+4 +0 TLS +GLOBAL DEFAULT +7 sg2
+ +[0-9]+: 0+14 +0 TLS +GLOBAL DEFAULT +7 sg6
+ +[0-9]+: 0+18 +0 TLS +GLOBAL DEFAULT +7 sg7
+ +[0-9]+: [0-9a-f]+ +0 NOTYPE  GLOBAL DEFAULT  ABS _edata
+ +[0-9]+: [0-9a-f]+ +0 NOTYPE  GLOBAL DEFAULT  ABS _end
+
+Symbol table '\.symtab' contains [0-9]+ entries:
+ +Num: +Value +Size Type +Bind +Vis +Ndx Name
+ +[0-9]+: 0+ +0 NOTYPE  LOCAL  DEFAULT  UND *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +1 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +2 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +3 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +4 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +5 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +6 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +7 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +8 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +9 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +10 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +11 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +12 *
+ +[0-9]+: [0-9a-f]+ +0 SECTION LOCAL  DEFAULT +13 *
+ +[0-9]+: 0+20 +0 TLS +LOCAL  DEFAULT +7 sl1
+ +[0-9]+: 0+24 +0 TLS +LOCAL  DEFAULT +7 sl2
+ +[0-9]+: 0+28 +0 TLS +LOCAL  DEFAULT +7 sl3
+ +[0-9]+: 0+2c +0 TLS +LOCAL  DEFAULT +7 sl4
+ +[0-9]+: 0+30 +0 TLS +LOCAL  DEFAULT +7 sl5
+ +[0-9]+: 0+34 +0 TLS +LOCAL  DEFAULT +7 sl6
+ +[0-9]+: 0+38 +0 TLS +LOCAL  DEFAULT +7 sl7
+ +[0-9]+: 0+3c +0 TLS +LOCAL  DEFAULT +7 sl8
+ +[0-9]+: 0+60 +0 TLS +LOCAL  HIDDEN +8 sH1
+ +[0-9]+: 0+ +0 TLS +LOCAL  HIDDEN +7 _TLS_MODULE_BASE_
+ +[0-9]+: 0+144c +0 OBJECT  LOCAL  HIDDEN  ABS _DYNAMIC
+ +[0-9]+: 0+48 +0 TLS +LOCAL  HIDDEN +7 sh3
+ +[0-9]+: 0+64 +0 TLS +LOCAL  HIDDEN +8 sH2
+ +[0-9]+: 0+78 +0 TLS +LOCAL  HIDDEN +8 sH7
+ +[0-9]+: 0+58 +0 TLS +LOCAL  HIDDEN +7 sh7
+ +[0-9]+: 0+5c +0 TLS +LOCAL  HIDDEN +7 sh8
+ +[0-9]+: 0+6c +0 TLS +LOCAL  HIDDEN +8 sH4
+ +[0-9]+: 0+4c +0 TLS +LOCAL  HIDDEN +7 sh4
+ +[0-9]+: 0+68 +0 TLS +LOCAL  HIDDEN +8 sH3
+ +[0-9]+: 0+50 +0 TLS +LOCAL  HIDDEN +7 sh5
+ +[0-9]+: 0+70 +0 TLS +LOCAL  HIDDEN +8 sH5
+ +[0-9]+: 0+74 +0 TLS +LOCAL  HIDDEN +8 sH6
+ +[0-9]+: 0+7c +0 TLS +LOCAL  HIDDEN +8 sH8
+ +[0-9]+: 0+40 +0 TLS +LOCAL  HIDDEN +7 sh1
+ +[0-9]+: 0+44 +0 TLS +LOCAL  HIDDEN +7 sh2
+ +[0-9]+: 0+54 +0 TLS +LOCAL  HIDDEN +7 sh6
+ +[0-9]+: 0+1c +0 TLS +GLOBAL DEFAULT +7 sg8
+ +[0-9]+: 0+8 +0 TLS +GLOBAL DEFAULT +7 sg3
+ +[0-9]+: 0+c +0 TLS +GLOBAL DEFAULT +7 sg4
+ +[0-9]+: 0+10 +0 TLS +GLOBAL DEFAULT +7 sg5
+ +[0-9]+: 0+ +0 TLS +GLOBAL DEFAULT +7 sg1
+ +[0-9]+: 0+350 +0 FUNC +GLOBAL DEFAULT +5 _start
+ +[0-9]+: [0-9a-f]+ +0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
+ +[0-9]+: 0+4 +0 TLS +GLOBAL DEFAULT +7 sg2
+ +[0-9]+: 0+14 +0 TLS +GLOBAL DEFAULT +7 sg6
+ +[0-9]+: 0+18 +0 TLS +GLOBAL DEFAULT +7 sg7
+ +[0-9]+: [0-9a-f]+ +0 NOTYPE  GLOBAL DEFAULT  ABS _edata
+ +[0-9]+: [0-9a-f]+ +0 NOTYPE  GLOBAL DEFAULT  ABS _end
Index: ld/testsuite/ld-xtensa/tlspic.sd
===================================================================
RCS file: ld/testsuite/ld-xtensa/tlspic.sd
diff -N ld/testsuite/ld-xtensa/tlspic.sd
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-xtensa/tlspic.sd	20 Aug 2008 23:17:01 -0000
@@ -0,0 +1,17 @@
+#source: tlspic1.s
+#source: tlspic2.s
+#as:
+#ld: -shared -melf32xtensa
+#objdump: -sj.text --stop-address=0x350
+#target: xtensa*-*-linux*
+
+.*: +file format elf32-xtensa-.e
+
+Contents of section .text:
+ 0+2e0 0+ 0+ 0+ 0+  .*
+ 0+2f0 0+ 0+ 0+ 0+  .*
+ 0+300 0+ 0+ 0+ 0+  .*
+ 0+310 0+ 0+ 0+ 0+  .*
+ 0+320 0+ 0+ 0*200* 0*260*  .*
+ 0+330 0*400* 0*470* 0*600* 0*650*  .*
+ 0+340 0+ 0+ 0+ 0+  .*
Index: ld/testsuite/ld-xtensa/tlspic.td
===================================================================
RCS file: ld/testsuite/ld-xtensa/tlspic.td
diff -N ld/testsuite/ld-xtensa/tlspic.td
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-xtensa/tlspic.td	20 Aug 2008 23:17:01 -0000
@@ -0,0 +1,16 @@
+#source: tlspic1.s
+#source: tlspic2.s
+#as:
+#ld: -shared -melf32xtensa
+#objdump: -sj.tdata
+#target: xtensa*-*-linux*
+
+.*: +file format elf32-xtensa-.e
+
+Contents of section .tdata:
+ *[0-9a-f]+ 0*110* 0*120* 0*130* 0*140*  .*
+ *[0-9a-f]+ 0*150* 0*160* 0*170* 0*180*  .*
+ *[0-9a-f]+ 0*410* 0*420* 0*430* 0*440*  .*
+ *[0-9a-f]+ 0*450* 0*460* 0*470* 0*480*  .*
+ *[0-9a-f]+ 0*9d0* 0*9e0* 0*9f0* 0*a00*  .*
+ *[0-9a-f]+ 0*a10* 0*a20* 0*a30* 0*a40*  .*
Index: ld/testsuite/ld-xtensa/tlspic1.s
===================================================================
RCS file: ld/testsuite/ld-xtensa/tlspic1.s
diff -N ld/testsuite/ld-xtensa/tlspic1.s
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-xtensa/tlspic1.s	20 Aug 2008 23:17:01 -0000
@@ -0,0 +1,120 @@
+	.section ".tdata", "awT", @progbits
+	.global sg1, sg2, sg3, sg4, sg5, sg6, sg7, sg8
+	.global sh1, sh2, sh3, sh4, sh5, sh6, sh7, sh8
+	.hidden sh1, sh2, sh3, sh4, sh5, sh6, sh7, sh8
+	.align	4
+sg1:	.long	17
+sg2:	.long	18
+sg3:	.long	19
+sg4:	.long	20
+sg5:	.long	21
+sg6:	.long	22
+sg7:	.long	23
+sg8:	.long	24
+sl1:	.long	65
+sl2:	.long	66
+sl3:	.long	67
+sl4:	.long	68
+sl5:	.long	69
+sl6:	.long	70
+sl7:	.long	71
+sl8:	.long	72
+sh1:	.long	157
+sh2:	.long	158
+sh3:	.long	159
+sh4:	.long	160
+sh5:	.long	161
+sh6:	.long	162
+sh7:	.long	163
+sh8:	.long	164
+
+	.text
+	.global	_start
+	.type	_start, @function
+_start:
+	entry	sp, 32
+
+	/* GD */
+	movi	a8, sg1@tlsfunc
+	movi	a10, sg1@tlsarg
+	callx8.tls a8, sg1@tlscall
+
+	/* GD -> IE because variable is referenced through IE too */
+	movi	a8, sg2@tlsfunc
+	movi	a10, sg2@tlsarg
+	callx8.tls a8, sg2@tlscall
+
+	/* GD against local variable */
+	movi	a8, sl1@tlsfunc
+	movi	a10, sl1@tlsarg
+	callx8.tls a8, sl1@tlscall
+
+	/* GD -> IE against local variable referenced through IE too */
+	movi	a8, sl2@tlsfunc
+	movi	a10, sl2@tlsarg
+	callx8.tls a8, sl2@tlscall
+
+	/* GD against hidden and local variable */
+	movi	a8, sh1@tlsfunc
+	movi	a10, sh1@tlsarg
+	callx8.tls a8, sh1@tlscall
+
+	/* GD -> IE against hidden and local variable referenced through
+	   IE too */
+	movi	a8, sh2@tlsfunc
+	movi	a10, sh2@tlsarg
+	callx8.tls a8, sh2@tlscall
+
+	/* GD against hidden but not local variable */
+	movi	a8, sH1@tlsfunc
+	movi	a10, sH1@tlsarg
+	callx8.tls a8, sH1@tlscall
+
+	/* GD -> IE against hidden but not local variable referenced through
+	   IE too */
+	movi	a8, sH2@tlsfunc
+	movi	a10, sH2@tlsarg
+	callx8.tls a8, sH2@tlscall
+
+	/* LD */
+	movi   a8, _TLS_MODULE_BASE_@tlsfunc
+	movi   a10, _TLS_MODULE_BASE_@tlsarg
+	callx8.tls a8, _TLS_MODULE_BASE_@tlscall
+	movi   a12, sl1@dtpoff
+	add    a12, a12, a10
+	movi   a13, 2+sl2@dtpoff
+	add    a13, a13, a10
+
+	/* LD against hidden and local variables */
+	movi   a12, sh1@dtpoff
+	add    a12, a12, a10
+	movi   a13, sh2@dtpoff+3
+	add    a13, a13, a10
+
+	/* LD against hidden but not local variables */
+	movi   a12, sH1@dtpoff
+	add    a12, a12, a10
+	movi   a13, sH2@dtpoff+1
+	add    a13, a13, a10
+
+	/* IE against global var */
+	rur	a2, THREADPTR
+	movi	a3, sg2@tpoff
+	add	a3, a3, a2
+
+	/* IE against local var  */
+	rur	a4, THREADPTR
+	movi	a5, sl2@tpoff
+	add	a5, a5, a4
+
+	/* IE against hidden and local var  */
+	rur	a6, THREADPTR
+	movi	a7, sh2@tpoff
+	add	a7, a7, a6
+
+	/* IE against hidden but not local var  */
+	rur	a8, THREADPTR
+	movi	a9, sH2@tpoff
+	add	a9, a9, a8
+
+	retw
Index: ld/testsuite/ld-xtensa/tlspic2.s
===================================================================
RCS file: ld/testsuite/ld-xtensa/tlspic2.s
diff -N ld/testsuite/ld-xtensa/tlspic2.s
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-xtensa/tlspic2.s	20 Aug 2008 23:17:01 -0000
@@ -0,0 +1,12 @@
+	.section ".tbss", "awT", @nobits
+	.global	sH1, sH2, sH3, sH4, sH5, sH6, sH7, sH8
+	.hidden	sH1, sH2, sH3, sH4, sH5, sH6, sH7, sH8
+	.align	4
+sH1:	.space	4
+sH2:	.space	4
+sH3:	.space	4
+sH4:	.space	4
+sH5:	.space	4
+sH6:	.space	4
+sH7:	.space	4
+sH8:	.space	4
Index: ld/testsuite/ld-xtensa/xtensa.exp
===================================================================
RCS file: ld/testsuite/ld-xtensa/xtensa.exp
diff -N ld/testsuite/ld-xtensa/xtensa.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-xtensa/xtensa.exp	20 Aug 2008 23:17:01 -0000
@@ -0,0 +1,54 @@
+# Expect script for ld-xtensa tests
+#   Copyright (C) 2008 Free Software Foundation
+#
+# This file is part of the GNU Binutils.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+#
+
+if { !([istarget "xtensa*-*-linux*"]) } {
+    return
+}
+
+# List contains test-items with 3 items followed by 2 lists:
+# 0:name 1:ld options 2:assembler options
+# 3:filenames of assembler files 4: action and options. 5: name of output file
+
+# Actions:
+# objdump: Apply objdump options on result.  Compare with regex (last arg).
+# nm: Apply nm options on result.  Compare with regex (last arg).
+# readelf: Apply readelf options on result.  Compare with regex (last arg).
+
+set xtensatests {
+    {"TLS -shared transitions"
+     "-shared -melf32xtensa" "" {tlspic1.s tlspic2.s}
+     {{readelf -WSsrl tlspic.rd}
+      {objdump "-drj.text --start-address=0x350" tlspic.dd}
+      {objdump "-sj.text --stop-address=0x350" tlspic.sd}
+      {objdump -sj.tdata tlspic.td}}
+      "libtlspic.so"}
+    {"Helper shared library" "-shared -melf32xtensa"
+     "" {tlslib.s} {} "libtlslib.so"}
+    {"TLS exec transitions"
+     "-melf32xtensa tmpdir/libtlslib.so" "" {tlsbin.s}
+     {{readelf -WSsrl tlsbin.rd}
+      {objdump "-drj.text --start-address=0x400238" tlsbin.dd}
+      {objdump "-sj.text --stop-address=0x400238" tlsbin.sd}
+      {objdump -sj.tdata tlsbin.td}}
+      "tlsbin"}
+}
+
+run_ld_link_tests $xtensatests

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