bfd/
authorDaniel Jacobowitz <drow@false.org>
Tue, 29 Mar 2005 16:54:22 +0000 (16:54 +0000)
committerDaniel Jacobowitz <drow@false.org>
Tue, 29 Mar 2005 16:54:22 +0000 (16:54 +0000)
* bfd-in2.h, libbfd.h: Regenerated.
* reloc.c: Add ARM TLS relocations.
* elf32-arm.c (elf32_arm_howto_table): Add dynamic TLS
relocations.
(elf32_arm_tls_gd32_howto, elf32_arm_tls_ldo32_howto)
(elf32_arm_tls_ldm32_howto, elf32_arm_tls_le32_howto)
(elf32_arm_tls_ie32_howto): New.
(elf32_arm_howto_from_type): Support TLS relocations.
(elf32_arm_reloc_map): Likewise.
(elf32_arm_reloc_type_lookup): Likewise.
(TCB_SIZE): Define.
(struct elf32_arm_obj_tdata): New.
(elf32_arm_tdata, elf32_arm_local_got_tls_type): Define.
(elf32_arm_mkobject): New function.
(struct elf32_arm_relocs_copied): Add pc_count.
(elf32_arm_hash_entry, GOT_UNKNOWN, GOT_NORMAL, GOT_TLS_GD)
(GOT_TLS_IE): Define.
(struct elf32_arm_link_hash_table): Add tls_ldm_got.
(elf32_arm_link_hash_newfunc): Initialize tls_type.
(elf32_arm_copy_indirect_symbol): Copy pc_count and tls_type.
(elf32_arm_link_hash_table_create): Initialize tls_ldm_got.
(dtpoff_base, tpoff): New functions.
(elf32_arm_final_link_relocate): Handle TLS relocations.
(IS_ARM_TLS_RELOC): Define.
(elf32_arm_relocate_section): Warn about TLS mismatches.
(elf32_arm_gc_sweep_hook): Handle TLS relocations and pc_count.
(elf32_arm_check_relocs): Detect invalid symbol indexes.  Handle
TLS relocations and pc_count.
(elf32_arm_adjust_dynamic_symbol): Check non_got_ref.
(allocate_dynrelocs): Handle TLS.  Bind REL32 relocs to local
calls.
(elf32_arm_size_dynamic_sections): Handle TLS.
(elf32_arm_finish_dynamic_symbol): Likewise.
(bfd_elf32_mkobject): Define.
gas/
* config/tc-arm.c (arm_parse_reloc): Add TLS relocations.
(md_apply_fix3): Mark TLS symbols.
(tc_gen_reloc): Handle TLS relocations.
(arm_fix_adjustable): Ignore TLS relocations.
(s_arm_elf_cons): Support expressions after decorated symbols.
gas/testuite/
* gas/arm/tls.s, gas/arm/tls.d: New files.
* gas/arm/arm.exp: Run TLS test.
include/elf/
* arm.h: Add TLS relocations.
ld/testsuite/
* ld-arm/tls-lib.s, ld-arm/tls-lib.d, ld-arm/tls-lib.r,
ld-arm/tls-app.s, ld-arm/tls-app.d, ld-arm/tls-app.r: New files.
* ld-arm/arm-lib.ld, ld-arm/arm-dyn.ld: Increase data segment
alignment.
* ld-arm/arm-elf.exp: Run TLS tests.

23 files changed:
bfd/ChangeLog
bfd/bfd-in2.h
bfd/elf32-arm.c
bfd/libbfd.h
bfd/reloc.c
gas/ChangeLog
gas/config/tc-arm.c
gas/testsuite/ChangeLog
gas/testsuite/gas/arm/arm.exp
gas/testsuite/gas/arm/tls.d [new file with mode: 0644]
gas/testsuite/gas/arm/tls.s [new file with mode: 0644]
include/elf/ChangeLog
include/elf/arm.h
ld/testsuite/ChangeLog
ld/testsuite/ld-arm/arm-dyn.ld
ld/testsuite/ld-arm/arm-elf.exp
ld/testsuite/ld-arm/arm-lib.ld
ld/testsuite/ld-arm/tls-app.d [new file with mode: 0644]
ld/testsuite/ld-arm/tls-app.r [new file with mode: 0644]
ld/testsuite/ld-arm/tls-app.s [new file with mode: 0644]
ld/testsuite/ld-arm/tls-lib.d [new file with mode: 0644]
ld/testsuite/ld-arm/tls-lib.r [new file with mode: 0644]
ld/testsuite/ld-arm/tls-lib.s [new file with mode: 0644]

index c2629c5..c4b62dd 100644 (file)
@@ -1,4 +1,42 @@
 2005-03-29  Daniel Jacobowitz  <dan@codesourcery.com>
+           Phil Blundell  <philb@gnu.org>
+
+       * bfd-in2.h, libbfd.h: Regenerated.
+       * reloc.c: Add ARM TLS relocations.
+       * elf32-arm.c (elf32_arm_howto_table): Add dynamic TLS
+       relocations.
+       (elf32_arm_tls_gd32_howto, elf32_arm_tls_ldo32_howto)
+       (elf32_arm_tls_ldm32_howto, elf32_arm_tls_le32_howto)
+       (elf32_arm_tls_ie32_howto): New.
+       (elf32_arm_howto_from_type): Support TLS relocations.
+       (elf32_arm_reloc_map): Likewise.
+       (elf32_arm_reloc_type_lookup): Likewise.
+       (TCB_SIZE): Define.
+       (struct elf32_arm_obj_tdata): New.
+       (elf32_arm_tdata, elf32_arm_local_got_tls_type): Define.
+       (elf32_arm_mkobject): New function.
+       (struct elf32_arm_relocs_copied): Add pc_count.
+       (elf32_arm_hash_entry, GOT_UNKNOWN, GOT_NORMAL, GOT_TLS_GD)
+       (GOT_TLS_IE): Define.
+       (struct elf32_arm_link_hash_table): Add tls_ldm_got.
+       (elf32_arm_link_hash_newfunc): Initialize tls_type.
+       (elf32_arm_copy_indirect_symbol): Copy pc_count and tls_type.
+       (elf32_arm_link_hash_table_create): Initialize tls_ldm_got.
+       (dtpoff_base, tpoff): New functions.
+       (elf32_arm_final_link_relocate): Handle TLS relocations.
+       (IS_ARM_TLS_RELOC): Define.
+       (elf32_arm_relocate_section): Warn about TLS mismatches.
+       (elf32_arm_gc_sweep_hook): Handle TLS relocations and pc_count.
+       (elf32_arm_check_relocs): Detect invalid symbol indexes.  Handle
+       TLS relocations and pc_count.
+       (elf32_arm_adjust_dynamic_symbol): Check non_got_ref.
+       (allocate_dynrelocs): Handle TLS.  Bind REL32 relocs to local
+       calls.
+       (elf32_arm_size_dynamic_sections): Handle TLS.
+       (elf32_arm_finish_dynamic_symbol): Likewise.
+       (bfd_elf32_mkobject): Define.
+
+2005-03-29  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * elf32-arm.c (elf32_arm_check_relocs): Increment count for all
        relocation types.  Don't count relocations which will use a PLT.
index 131bd83..55492cb 100644 (file)
@@ -2691,6 +2691,14 @@ field in the instruction.  */
   BFD_RELOC_ARM_RELATIVE,
   BFD_RELOC_ARM_GOTOFF,
   BFD_RELOC_ARM_GOTPC,
+  BFD_RELOC_ARM_TLS_GD32,
+  BFD_RELOC_ARM_TLS_LDO32,
+  BFD_RELOC_ARM_TLS_LDM32,
+  BFD_RELOC_ARM_TLS_DTPOFF32,
+  BFD_RELOC_ARM_TLS_DTPMOD32,
+  BFD_RELOC_ARM_TLS_TPOFF32,
+  BFD_RELOC_ARM_TLS_IE32,
+  BFD_RELOC_ARM_TLS_LE32,
 
 /* Pc-relative or absolute relocation depending on target.  Used for
 entries in .init_array sections.  */
