2008-08-20 Bob Wilson <bob.wilson@acm.org>
authorBob Wilson <bob.wilson@acm.org>
Wed, 20 Aug 2008 23:28:59 +0000 (23:28 +0000)
committerBob Wilson <bob.wilson@acm.org>
Wed, 20 Aug 2008 23:28:59 +0000 (23:28 +0000)
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.

26 files changed:
bfd/ChangeLog
bfd/bfd-in2.h
bfd/elf-bfd.h
bfd/elf32-xtensa.c
bfd/libbfd.h
bfd/reloc.c
gas/ChangeLog
gas/config/tc-xtensa.c
gas/config/xtensa-istack.h
gas/config/xtensa-relax.c
gas/config/xtensa-relax.h
include/elf/ChangeLog
include/elf/xtensa.h
ld/testsuite/ld-xtensa/tlsbin.dd [new file with mode: 0644]
ld/testsuite/ld-xtensa/tlsbin.rd [new file with mode: 0644]
ld/testsuite/ld-xtensa/tlsbin.s [new file with mode: 0644]
ld/testsuite/ld-xtensa/tlsbin.sd [new file with mode: 0644]
ld/testsuite/ld-xtensa/tlsbin.td [new file with mode: 0644]
ld/testsuite/ld-xtensa/tlslib.s [new file with mode: 0644]
ld/testsuite/ld-xtensa/tlspic.dd [new file with mode: 0644]
ld/testsuite/ld-xtensa/tlspic.rd [new file with mode: 0644]
ld/testsuite/ld-xtensa/tlspic.sd [new file with mode: 0644]
ld/testsuite/ld-xtensa/tlspic.td [new file with mode: 0644]
ld/testsuite/ld-xtensa/tlspic1.s [new file with mode: 0644]
ld/testsuite/ld-xtensa/tlspic2.s [new file with mode: 0644]
ld/testsuite/ld-xtensa/xtensa.exp [new file with mode: 0644]

index 6c3dd56..0b753bf 100644 (file)
@@ -1,3 +1,45 @@
+2008-08-20  Bob Wilson  <bob.wilson@acm.org>
+
+       * 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.
+
 2008-08-18  Richard Sandiford  <rdsandiford@googlemail.com>
 
        * elfxx-mips.c (_bfd_mips_elf_copy_indirect_symbol): Copy MIPS16
index a39c461..c8ef5f9 100644 (file)
@@ -4369,6 +4369,15 @@ internally by the linker after analysis of a
 BFD_RELOC_XTENSA_ASM_EXPAND.  */
   BFD_RELOC_XTENSA_ASM_SIMPLIFY,
 
+/* Xtensa TLS relocations.  */
+  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,
+
 /* 8 bit signed offset in (ix+d) or (iy+d).  */
   BFD_RELOC_Z80_DISP8,
 
index 2b739f9..993458e 100644 (file)
@@ -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 a618d3b..3463efa 100644 (file)
@@ -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 ATTRIBUTE_UNUSED,
       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_entry[PLT_ENTRY_SIZE] =
   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 *abfd)
     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 *abfd)
 
   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;
 
-         if ((sec->flags & SEC_ALLOC) != 0)
+       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;
+
+       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 elf_link_hash_entry *h, void *arg)
 {
   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 elf_link_hash_entry *h, void *arg)
   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 (struct bfd_link_info *info)
 
       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 *output_bfd ATTRIBUTE_UNUSED,
   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;
+}
+
 \f