index 2e1f1d3..7464bbe 100644 (file)
@@ -294,49 +294,49 @@ static reloc_howto_type elf32_arm_howto_table[] =
         0x07ff07ff,            /* dst_mask */
         TRUE),                 /* pcrel_offset */
 
-  /* These next three relocs are not defined, but we need to fill the space.  */
+  /* Dynamic TLS relocations.  */
 
-  HOWTO (R_ARM_NONE,           /* type */
-        0,                     /* rightshift */
-        0,                     /* size (0 = byte, 1 = short, 2 = long) */
-        0,                     /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont,/* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_ARM_unknown_17",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOWTO (R_ARM_TLS_DTPMOD32,   /* type */
+         0,                     /* rightshift */
+         2,                     /* size (0 = byte, 1 = short, 2 = long) */
+         32,                    /* bitsize */
+         FALSE,                 /* pc_relative */
+         0,                     /* bitpos */
+         complain_overflow_bitfield,/* complain_on_overflow */
+         bfd_elf_generic_reloc, /* special_function */
+         "R_ARM_TLS_DTPMOD32", /* name */
+         TRUE,                 /* partial_inplace */
+         0xffffffff,           /* src_mask */
+         0xffffffff,           /* dst_mask */
+         FALSE),                /* pcrel_offset */
 
-  HOWTO (R_ARM_NONE,           /* type */
-        0,                     /* rightshift */
-        0,                     /* size (0 = byte, 1 = short, 2 = long) */
-        0,                     /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont,/* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_ARM_unknown_18",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOWTO (R_ARM_TLS_DTPOFF32,   /* type */
+         0,                     /* rightshift */
+         2,                     /* size (0 = byte, 1 = short, 2 = long) */
+         32,                    /* bitsize */
+         FALSE,                 /* pc_relative */
+         0,                     /* bitpos */
+         complain_overflow_bitfield,/* complain_on_overflow */
+         bfd_elf_generic_reloc, /* special_function */
+         "R_ARM_TLS_DTPOFF32", /* name */
+         TRUE,                 /* partial_inplace */
+         0xffffffff,           /* src_mask */
+         0xffffffff,           /* dst_mask */
+         FALSE),                /* pcrel_offset */
 
-  HOWTO (R_ARM_NONE,           /* type */
-        0,                     /* rightshift */
-        0,                     /* size (0 = byte, 1 = short, 2 = long) */
-        0,                     /* bitsize */
-        FALSE,                 /* pc_relative */
-        0,                     /* bitpos */
-        complain_overflow_dont,/* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_ARM_unknown_19",    /* name */
-        FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        FALSE),                /* pcrel_offset */
+  HOWTO (R_ARM_TLS_TPOFF32,    /* type */
+         0,                     /* rightshift */
+         2,                     /* size (0 = byte, 1 = short, 2 = long) */
+         32,                    /* bitsize */
+         FALSE,                 /* pc_relative */
+         0,                     /* bitpos */
+         complain_overflow_bitfield,/* complain_on_overflow */
+         bfd_elf_generic_reloc, /* special_function */
+         "R_ARM_TLS_TPOFF32",  /* name */
+         TRUE,                 /* partial_inplace */
+         0xffffffff,           /* src_mask */
+         0xffffffff,           /* dst_mask */
+         FALSE),                /* pcrel_offset */
 
   /* Relocs used in ARM Linux */
 
@@ -663,6 +663,81 @@ static reloc_howto_type elf32_arm_howto_table[] =
         TRUE),                 /* pcrel_offset */
 };
 
+static reloc_howto_type elf32_arm_tls_gd32_howto = 
+  HOWTO (R_ARM_TLS_GD32,       /* type */
+         0,                     /* rightshift */
+         2,                     /* size (0 = byte, 1 = short, 2 = long) */
+         32,                    /* bitsize */
+         FALSE,                 /* pc_relative */
+         0,                     /* bitpos */
+         complain_overflow_bitfield,/* complain_on_overflow */
+         NULL,                 /* special_function */
+         "R_ARM_TLS_GD32",     /* name */
+         TRUE,                 /* partial_inplace */
+         0xffffffff,           /* src_mask */
+         0xffffffff,           /* dst_mask */
+         FALSE);                /* pcrel_offset */
+
+static reloc_howto_type elf32_arm_tls_ldo32_howto = 
+  HOWTO (R_ARM_TLS_LDO32,      /* type */
+         0,                     /* rightshift */
+         2,                     /* size (0 = byte, 1 = short, 2 = long) */
+         32,                    /* bitsize */
+         FALSE,                 /* pc_relative */
+         0,                     /* bitpos */
+         complain_overflow_bitfield,/* complain_on_overflow */
+         bfd_elf_generic_reloc, /* special_function */
+         "R_ARM_TLS_LDO32",    /* name */
+         TRUE,                 /* partial_inplace */
+         0xffffffff,           /* src_mask */
+         0xffffffff,           /* dst_mask */
+         FALSE);                /* pcrel_offset */
+
+static reloc_howto_type elf32_arm_tls_ldm32_howto = 
+  HOWTO (R_ARM_TLS_LDM32,      /* type */
+         0,                     /* rightshift */
+         2,                     /* size (0 = byte, 1 = short, 2 = long) */
+         32,                    /* bitsize */
+         FALSE,                 /* pc_relative */
+         0,                     /* bitpos */
+         complain_overflow_bitfield,/* complain_on_overflow */
+         bfd_elf_generic_reloc, /* special_function */
+         "R_ARM_TLS_LDM32",    /* name */
+         TRUE,                 /* partial_inplace */
+         0xffffffff,           /* src_mask */
+         0xffffffff,           /* dst_mask */
+         FALSE);                /* pcrel_offset */
+
+static reloc_howto_type elf32_arm_tls_le32_howto = 
+  HOWTO (R_ARM_TLS_LE32,       /* type */
+         0,                     /* rightshift */
+         2,                     /* size (0 = byte, 1 = short, 2 = long) */
+         32,                    /* bitsize */
+         FALSE,                 /* pc_relative */
+         0,                     /* bitpos */
+         complain_overflow_bitfield,/* complain_on_overflow */
+         bfd_elf_generic_reloc, /* special_function */
+         "R_ARM_TLS_LE32",     /* name */
+         TRUE,                 /* partial_inplace */
+         0xffffffff,           /* src_mask */
+         0xffffffff,           /* dst_mask */
+         FALSE);                /* pcrel_offset */
+
+static reloc_howto_type elf32_arm_tls_ie32_howto = 
+  HOWTO (R_ARM_TLS_IE32,       /* type */
+         0,                     /* rightshift */
+         2,                     /* size (0 = byte, 1 = short, 2 = long) */
+         32,                    /* bitsize */
+         FALSE,                  /* pc_relative */
+         0,                     /* bitpos */
+         complain_overflow_bitfield,/* complain_on_overflow */
+         NULL,                 /* special_function */
+         "R_ARM_TLS_IE32",     /* name */
+         TRUE,                 /* partial_inplace */
+         0xffffffff,           /* src_mask */
+         0xffffffff,           /* dst_mask */
+         FALSE);                /* pcrel_offset */
+
   /* GNU extension to record C++ vtable hierarchy */
 static reloc_howto_type elf32_arm_vtinherit_howto =
   HOWTO (R_ARM_GNU_VTINHERIT, /* type */
@@ -825,6 +900,26 @@ elf32_arm_howto_from_type (unsigned int r_type)
 
     case R_ARM_THM_PC9:
       return &elf32_arm_thm_pc9_howto;
+      
+    case R_ARM_TLS_GD32:
+      return &elf32_arm_tls_gd32_howto;
+      break;
+
+    case R_ARM_TLS_LDO32:
+      return &elf32_arm_tls_ldo32_howto;
+      break;
+
+    case R_ARM_TLS_LDM32:
+      return &elf32_arm_tls_ldm32_howto;
+      break;
+
+    case R_ARM_TLS_IE32:
+      return &elf32_arm_tls_ie32_howto;
+      break;
+
+    case R_ARM_TLS_LE32:
+      return &elf32_arm_tls_le32_howto;
+      break;
 
     case R_ARM_RREL32:
     case R_ARM_RABS32:
@@ -879,7 +974,16 @@ static const struct elf32_arm_reloc_map elf32_arm_reloc_map[] =
     {BFD_RELOC_ARM_ROSEGREL32,      R_ARM_ROSEGREL32},
     {BFD_RELOC_ARM_SBREL32,         R_ARM_SBREL32},
     {BFD_RELOC_ARM_PREL31,          R_ARM_PREL31},
-    {BFD_RELOC_ARM_TARGET2,         R_ARM_TARGET2}
+    {BFD_RELOC_ARM_TARGET2,         R_ARM_TARGET2},
+    {BFD_RELOC_ARM_PLT32,            R_ARM_PLT32},
+    {BFD_RELOC_ARM_TLS_GD32,        R_ARM_TLS_GD32},
+    {BFD_RELOC_ARM_TLS_LDO32,       R_ARM_TLS_LDO32},
+    {BFD_RELOC_ARM_TLS_LDM32,       R_ARM_TLS_LDM32},
+    {BFD_RELOC_ARM_TLS_DTPMOD32,     R_ARM_TLS_DTPMOD32},
+    {BFD_RELOC_ARM_TLS_DTPOFF32,     R_ARM_TLS_DTPOFF32},
+    {BFD_RELOC_ARM_TLS_TPOFF32,      R_ARM_TLS_TPOFF32},
+    {BFD_RELOC_ARM_TLS_IE32,         R_ARM_TLS_IE32},
+    {BFD_RELOC_ARM_TLS_LE32,         R_ARM_TLS_LE32},
   };
 
 static reloc_howto_type *