+/* 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 *howto,
     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 *howto,
       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 *howto,
     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_link_info *info,
 }
 
 
+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_bfd,
   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_bfd,
       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_bfd,
       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_bfd,
              && !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_bfd,
          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_bfd,
                 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_bfd,
             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_bfd,
 
       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 opcode)
 }
 
 
+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_section elf_xtensa_special_sections[] =
 
 #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_section elf_xtensa_special_sections[] =
 #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 640b284..eb84d58 100644 (file)
@@ -2001,6 +2001,13 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_XTENSA_OP2",
   "BFD_RELOC_XTENSA_ASM_EXPAND",
   "BFD_RELOC_XTENSA_ASM_SIMPLIFY",
+  "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",
   "BFD_RELOC_Z80_DISP8",
   "BFD_RELOC_Z8K_DISP7",
   "BFD_RELOC_Z8K_CALLR",
index 03b46bf..9d993af 100644 (file)
@@ -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 6bee5f9..096f1d4 100644 (file)
@@ -1,3 +1,29 @@
+2008-08-20  Bob Wilson  <bob.wilson@acm.org>
+
+       * 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.
+       
 2008-08-20  H.J. Lu  <hongjiu.lu@intel.com>
 
        AVX Programming Reference (August, 2008)
index d2753bb..b893cef 100644 (file)
@@ -356,6 +356,11 @@ op_placement_info_table op_placement_table;
 #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_relocs[] =
   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_code_real_type 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 operator)
        }
     }
 
+  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 expressionS *exp)
     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 *insn, BuildInstr *bi)
            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 *insn)
       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, segT seg)
       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, segT seg)
       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 0bd0974..ebbb4f0 100644 (file)
@@ -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 89a53d3..8f49afa 100644 (file)
@@ -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_insn,
   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_insn,
      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_insn,
          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_insn,
          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 74b4ae8..14898c9 100644 (file)
@@ -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 407c5ec..ae259dc 100644 (file)
@@ -1,3 +1,9 @@
+2008-08-20  Bob Wilson  <bob.wilson@acm.org>
+
+       * 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.
+
 2008-08-08  Richard Sandiford  <rdsandiford@googlemail.com>
            Daniel Jacobowitz  <dan@codesourcery.com>
            Catherine Moore  <clm@codesourcery.com>
index 7e70cb5..400bc17 100644 (file)
@@ -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_type)
      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.  */
diff --git a/ld/testsuite/ld-xtensa/tlsbin.dd b/ld/testsuite/ld-xtensa/tlsbin.dd
new file mode 100644 (file)
index 0000000..c3fad8b
--- /dev/null
@@ -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.*
diff --git a/ld/testsuite/ld-xtensa/tlsbin.rd b/ld/testsuite/ld-xtensa/tlsbin.rd
new file mode 100644 (file)
index 0000000..b2e8726
--- /dev/null
@@ -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
diff --git a/ld/testsuite/ld-xtensa/tlsbin.s b/ld/testsuite/ld-xtensa/tlsbin.s
new file mode 100644 (file)
index 0000000..2220cfc
--- /dev/null
@@ -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
diff --git a/ld/testsuite/ld-xtensa/tlsbin.sd b/ld/testsuite/ld-xtensa/tlsbin.sd
new file mode 100644 (file)
index 0000000..484db07
--- /dev/null
@@ -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*  .*
diff --git a/ld/testsuite/ld-xtensa/tlsbin.td b/ld/testsuite/ld-xtensa/tlsbin.td
new file mode 100644 (file)
index 0000000..efef45c
--- /dev/null
@@ -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*  .*
diff --git a/ld/testsuite/ld-xtensa/tlslib.s b/ld/testsuite/ld-xtensa/tlslib.s
new file mode 100644 (file)
index 0000000..a2c430b
--- /dev/null
@@ -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
diff --git a/ld/testsuite/ld-xtensa/tlspic.dd b/ld/testsuite/ld-xtensa/tlspic.dd
new file mode 100644 (file)
index 0000000..9f6e20a
--- /dev/null
@@ -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.*
diff --git a/ld/testsuite/ld-xtensa/tlspic.rd b/ld/testsuite/ld-xtensa/tlspic.rd
new file mode 100644 (file)
index 0000000..54dd71a
--- /dev/null
@@ -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
diff --git a/ld/testsuite/ld-xtensa/tlspic.sd b/ld/testsuite/ld-xtensa/tlspic.sd
new file mode 100644 (file)
index 0000000..57dafc0
--- /dev/null
@@ -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+  .*
diff --git a/ld/testsuite/ld-xtensa/tlspic.td b/ld/testsuite/ld-xtensa/tlspic.td
new file mode 100644 (file)
index 0000000..d3e11d3
--- /dev/null
@@ -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*  .*
diff --git a/ld/testsuite/ld-xtensa/tlspic1.s b/ld/testsuite/ld-xtensa/tlspic1.s
new file mode 100644 (file)
index 0000000..9ecde66
--- /dev/null
@@ -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
diff --git a/ld/testsuite/ld-xtensa/tlspic2.s b/ld/testsuite/ld-xtensa/tlspic2.s
new file mode 100644 (file)
index 0000000..9f337e7
--- /dev/null
@@ -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
diff --git a/ld/testsuite/ld-xtensa/xtensa.exp b/ld/testsuite/ld-xtensa/xtensa.exp
new file mode 100644 (file)
index 0000000..e6dc827
--- /dev/null
@@ -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