@@ -903,6 +1007,21 @@ elf32_arm_reloc_type_lookup (abfd, code)
     case BFD_RELOC_THUMB_PCREL_BRANCH9:
       return & elf32_arm_thm_pc9_howto;
 
+    case BFD_RELOC_ARM_TLS_GD32:
+      return & elf32_arm_tls_gd32_howto;
+
+    case BFD_RELOC_ARM_TLS_LDO32:
+      return & elf32_arm_tls_ldo32_howto;
+
+    case BFD_RELOC_ARM_TLS_LDM32:
+      return & elf32_arm_tls_ldm32_howto;
+
+    case BFD_RELOC_ARM_TLS_IE32:
+      return & elf32_arm_tls_ie32_howto;
+
+    case BFD_RELOC_ARM_TLS_LE32:
+      return & elf32_arm_tls_le32_howto;
+
     default:
       for (i = 0; i < NUM_ELEM (elf32_arm_reloc_map); i ++)
        if (elf32_arm_reloc_map[i].bfd_reloc_val == code)
@@ -1093,14 +1212,41 @@ struct _arm_elf_section_data
 #define elf32_arm_section_data(sec) \
   ((struct _arm_elf_section_data *) elf_section_data (sec))
 
+/* The size of the thread control block.  */
+#define TCB_SIZE       8
+
+struct elf32_arm_obj_tdata
+{
+  struct elf_obj_tdata root;
+
+  /* tls_type for each local got entry.  */
+  char *local_got_tls_type;
+};
+
+#define elf32_arm_tdata(abfd) \
+  ((struct elf32_arm_obj_tdata *) (abfd)->tdata.any)
+
+#define elf32_arm_local_got_tls_type(abfd) \
+  (elf32_arm_tdata (abfd)->local_got_tls_type)
+
+static bfd_boolean
+elf32_arm_mkobject (bfd *abfd)
+{
+  bfd_size_type amt = sizeof (struct elf32_arm_obj_tdata);
+  abfd->tdata.any = bfd_zalloc (abfd, amt);
+  if (abfd->tdata.any == NULL)
+    return FALSE;
+  return TRUE;
+}
+
 /* The ARM linker needs to keep track of the number of relocs that it
    decides to copy in check_relocs for each symbol.  This is so that
    it can discard PC relative relocs if it doesn't need them when
    linking with -Bsymbolic.  We store the information in a field
    extending the regular ELF linker hash table.  */
 
-/* This structure keeps track of the number of PC relative relocs we
-   have copied for a given symbol.  */
+/* This structure keeps track of the number of relocs we have copied
+   for a given symbol.  */
 struct elf32_arm_relocs_copied
   {
     /* Next section.  */
@@ -1109,8 +1255,12 @@ struct elf32_arm_relocs_copied
     asection * section;
     /* Number of relocs copied in this section.  */
     bfd_size_type count;
+    /* Number of PC-relative relocs copied in this section.  */
+    bfd_size_type pc_count;
   };
 
+#define elf32_arm_hash_entry(ent) ((struct elf32_arm_link_hash_entry *)(ent))
+
 /* Arm ELF linker hash entry.  */
 struct elf32_arm_link_hash_entry
   {
@@ -1127,6 +1277,12 @@ struct elf32_arm_link_hash_entry
        used, we need to record the index into .got.plt instead of
        recomputing it from the PLT offset.  */
     bfd_signed_vma plt_got_offset;
+
+#define GOT_UNKNOWN    0
+#define GOT_NORMAL     1
+#define GOT_TLS_GD     2
+#define GOT_TLS_IE     4
+    unsigned char tls_type;
   };
 
 /* Traverse an arm ELF linker hash table.  */
@@ -1189,6 +1345,12 @@ struct elf32_arm_link_hash_table
     asection *sdynbss;
     asection *srelbss;
 
+    /* Data for R_ARM_TLS_LDM32 relocations.  */
+    union {
+      bfd_signed_vma refcount;
+      bfd_vma offset;
+    } tls_ldm_got;
+    
     /* Small local sym to section mapping cache.  */
     struct sym_sec_cache sym_sec;
 
@@ -1220,6 +1382,7 @@ elf32_arm_link_hash_newfunc (struct bfd_hash_entry * entry,
   if (ret != NULL)
     {
       ret->relocs_copied = NULL;
+      ret->tls_type = GOT_UNKNOWN;
       ret->plt_thumb_refcount = 0;
       ret->plt_got_offset = -1;
     }
@@ -1321,6 +1484,7 @@ elf32_arm_copy_indirect_symbol (const struct elf_backend_data *bed,
              for (q = edir->relocs_copied; q != NULL; q = q->next)
                if (q->section == p->section)
                  {
+                   q->pc_count += p->pc_count;
                    q->count += p->count;
                    *pp = p->next;
                    break;
@@ -1346,6 +1510,13 @@ elf32_arm_copy_indirect_symbol (const struct elf_backend_data *bed,
   else
     BFD_ASSERT (eind->plt_thumb_refcount == 0);
 
+  if (ind->root.type == bfd_link_hash_indirect
+      && dir->got.refcount <= 0)
+    {
+      edir->tls_type = eind->tls_type;
+      eind->tls_type = GOT_UNKNOWN;
+    }
+
   _bfd_elf_link_hash_copy_indirect (bed, dir, ind);
 }
 
@@ -1392,6 +1563,7 @@ elf32_arm_link_hash_table_create (bfd *abfd)
   ret->use_rel = 1;
   ret->sym_sec.abfd = NULL;
   ret->obfd = abfd;
+  ret->tls_ldm_got.refcount = 0;
 
   return &ret->root.root;
 }
@@ -2228,6 +2400,35 @@ arm_real_reloc_type (struct elf32_arm_link_hash_table * globals,
 #endif /* OLD_ARM_ABI */
 
 
+/* 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 a relocation as part of a final link.  */
 
 static bfd_reloc_status_type
@@ -2979,6 +3180,222 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                                       contents, rel->r_offset, value,
                                       (bfd_vma) 0);
 
+    case R_ARM_TLS_LDO32:
+      value = value - dtpoff_base (info);
+
+      return _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                      contents, rel->r_offset, value, (bfd_vma) 0);
+
+    case R_ARM_TLS_LDM32:
+      {
+       bfd_vma off;
+
+       if (globals->sgot == NULL)
+         abort ();
+
+       off = globals->tls_ldm_got.offset;
+
+       if ((off & 1) != 0)
+         off &= ~1;
+       else
+         {
+           /* If we don't know the module number, create a relocation
+              for it.  */
+           if (info->shared)
+             {
+               Elf_Internal_Rela outrel;
+               bfd_byte *loc;
+
+               if (globals->srelgot == NULL)
+                 abort ();
+
+               outrel.r_offset = (globals->sgot->output_section->vma
+                                  + globals->sgot->output_offset + off);
+               outrel.r_info = ELF32_R_INFO (0, R_ARM_TLS_DTPMOD32);
+
+               bfd_put_32 (output_bfd, 0, globals->sgot->contents + off);
+
+               loc = globals->srelgot->contents;
+               loc += globals->srelgot->reloc_count++ * sizeof (Elf32_External_Rel);
+               bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+             }
+           else
+             bfd_put_32 (output_bfd, 1, globals->sgot->contents + off);
+
+           globals->tls_ldm_got.offset |= 1;
+         }
+
+       value = globals->sgot->output_section->vma + globals->sgot->output_offset + off 
+         - (input_section->output_section->vma + input_section->output_offset + rel->r_offset);
+
+       return _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                        contents, rel->r_offset, value,
+                                        (bfd_vma) 0);
+      }
+
+    case R_ARM_TLS_GD32:
+    case R_ARM_TLS_IE32:
+      {
+       bfd_vma off;
+       int indx;
+       char tls_type;
+
+       if (globals->sgot == NULL)
+         abort ();
+
+       indx = 0;
+       if (h != NULL)
+         {
+           bfd_boolean dyn;
+           dyn = globals->root.dynamic_sections_created;
+           if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+               && (!info->shared
+                   || !SYMBOL_REFERENCES_LOCAL (info, h)))
+             {
+               *unresolved_reloc_p = FALSE;
+               indx = h->dynindx;
+             }
+           off = h->got.offset;
+           tls_type = ((struct elf32_arm_link_hash_entry *) h)->tls_type;
+         }
+       else
+         {
+           if (local_got_offsets == NULL)
+             abort ();
+           off = local_got_offsets[r_symndx];
+           tls_type = elf32_arm_local_got_tls_type (input_bfd)[r_symndx];
+         }
+
+       if (tls_type == GOT_UNKNOWN)
+         abort ();
+
+       if ((off & 1) != 0)
+         off &= ~1;
+       else
+         {
+           bfd_boolean need_relocs = FALSE;
+           Elf_Internal_Rela outrel;
+           bfd_byte *loc = NULL;
+           int cur_off = off;
+
+           /* The GOT entries have not been initialized yet.  Do it
+              now, and emit any relocations.  If both an IE GOT and a
+              GD GOT are necessary, we emit the GD first.  */
+
+           if ((info->shared || indx != 0)
+               && (h == NULL
+                   || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+                   || h->root.type != bfd_link_hash_undefweak))
+             {
+               need_relocs = TRUE;
+               if (globals->srelgot == NULL)
+                 abort ();
+               loc = globals->srelgot->contents;
+               loc += globals->srelgot->reloc_count * sizeof (Elf32_External_Rel);
+             }
+
+           if (tls_type & GOT_TLS_GD)
+             {
+               if (need_relocs)
+                 {
+                   outrel.r_offset = (globals->sgot->output_section->vma
+                                      + globals->sgot->output_offset + cur_off);
+                   outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_DTPMOD32);
+                   bfd_put_32 (output_bfd, 0, globals->sgot->contents + cur_off);
+
+                   bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+                   globals->srelgot->reloc_count++;
+                   loc += sizeof (Elf32_External_Rel);
+
+                   if (indx == 0)
+                     bfd_put_32 (output_bfd, value - dtpoff_base (info),
+                                 globals->sgot->contents + cur_off + 4);
+                   else
+                     {
+                       bfd_put_32 (output_bfd, 0,
+                                   globals->sgot->contents + cur_off + 4);
+
+                       outrel.r_info = ELF32_R_INFO (indx,
+                                                     R_ARM_TLS_DTPOFF32);
+                       outrel.r_offset += 4;
+                       bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+                       globals->srelgot->reloc_count++;
+                       loc += sizeof (Elf32_External_Rel);
+                     }
+                 }
+               else
+                 {
+                   /* If we are not emitting relocations for a
+                      general dynamic reference, then we must be in a
+                      static link or an executable link with the
+                      symbol binding locally.  Mark it as belonging
+                      to module 1, the executable.  */
+                   bfd_put_32 (output_bfd, 1,
+                               globals->sgot->contents + cur_off);
+                   bfd_put_32 (output_bfd, value - dtpoff_base (info),
+                               globals->sgot->contents + cur_off + 4);
+                 }
+
+               cur_off += 8;
+             }
+
+           if (tls_type & GOT_TLS_IE)
+             {
+               if (need_relocs)
+                 {
+                   outrel.r_offset = (globals->sgot->output_section->vma
+                                      + globals->sgot->output_offset
+                                      + cur_off);
+                   outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_TPOFF32);
+
+                   if (indx == 0)
+                     bfd_put_32 (output_bfd, value - dtpoff_base (info),
+                                 globals->sgot->contents + cur_off);
+                   else
+                     bfd_put_32 (output_bfd, 0,
+                                 globals->sgot->contents + cur_off);
+
+                   bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+                   globals->srelgot->reloc_count++;
+                   loc += sizeof (Elf32_External_Rel);
+                 }
+               else
+                 bfd_put_32 (output_bfd, tpoff (info, value),
+                             globals->sgot->contents + cur_off);
+               cur_off += 4;
+             }
+
+           if (h != NULL)
+             h->got.offset |= 1;
+           else
+             local_got_offsets[r_symndx] |= 1;
+         }
+
+       if ((tls_type & GOT_TLS_GD) && r_type != R_ARM_TLS_GD32)
+         off += 8;
+       value = globals->sgot->output_section->vma + globals->sgot->output_offset + off 
+         - (input_section->output_section->vma + input_section->output_offset + rel->r_offset);
+
+       return _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                        contents, rel->r_offset, value,
+                                        (bfd_vma) 0);
+      }
+
+    case R_ARM_TLS_LE32:
+      if (info->shared)
+       {
+         (*_bfd_error_handler)
+           (_("%B(%A+0x%lx): R_ARM_TLS_LE32 relocation not permitted in shared object"),
+            input_bfd, input_section,
+            (long) rel->r_offset, howto->name);
+         return FALSE;   
+       }
+      else
+       value = tpoff (info, value);
+      
+      return _bfd_final_link_relocate (howto, input_bfd, input_section,
+                                      contents, rel->r_offset, value, (bfd_vma) 0);
+
     case R_ARM_SBREL32:
       return bfd_reloc_notsupported;
 
@@ -3098,6 +3515,16 @@ arm_add_to_rel (bfd *              abfd,
     }
 }
 
+#define IS_ARM_TLS_RELOC(R_TYPE)       \
+  ((R_TYPE) == R_ARM_TLS_GD32          \
+   || (R_TYPE) == R_ARM_TLS_LDO32      \
+   || (R_TYPE) == R_ARM_TLS_LDM32      \
+   || (R_TYPE) == R_ARM_TLS_DTPOFF32   \
+   || (R_TYPE) == R_ARM_TLS_DTPMOD32   \
+   || (R_TYPE) == R_ARM_TLS_TPOFF32    \
+   || (R_TYPE) == R_ARM_TLS_LE32       \
+   || (R_TYPE) == R_ARM_TLS_IE32)
+
 /* Relocate an ARM ELF section.  */
 static bfd_boolean
 elf32_arm_relocate_section (bfd *                  output_bfd,
@@ -3136,6 +3563,7 @@ elf32_arm_relocate_section (bfd *                  output_bfd,
       bfd_vma                      relocation;
       bfd_reloc_status_type        r;
       arelent                      bfd_reloc;
+      char                         sym_type;
       bfd_boolean                  unresolved_reloc = FALSE;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
@@ -3179,6 +3607,7 @@ elf32_arm_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];
          if (globals->use_rel)
            {
@@ -3232,6 +3661,8 @@ elf32_arm_relocate_section (bfd *                  output_bfd,
                                   r_symndx, symtab_hdr, sym_hashes,
                                   h, sec, relocation,
                                   unresolved_reloc, warned);
+
+         sym_type = h->type;
        }
 
       if (h != NULL)
@@ -3244,6 +3675,24 @@ elf32_arm_relocate_section (bfd *                  output_bfd,
            name = bfd_section_name (input_bfd, sec);
        }
 
+      if (r_symndx != 0
+         && r_type != R_ARM_NONE
+         && (h == NULL
+             || h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+         && IS_ARM_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);
+       }
+
       r = elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
                                         input_section, contents, rel,
                                         relocation, info, sec, name,
@@ -3823,10 +4272,10 @@ elf32_arm_gc_mark_hook (asection *                   sec,
 /* Update the got entry reference counts for the section being removed.  */
 
 static bfd_boolean
-elf32_arm_gc_sweep_hook (bfd *                     abfd ATTRIBUTE_UNUSED,
-                        struct bfd_link_info *    info ATTRIBUTE_UNUSED,
-                        asection *                sec ATTRIBUTE_UNUSED,
-                        const Elf_Internal_Rela * relocs ATTRIBUTE_UNUSED)
+elf32_arm_gc_sweep_hook (bfd *                     abfd,
+                        struct bfd_link_info *    info,
+                        asection *                sec,
+                        const Elf_Internal_Rela * relocs)
 {
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
@@ -3868,6 +4317,8 @@ elf32_arm_gc_sweep_hook (bfd *                     abfd ATTRIBUTE_UNUSED,
 #ifndef OLD_ARM_ABI
        case R_ARM_GOT_PREL:
 #endif
+       case R_ARM_TLS_GD32:
+       case R_ARM_TLS_IE32:
          if (h != NULL)
            {
              if (h->got.refcount > 0)
@@ -3880,6 +4331,10 @@ elf32_arm_gc_sweep_hook (bfd *                     abfd ATTRIBUTE_UNUSED,
            }
          break;
 
+       case R_ARM_TLS_LDM32:
+         elf32_arm_hash_table (info)->tls_ldm_got.refcount -= 1;
+         break;
+
        case R_ARM_ABS32:
        case R_ARM_REL32:
        case R_ARM_PC24:
@@ -3915,6 +4370,8 @@ elf32_arm_gc_sweep_hook (bfd *                     abfd ATTRIBUTE_UNUSED,
                  if (p->section == sec)
                    {
                      p->count -= 1;
+                     if (ELF32_R_TYPE (rel->r_info) == R_ARM_REL32)
+                       p->pc_count -= 1;
                      if (p->count == 0)
                        *pp = p->next;
                      break;
@@ -3986,6 +4443,14 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
 #ifndef OLD_ARM_ABI
       r_type = arm_real_reloc_type (htab, r_type);
 #endif
+
+      if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
+       {
+         (*_bfd_error_handler) (_("%B: bad symbol index: %d"), abfd,
+                                r_symndx);
+         return FALSE;
+       }
+
       if (r_symndx < symtab_hdr->sh_info)
         h = NULL;
       else
@@ -3999,33 +4464,69 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
 #ifndef OLD_ARM_ABI
          case R_ARM_GOT_PREL:
 #endif
+         case R_ARM_TLS_GD32:
+         case R_ARM_TLS_IE32:
            /* This symbol requires a global offset table entry.  */
-           if (h != NULL)
-             {
-               h->got.refcount++;
-             }
-           else
-             {
-               bfd_signed_vma *local_got_refcounts;
+           {
+             int tls_type, old_tls_type;
 
-               /* 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;
+             switch (r_type)
+               {
+               case R_ARM_TLS_GD32: tls_type = GOT_TLS_GD; break;
+               case R_ARM_TLS_IE32: tls_type = GOT_TLS_IE; break;
+               default: tls_type = GOT_NORMAL; break;
+               }
 
-                   size = symtab_hdr->sh_info;
-                   size *= (sizeof (bfd_signed_vma) + sizeof (char));
-                   local_got_refcounts = 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;
-             }
-           if (r_type == R_ARM_GOT32)
-             break;
-           /* Fall through.  */
+             if (h != NULL)
+               {
+                 h->got.refcount++;
+                 old_tls_type = elf32_arm_hash_entry (h)->tls_type;
+               }
+             else
+               {
+                 bfd_signed_vma *local_got_refcounts;
+
+                 /* 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;
+                     
+                     size = symtab_hdr->sh_info;
+                     size *= (sizeof (bfd_signed_vma) + sizeof(char));
+                     local_got_refcounts = bfd_zalloc (abfd, size);
+                     if (local_got_refcounts == NULL)
+                       return FALSE;
+                     elf_local_got_refcounts (abfd) = local_got_refcounts;
+                     elf32_arm_local_got_tls_type (abfd)
+                       = (char *) (local_got_refcounts + symtab_hdr->sh_info);
+                   }
+                 local_got_refcounts[r_symndx] += 1;
+                 old_tls_type = elf32_arm_local_got_tls_type (abfd) [r_symndx];
+               }
+
+             /* We will already have issued an error message if there is a
+                TLS / non-TLS mismatch, based on the symbol type.  We don't
+                support any linker relaxations.  So just combine any TLS
+                types needed.  */
+             if (old_tls_type != GOT_UNKNOWN && old_tls_type != GOT_NORMAL
+                 && tls_type != GOT_NORMAL)
+               tls_type |= old_tls_type;
+
+             if (old_tls_type != tls_type)
+               {
+                 if (h != NULL)
+                   elf32_arm_hash_entry (h)->tls_type = tls_type;
+                 else
+                   elf32_arm_local_got_tls_type (abfd) [r_symndx] = tls_type;
+               }
+           }
+           /* Fall through */
+
+         case R_ARM_TLS_LDM32:
+           if (r_type == R_ARM_TLS_LDM32)
+               htab->tls_ldm_got.refcount++;
+           /* Fall through */
 
          case R_ARM_GOTOFF:
          case R_ARM_GOTPC:
@@ -4176,8 +4677,11 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
                    *head = p;
                    p->section = sec;
                    p->count = 0;
+                   p->pc_count = 0;
                  }
 
+               if (r_type == R_ARM_REL32)
+                 p->pc_count += 1;
                p->count += 1;
              }
            break;
@@ -4404,6 +4908,11 @@ elf32_arm_adjust_dynamic_symbol (struct bfd_link_info * info,
       return TRUE;
     }
 
+  /* If there are no non-GOT references, we do not need a copy
+     relocation.  */
+  if (!h->non_got_ref)
+    return TRUE;
+
   /* This is a reference to a symbol defined by a dynamic object which
      is not a function.  */
 
@@ -4571,6 +5080,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
     {
       asection *s;
       bfd_boolean dyn;
+      int tls_type = elf32_arm_hash_entry (h)->tls_type;
+      int indx;
 
       /* Make sure this symbol is output as a dynamic symbol.
         Undefined weak syms won't yet be marked as dynamic.  */
@@ -4585,12 +5096,49 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
        {
          s = htab->sgot;
          h->got.offset = s->size;
-         s->size += 4;
+
+         if (tls_type == GOT_UNKNOWN)
+           abort ();
+
+         if (tls_type == GOT_NORMAL)
+           /* Non-TLS symbols need one GOT slot.  */
+           s->size += 4;
+         else
+           {
+             if (tls_type & GOT_TLS_GD)
+               /* R_ARM_TLS_GD32 needs 2 consecutive GOT slots.  */
+               s->size += 8;
+             if (tls_type & GOT_TLS_IE)
+               /* R_ARM_TLS_IE32 needs one GOT slot.  */
+               s->size += 4;
+           }
+
          dyn = htab->root.dynamic_sections_created;
-         if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
-              || h->root.type != bfd_link_hash_undefweak)
-             && (info->shared
-                 || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
+
+         indx = 0;
+         if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+             && (!info->shared
+                 || !SYMBOL_REFERENCES_LOCAL (info, h)))
+           indx = h->dynindx;
+
+         if (tls_type != GOT_NORMAL
+             && (info->shared || indx != 0)
+             && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+                 || h->root.type != bfd_link_hash_undefweak))
+           {
+             if (tls_type & GOT_TLS_IE)
+               htab->srelgot->size += sizeof (Elf32_External_Rel);
+
+             if (tls_type & GOT_TLS_GD)
+               htab->srelgot->size += sizeof (Elf32_External_Rel);
+
+             if ((tls_type & GOT_TLS_GD) && indx != 0)
+               htab->srelgot->size += sizeof (Elf32_External_Rel);
+           }
+         else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+                   || h->root.type != bfd_link_hash_undefweak)
+                  && (info->shared
+                  || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
            htab->srelgot->size += sizeof (Elf32_External_Rel);
        }
     }
@@ -4608,7 +5156,28 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
 
   if (info->shared || htab->root.is_relocatable_executable)
     {
-      /* Discard relocs on undefined weak syms with non-default
+      /* The only reloc that uses pc_count is R_ARM_REL32, which will
+        appear on something like ".long foo - .".  We want calls to
+        protected symbols to resolve directly to the function rather
+        than going via the plt.  If people want function pointer
+        comparisons to work as expected then they should avoid
+        writing assembly like ".long foo - .".  */
+      if (SYMBOL_CALLS_LOCAL (info, h))
+       {
+         struct elf32_arm_relocs_copied **pp;
+
+         for (pp = &eh->relocs_copied; (p = *pp) != NULL; )
+           {
+             p->count -= p->pc_count;
+             p->pc_count = 0;
+             if (p->count == 0)
+               *pp = p->next;
+             else
+               pp = &p->next;
+           }
+       }
+
+      /* Also discard relocs on undefined weak syms with non-default
          visibility.  */
       if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
          && h->root.type == bfd_link_hash_undefweak)
@@ -4773,6 +5342,7 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
       symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
       locsymcount = symtab_hdr->sh_info;
       end_local_got = local_got + locsymcount;
+      local_tls_type = elf32_arm_local_got_tls_type (ibfd);
       s = htab->sgot;
       srel = htab->srelgot;
       for (; local_got < end_local_got; ++local_got, ++local_tls_type)
@@ -4780,8 +5350,15 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
          if (*local_got > 0)
            {
              *local_got = s->size;
-             s->size += 4;
-             if (info->shared)
+             if (*local_tls_type & GOT_TLS_GD)
+               /* TLS_GD relocs need an 8-byte structure in the GOT.  */
+               s->size += 8;
+             if (*local_tls_type & GOT_TLS_IE)
+               s->size += 4;
+             if (*local_tls_type == GOT_NORMAL)
+               s->size += 4;
+
+             if (info->shared || *local_tls_type == GOT_TLS_GD)
                srel->size += sizeof (Elf32_External_Rel);
            }
          else
@@ -4789,6 +5366,18 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
        }
     }
 
+  if (htab->tls_ldm_got.refcount > 0)
+    {
+      /* Allocate two GOT entries and one dynamic relocation (if necessary)
+        for R_ARM_TLS_LDM32 relocations.  */
+      htab->tls_ldm_got.offset = htab->sgot->size;
+      htab->sgot->size += 8;
+      if (info->shared)
+       htab->srelgot->size += sizeof (Elf32_External_Rel);
+    }
+  else
+    htab->tls_ldm_got.offset = -1;
+
   /* Allocate global sym .plt and .got entries, and space for global
      sym dynamic relocs.  */
   elf_link_hash_traverse (& htab->root, allocate_dynrelocs, info);
@@ -5059,7 +5648,9 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info * info,
        }
     }
 
-  if (h->got.offset != (bfd_vma) -1)
+  if (h->got.offset != (bfd_vma) -1
+      && (elf32_arm_hash_entry (h)->tls_type & GOT_TLS_GD) == 0
+      && (elf32_arm_hash_entry (h)->tls_type & GOT_TLS_IE) == 0)
     {
       asection * sgot;
       asection * srel;
@@ -5754,6 +6345,8 @@ const struct elf_size_info elf32_arm_size_info = {
 #endif
 #define ELF_MINPAGESIZE                        0x1000
 
+#define bfd_elf32_mkobject                     elf32_arm_mkobject
+
 #define bfd_elf32_bfd_copy_private_bfd_data    elf32_arm_copy_private_bfd_data
 #define bfd_elf32_bfd_merge_private_bfd_data   elf32_arm_merge_private_bfd_data
 #define bfd_elf32_bfd_set_private_flags                elf32_arm_set_private_flags
index 3cfed3a..dadba59 100644 (file)
@@ -1177,6 +1177,14 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_ARM_RELATIVE",
   "BFD_RELOC_ARM_GOTOFF",
   "BFD_RELOC_ARM_GOTPC",
+  "BFD_RELOC_ARM_TLS_GD32",
+  "BFD_RELOC_ARM_TLS_LDO32",
+  "BFD_RELOC_ARM_TLS_LDM32",
+  "BFD_RELOC_ARM_TLS_DTPOFF32",
+  "BFD_RELOC_ARM_TLS_DTPMOD32",
+  "BFD_RELOC_ARM_TLS_TPOFF32",
+  "BFD_RELOC_ARM_TLS_IE32",
+  "BFD_RELOC_ARM_TLS_LE32",
   "BFD_RELOC_ARM_TARGET1",
   "BFD_RELOC_ARM_ROSEGREL32",
   "BFD_RELOC_ARM_SBREL32",
index d952d1b..17f8a45 100644 (file)
@@ -2678,6 +2678,22 @@ ENUMX
   BFD_RELOC_ARM_GOTOFF
 ENUMX
   BFD_RELOC_ARM_GOTPC
+ENUMX
+  BFD_RELOC_ARM_TLS_GD32
+ENUMX
+  BFD_RELOC_ARM_TLS_LDO32
+ENUMX
+  BFD_RELOC_ARM_TLS_LDM32
+ENUMX
+  BFD_RELOC_ARM_TLS_DTPOFF32
+ENUMX
+  BFD_RELOC_ARM_TLS_DTPMOD32
+ENUMX
+  BFD_RELOC_ARM_TLS_TPOFF32
+ENUMX
+  BFD_RELOC_ARM_TLS_IE32
+ENUMX
+  BFD_RELOC_ARM_TLS_LE32
 ENUMDOC
   These relocs are only used within the ARM assembler.  They are not
   (at present) written to any object files.
index 2cc9445..d6263e6 100644 (file)
@@ -1,3 +1,12 @@
+2005-03-29  Daniel Jacobowitz  <dan@codesourcery.com>
+           Phil Blundell  <philb@gnu.org>
+
+       * config/tc-arm.c (arm_parse_reloc): Add TLS relocations.
+       (md_apply_fix3): Mark TLS symbols.
+       (tc_gen_reloc): Handle TLS relocations.
+       (arm_fix_adjustable): Ignore TLS relocations.
+       (s_arm_elf_cons): Support expressions after decorated symbols.
+
 2005-03-29  Julian Brown  <julian@codesourcery.com>
 
        * config/tc-arm.c (marked_pr_dependency): New bitmap, bit N indicates
index 9baf888..78d126c 100644 (file)
@@ -4881,6 +4881,11 @@ arm_parse_reloc (void)
     MAP ("(target1)", BFD_RELOC_ARM_TARGET1),
     MAP ("(sbrel)", BFD_RELOC_ARM_SBREL32),
     MAP ("(target2)", BFD_RELOC_ARM_TARGET2),
+    MAP ("(tlsgd)", BFD_RELOC_ARM_TLS_GD32),
+    MAP ("(tlsldm)", BFD_RELOC_ARM_TLS_LDM32),
+    MAP ("(tlsldo)", BFD_RELOC_ARM_TLS_LDO32),
+    MAP ("(gottpoff)", BFD_RELOC_ARM_TLS_IE32),
+    MAP ("(tpoff)", BFD_RELOC_ARM_TLS_LE32),
     { NULL, 0,         BFD_RELOC_UNUSED }
 #undef MAP
   };
@@ -12224,6 +12229,14 @@ md_apply_fix3 (fixS *   fixP,
       break;
 
 #ifdef OBJ_ELF
+     case BFD_RELOC_ARM_TLS_GD32:
+     case BFD_RELOC_ARM_TLS_LE32:
+     case BFD_RELOC_ARM_TLS_IE32:
+     case BFD_RELOC_ARM_TLS_LDM32:
+     case BFD_RELOC_ARM_TLS_LDO32:
+       S_SET_THREAD_LOCAL (fixP->fx_addsy);
+       /* fall through */
+
     case BFD_RELOC_ARM_GOT32:
     case BFD_RELOC_ARM_GOTOFF:
     case BFD_RELOC_ARM_TARGET2:
@@ -12547,6 +12560,18 @@ tc_gen_reloc (asection * section ATTRIBUTE_UNUSED,
     case BFD_RELOC_ARM_SBREL32:
     case BFD_RELOC_ARM_PREL31:
     case BFD_RELOC_ARM_TARGET2:
+    case BFD_RELOC_ARM_TLS_LE32:
+    case BFD_RELOC_ARM_TLS_LDO32:
+      code = fixp->fx_r_type;
+      break;
+
+    case BFD_RELOC_ARM_TLS_GD32:
+    case BFD_RELOC_ARM_TLS_IE32:
+    case BFD_RELOC_ARM_TLS_LDM32:
+      /* BFD will include the symbol's address in the addend.  
+        But we don't want that, so subtract it out again here.  */
+      if (!S_IS_COMMON (fixp->fx_addsy))
+       reloc->addend -= (*reloc->sym_ptr_ptr)->value;
       code = fixp->fx_r_type;
       break;
 #endif
@@ -13843,6 +13868,11 @@ arm_fix_adjustable (fixS * fixP)
   if (fixP->fx_r_type == BFD_RELOC_ARM_PLT32
       || fixP->fx_r_type == BFD_RELOC_ARM_GOT32
       || fixP->fx_r_type == BFD_RELOC_ARM_GOTOFF
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_GD32
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LE32
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_IE32
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDM32
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDO32
       || fixP->fx_r_type == BFD_RELOC_ARM_TARGET2)
     return 0;
 
@@ -13898,8 +13928,12 @@ s_arm_elf_cons (int nbytes)
   do
     {
       bfd_reloc_code_real_type reloc;
+      char *sym_start;
+      int sym_len;
 
+      sym_start = input_line_pointer;
       expression (& exp);
+      sym_len = input_line_pointer - sym_start;
 
       if (exp.X_op == O_symbol
          && * input_line_pointer == '('
@@ -13913,9 +13947,22 @@ s_arm_elf_cons (int nbytes)
                    howto->name, nbytes);
          else
            {
-             char *p = frag_more ((int) nbytes);
+             char *p;
              int offset = nbytes - size;
-
+             char *saved_buf = alloca (sym_len), *saved_input;
+
+             /* We've parsed an expression stopping at O_symbol.  But there
+                may be more expression left now that we have parsed the
+                relocation marker.  Parse it again.  */
+             saved_input = input_line_pointer - sym_len;
+             memcpy (saved_buf, saved_input, sym_len);
+             memmove (saved_input, sym_start, sym_len);
+             input_line_pointer = saved_input;
+             expression (& exp);
+             memcpy (saved_input, saved_buf, sym_len);
+             assert (input_line_pointer >= saved_input + sym_len);
+
+             p = frag_more ((int) nbytes);
              fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
                           &exp, 0, reloc);
            }
index bef8501..5559174 100644 (file)
@@ -1,3 +1,8 @@
+2005-03-29  Daniel Jacobowitz  <dan@codesourcery.com>
+
+       * gas/arm/tls.s, gas/arm/tls.d: New files.
+       * gas/arm/arm.exp: Run TLS test.
+
 2005-03-28  H.J. Lu  <hongjiu.lu@intel.com>
 
        PR 803
index 4ea6454..7cd5630 100644 (file)
@@ -72,6 +72,8 @@ if {[istarget *arm*-*-*] || [istarget "xscale-*-*"]} then {
        run_dump_test "mapping"
        gas_test "bignum1.s" "" $stdoptlist "bignums"
        run_dump_test "unwind"
+
+       run_dump_test "tls"
     }
 
     if {! [istarget arm*-*-aout] && ![istarget arm-*-pe]} then {
diff --git a/gas/testsuite/gas/arm/tls.d b/gas/testsuite/gas/arm/tls.d
new file mode 100644 (file)
index 0000000..71a1a6f
--- /dev/null
@@ -0,0 +1,21 @@
+#objdump: -dr
+#name: TLS
+
+# Test generation of TLS relocations
+
+.*: +file format .*arm.*
+
+Disassembly of section .text:
+
+00+0 <main>:
+   0:  e1a00000        nop                     \(mov r0,r0\)
+   4:  e1a00000        nop                     \(mov r0,r0\)
+   8:  e1a0f00e        mov     pc, lr
+   c:  00000000        andeq   r0, r0, r0
+                       c: R_ARM_TLS_GD32       a
+  10:  00000004        andeq   r0, r0, r4
+                       10: R_ARM_TLS_LDM32     b
+  14:  00000008        andeq   r0, r0, r8
+                       14: R_ARM_TLS_IE32      c
+  18:  00000000        andeq   r0, r0, r0
+                       18: R_ARM_TLS_LE32      d
diff --git a/gas/testsuite/gas/arm/tls.s b/gas/testsuite/gas/arm/tls.s
new file mode 100644 (file)
index 0000000..48722a4
--- /dev/null
@@ -0,0 +1,14 @@
+       .text
+       .globl main
+       .type main, %function
+main:
+       nop
+.L2:
+       nop
+       mov     pc, lr
+
+.Lpool:
+       .word   a(tlsgd) + (. - .L2 - 8)
+       .word   b(tlsldm) + (. - .L2 - 8)
+       .word   c(gottpoff) + (. - .L2 - 8)
+       .word   d(tpoff)
index b273adb..1d9aedb 100644 (file)
@@ -1,3 +1,8 @@
+2005-03-29  Daniel Jacobowitz  <dan@codesourcery.com>
+           Phil Blundell  <philb@gnu.org>
+
+       * arm.h: Add TLS relocations.
+
 2005-03-23  Ben Elliston  <bje@au.ibm.com>
 
        * dwarf.h: Merge with GCC's dwarf.h.
index de3ed06..81a8de1 100644 (file)
@@ -114,6 +114,9 @@ START_RELOC_NUMBERS (elf_arm_reloc_type)
   RELOC_NUMBER (R_ARM_THM_SWI8,        14)
   RELOC_NUMBER (R_ARM_XPC25,           15)
   RELOC_NUMBER (R_ARM_THM_XPC22,       16)
+  RELOC_NUMBER (R_ARM_TLS_DTPMOD32,    17)
+  RELOC_NUMBER (R_ARM_TLS_DTPOFF32,    18)
+  RELOC_NUMBER (R_ARM_TLS_TPOFF32,     19)
 #endif /* not OLD_ARM_ABI */
   RELOC_NUMBER (R_ARM_COPY,            20)   /* Copy symbol at runtime.  */
   RELOC_NUMBER (R_ARM_GLOB_DAT,        21)   /* Create GOT entry.  */
@@ -153,7 +156,12 @@ START_RELOC_NUMBERS (elf_arm_reloc_type)
   RELOC_NUMBER (R_ARM_GNU_VTINHERIT,  101)
   RELOC_NUMBER (R_ARM_THM_PC11,       102)   /* Cygnus extension to abi: Thumb unconditional branch.  */
   RELOC_NUMBER (R_ARM_THM_PC9,        103)   /* Cygnus extension to abi: Thumb conditional branch.  */
-  FAKE_RELOC   (FIRST_INVALID_RELOC3, 104)
+  RELOC_NUMBER (R_ARM_TLS_GD32,              104)
+  RELOC_NUMBER (R_ARM_TLS_LDM32,      105)
+  RELOC_NUMBER (R_ARM_TLS_LDO32,      106)
+  RELOC_NUMBER (R_ARM_TLS_IE32,       107)
+  RELOC_NUMBER (R_ARM_TLS_LE32,              108)
+  FAKE_RELOC   (FIRST_INVALID_RELOC3, 109)
   FAKE_RELOC   (LAST_INVALID_RELOC3,  248)
   RELOC_NUMBER (R_ARM_RXPC25,         249)
 #endif /* not OLD_ARM_ABI */
index 9ae7391..cd9ca63 100644 (file)
@@ -1,3 +1,11 @@
+2005-03-29  Daniel Jacobowitz  <dan@codesourcery.com>
+
+       * ld-arm/tls-lib.s, ld-arm/tls-lib.d, ld-arm/tls-lib.r,
+       ld-arm/tls-app.s, ld-arm/tls-app.d, ld-arm/tls-app.r: New files.
+       * ld-arm/arm-lib.ld, ld-arm/arm-dyn.ld: Increase data segment
+       alignment.
+       * ld-arm/arm-elf.exp: Run TLS tests.
+
 2005-03-28  H.J. Lu  <hongjiu.lu@intel.com>
 
        PR 803
index 20b9613..96bc10c 100644 (file)
@@ -76,7 +76,7 @@ SECTIONS
   .gcc_except_table   : ONLY_IF_RO { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
   /* Adjust the address for the data segment.  We want to adjust up to
      the same address within the page on the next page up.  */
-  . = ALIGN(256) + (. & (256 - 1));
+  . = ALIGN (0x8000) - ((0x8000 - .) & (0x8000 - 1)); . = DATA_SEGMENT_ALIGN (0x8000, 0x1000);
   /* Exception handling  */
   .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
   .gcc_except_table   : ONLY_IF_RW { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
index 3e747ca..1a0e5b9 100644 (file)
@@ -75,6 +75,12 @@ set armelftests {
     {"arm-rel31" "-static -T arm.ld" "" {arm-rel31.s}
      {{objdump -s arm-rel31.d}}
      "arm-rel31"}
+    {"TLS shared library" "-shared -T arm-lib.ld" "" {tls-lib.s}
+     {{objdump -fdw tls-lib.d} {objdump -Rw tls-lib.r}}
+     "tls-lib.so"}
+    {"TLS dynamic application" "-T arm-dyn.ld tmpdir/tls-lib.so" "" {tls-app.s}
+     {{objdump -fdw tls-app.d} {objdump -Rw tls-app.r}}
+     "tls-app"}
 }
 
 run_ld_link_tests $armelftests
index 075eaa4..0415d20 100644 (file)
@@ -75,7 +75,7 @@ SECTIONS
   .gcc_except_table   : ONLY_IF_RO { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
   /* Adjust the address for the data segment.  We want to adjust up to
      the same address within the page on the next page up.  */
-  . = ALIGN(256) + (. & (256 - 1));
+  . = ALIGN (0x8000) - ((0x8000 - .) & (0x8000 - 1)); . = DATA_SEGMENT_ALIGN (0x8000, 0x1000);
   /* Exception handling  */
   .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
   .gcc_except_table   : ONLY_IF_RW { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
diff --git a/ld/testsuite/ld-arm/tls-app.d b/ld/testsuite/ld-arm/tls-app.d
new file mode 100644 (file)
index 0000000..7f486ef
--- /dev/null
@@ -0,0 +1,18 @@
+
+.*:     file format elf32-.*arm
+architecture: arm, flags 0x00000112:
+EXEC_P, HAS_SYMS, D_PAGED
+start address 0x00008274
+
+Disassembly of section .text:
+
+00008274 <foo>:
+    8274:      e1a00000        nop                     \(mov r0,r0\)
+    8278:      e1a00000        nop                     \(mov r0,r0\)
+    827c:      e1a0f00e        mov     pc, lr
+    8280:      000080bc        streqh  r8, \[r0\], -ip
+    8284:      000080b4        streqh  r8, \[r0\], -r4
+    8288:      000080ac        andeq   r8, r0, ip, lsr #1
+    828c:      00000004        andeq   r0, r0, r4
+    8290:      000080c4        andeq   r8, r0, r4, asr #1
+    8294:      00000014        andeq   r0, r0, r4, lsl r0
diff --git a/ld/testsuite/ld-arm/tls-app.r b/ld/testsuite/ld-arm/tls-app.r
new file mode 100644 (file)
index 0000000..3f02397
--- /dev/null
@@ -0,0 +1,12 @@
+
+.*:     file format elf32-.*arm
+
+DYNAMIC RELOCATION RECORDS
+OFFSET   TYPE              VALUE 
+00010334 R_ARM_TLS_DTPMOD32  app_gd
+00010338 R_ARM_TLS_DTPOFF32  app_gd
+0001033c R_ARM_TLS_DTPMOD32  lib_gd
+00010340 R_ARM_TLS_DTPOFF32  lib_gd
+00010344 R_ARM_TLS_TPOFF32  app_ie
+
+
diff --git a/ld/testsuite/ld-arm/tls-app.s b/ld/testsuite/ld-arm/tls-app.s
new file mode 100644 (file)
index 0000000..d505295
--- /dev/null
@@ -0,0 +1,34 @@
+       .text
+       .globl foo
+       .type foo, %function
+foo:
+       nop
+.L2:
+       nop
+       mov     pc, lr
+
+.Lpool:
+       .word   lib_gd(tlsgd) + (. - .L2 - 8)
+       .word   app_gd(tlsgd) + (. - .L2 - 8)
+       .word   app_ld(tlsldm) + (. - .L2 - 8)
+       .word   app_ld(tlsldo)
+       .word   app_ie(gottpoff) + (. - .L2 - 8)
+       .word   app_le(tpoff)
+
+       .section .tdata,"awT"
+       .global app_gd
+app_gd:
+       .space  4
+
+       .global app_ld
+app_ld:
+       .space  4
+
+       .section .tbss,"awT",%nobits
+       .global app_ie
+app_ie:
+       .space  4
+
+       .global app_le
+app_le:
+       .space  4
diff --git a/ld/testsuite/ld-arm/tls-lib.d b/ld/testsuite/ld-arm/tls-lib.d
new file mode 100644 (file)
index 0000000..0492877
--- /dev/null
@@ -0,0 +1,15 @@
+
+.*:     file format elf32-.*arm
+architecture: arm, flags 0x00000150:
+HAS_SYMS, DYNAMIC, D_PAGED
+start address 0x.*
+
+Disassembly of section .text:
+
+00000328 <foo>:
+ 328:  e1a00000        nop                     \(mov r0,r0\)
+ 32c:  e1a00000        nop                     \(mov r0,r0\)
+ 330:  e1a0f00e        mov     pc, lr
+ 334:  00008098        muleq   r0, r8, r0
+ 338:  0000808c        andeq   r8, r0, ip, lsl #1
+ 33c:  00000004        andeq   r0, r0, r4
diff --git a/ld/testsuite/ld-arm/tls-lib.r b/ld/testsuite/ld-arm/tls-lib.r
new file mode 100644 (file)
index 0000000..c0bf4bf
--- /dev/null
@@ -0,0 +1,10 @@
+
+.*:     file format elf32-.*arm
+
+DYNAMIC RELOCATION RECORDS
+OFFSET   TYPE              VALUE 
+000083c4 R_ARM_TLS_DTPMOD32  \*ABS\*
+000083cc R_ARM_TLS_DTPMOD32  lib_gd
+000083d0 R_ARM_TLS_DTPOFF32  lib_gd
+
+
diff --git a/ld/testsuite/ld-arm/tls-lib.s b/ld/testsuite/ld-arm/tls-lib.s
new file mode 100644 (file)
index 0000000..fa928c0
--- /dev/null
@@ -0,0 +1,22 @@
+       .text
+       .globl foo
+       .type foo, %function
+foo:
+       nop
+.L2:
+       nop
+       mov     pc, lr
+
+.Lpool:
+       .word   lib_gd(tlsgd) + (. - .L2 - 8)
+       .word   lib_ld(tlsldm) + (. - .L2 - 8)
+       .word   lib_ld(tlsldo)
+
+       .section .tdata,"awT"
+       .global lib_gd
+lib_gd:
+       .space  4
+
+       .global lib_ld
+lib_ld:
+       .space  4