* mn10300.h (elf_mn10300_reloc_type): Add R_MN10300_TLS_GD,
authorNick Clifton <nickc@redhat.com>
Wed, 7 Mar 2012 17:52:00 +0000 (17:52 +0000)
committerNick Clifton <nickc@redhat.com>
Wed, 7 Mar 2012 17:52:00 +0000 (17:52 +0000)
R_MN10300_TLS_LD, R_MN10300_TLS_LDO, R_MN10300_TLS_GOTIE,
R_MN10300_TLS_IE, R_MN10300_TLS_LE, R_MN10300_TLS_DPTMOD,
R_MN10300_TLS_DTPOFF and R_MN10300_TLS_TPOFF.

* elf-m10300.c (elf32_mn10300_link_hash_entry): Add tls_type
field.
(elf32_mn10300_link_hash_table): Add tls_ldm_got entry;
(elf_mn10300_tdata): Define.
(elf_mn10300_local_got_tls_type): Define.
(elf_mn10300_howto_table): Add entries for R_MN10300_TLS_GD,
R_MN10300_TLS_LD, R_MN10300_TLS_LDO, R_MN10300_TLS_GOTIE,
R_MN10300_TLS_IE, R_MN10300_TLS_LE, R_MN10300_TLS_DPTMOD,
R_MN10300_TLS_DTPOFF, R_MN10300_TLS_TPOFF relocs.
(mn10300_reloc_map): Likewise.
(elf_mn10300_tls_transition): New function.
(dtpoff, tpoff, mn10300_do_tls_transition): New functions.
(mn10300_elf_check_relocs): Add TLS support.
(mn10300_elf_final_link_relocate): Likewise.
(mn10300_elf_relocate_section): Likewise.
(mn10300_elf_relax_section): Likewise.
(elf32_mn10300_link_hash_newfunc): Initialise new field.
(_bfd_mn10300_copy_indirect_symbol): New function.
(elf32_mn10300_link_hash_table_create): Initialise new fields.
(_bfd_mn10300_elf_size_dynamic_sections): Add TLS support.
(_bfd_mn10300_elf_finish_dynamic_symbol): Likewise.
(_bfd_mn10300_elf_reloc_type_class): Allocate an
elf_mn10300_obj_tdata structure.
(elf_backend_copy_indirect_symbol): Define.
* reloc.c (BFD_MN10300_TLS_GD, BFD_MN10300_TLS_LD,
BFD_MN10300_TLS_LDO, BFD_MN10300_TLS_GOTIE, BFD_MN10300_TLS_IE,
BFD_MN10300_TLS_LE, BFD_MN10300_TLS_DPTMOD,
BFD_MN10300_TLS_DTPOFF, BFD_MN10300_TLS_TPOFF): New relocations.
(BFD_RELOC_MN10300_32_PCREL, BFD_RELOC_MN10300_16_PCREL): Move to
alongside other MN10300 relocations.
* bfd-in2.h: Regenerate.
* libbfd.h: Regenerate.

* config/tc-mn10300.c (other_registers): Add SSP and USP.
(md_assemble): Add support for TLS relocs.
(mn10300_parse_name): Likewise.

* readelf.c (is_16bit_abs_reloc): Add detection of R_MN10300_16.

bfd/ChangeLog
bfd/bfd-in2.h
bfd/elf-m10300.c
bfd/libbfd.h
bfd/reloc.c
binutils/ChangeLog
binutils/readelf.c
gas/ChangeLog
gas/config/tc-mn10300.c
include/elf/ChangeLog
include/elf/mn10300.h

index 4a951bb797d9cf006c3c06ecc46c60fc98099687..ce08017ac482bbc2cd0781403929fc9876234155 100644 (file)
@@ -1,3 +1,38 @@
+2012-03-07  Nick Clifton  <nickc@redhat.com>
+
+       * elf-m10300.c (elf32_mn10300_link_hash_entry): Add tls_type
+       field.
+       (elf32_mn10300_link_hash_table): Add tls_ldm_got entry;
+       (elf_mn10300_tdata): Define.
+       (elf_mn10300_local_got_tls_type): Define.
+       (elf_mn10300_howto_table): Add entries for R_MN10300_TLS_GD,
+       R_MN10300_TLS_LD, R_MN10300_TLS_LDO, R_MN10300_TLS_GOTIE,
+       R_MN10300_TLS_IE, R_MN10300_TLS_LE, R_MN10300_TLS_DPTMOD,
+       R_MN10300_TLS_DTPOFF, R_MN10300_TLS_TPOFF relocs.
+       (mn10300_reloc_map): Likewise.
+       (elf_mn10300_tls_transition): New function.
+       (dtpoff, tpoff, mn10300_do_tls_transition): New functions.
+       (mn10300_elf_check_relocs): Add TLS support.
+       (mn10300_elf_final_link_relocate): Likewise.
+       (mn10300_elf_relocate_section): Likewise.
+       (mn10300_elf_relax_section): Likewise.
+       (elf32_mn10300_link_hash_newfunc): Initialise new field.
+       (_bfd_mn10300_copy_indirect_symbol): New function.
+       (elf32_mn10300_link_hash_table_create): Initialise new fields.
+       (_bfd_mn10300_elf_size_dynamic_sections): Add TLS support.
+       (_bfd_mn10300_elf_finish_dynamic_symbol): Likewise.
+       (_bfd_mn10300_elf_reloc_type_class): Allocate an
+       elf_mn10300_obj_tdata structure.
+       (elf_backend_copy_indirect_symbol): Define.
+       * reloc.c (BFD_MN10300_TLS_GD, BFD_MN10300_TLS_LD,
+       BFD_MN10300_TLS_LDO, BFD_MN10300_TLS_GOTIE, BFD_MN10300_TLS_IE,
+       BFD_MN10300_TLS_LE, BFD_MN10300_TLS_DPTMOD,
+       BFD_MN10300_TLS_DTPOFF, BFD_MN10300_TLS_TPOFF): New relocations.
+       (BFD_RELOC_MN10300_32_PCREL, BFD_RELOC_MN10300_16_PCREL): Move to
+       alongside other MN10300 relocations.
+       * bfd-in2.h: Regenerate.
+       * libbfd.h: Regenerate.
+
 2012-03-06  Jakub Jelinek  <jakub@redhat.com>
 
        * elf64-x86-64.c (elf_x86_64_relocate_section): For R_X86_64_RELATIVE
index 9c77fed8c50c91ec8226c2885f4a0c23c9daecb5..bb438601176007768c48f1ad6a49ffd939b575a9 100644 (file)
@@ -2963,6 +2963,25 @@ be honoured at the offset's location, regardless of linker
 relaxation.  */
   BFD_RELOC_MN10300_ALIGN,
 
+/* Various TLS-related relocations.  */
+  BFD_RELOC_MN10300_TLS_GD,
+  BFD_RELOC_MN10300_TLS_LD,
+  BFD_RELOC_MN10300_TLS_LDO,
+  BFD_RELOC_MN10300_TLS_GOTIE,
+  BFD_RELOC_MN10300_TLS_IE,
+  BFD_RELOC_MN10300_TLS_LE,
+  BFD_RELOC_MN10300_TLS_DTPMOD,
+  BFD_RELOC_MN10300_TLS_DTPOFF,
+  BFD_RELOC_MN10300_TLS_TPOFF,
+
+/* This is a 32bit pcrel reloc for the mn10300, offset by two bytes in the
+instruction.  */
+  BFD_RELOC_MN10300_32_PCREL,
+
+/* This is a 16bit pcrel reloc for the mn10300, offset by two bytes in the
+instruction.  */
+  BFD_RELOC_MN10300_16_PCREL,
+
 
 /* i386/elf relocations  */
   BFD_RELOC_386_GOT32,
@@ -3798,14 +3817,6 @@ instructions.  */
 /* start data in text.  */
   BFD_RELOC_V850_DATA,
 
-/* This is a 32bit pcrel reloc for the mn10300, offset by two bytes in the
-instruction.  */
-  BFD_RELOC_MN10300_32_PCREL,
-
-/* This is a 16bit pcrel reloc for the mn10300, offset by two bytes in the
-instruction.  */
-  BFD_RELOC_MN10300_16_PCREL,
-
 /* This is a 8bit DP reloc for the tms320c30, where the most
 significant 8 bits of a 24 bit word are placed into the least
 significant 8 bits of the opcode.  */
index fa33b4a2fbe19f6be4b9549b0b68feba80242175..d5e183df21a3f5c4896d2283e2198a8bb1f87725 100644 (file)
@@ -69,6 +69,14 @@ struct elf32_mn10300_link_hash_entry
 
   /* Calculated value.  */
   bfd_vma value;
+
+#define GOT_UNKNOWN    0
+#define GOT_NORMAL     1
+#define GOT_TLS_GD     2
+#define GOT_TLS_LD     3
+#define GOT_TLS_IE     4
+  /* Used to distinguish GOT entries for TLS types from normal GOT entries.  */
+  unsigned char tls_type;
 };
 
 /* We derive a hash table from the main elf linker hash table so
@@ -87,8 +95,31 @@ struct elf32_mn10300_link_hash_table
   /* Random linker state flags.  */
 #define MN10300_HASH_ENTRIES_INITIALIZED 0x1
   char flags;
+  struct
+  {
+    bfd_signed_vma  refcount;
+    bfd_vma         offset;
+    char            got_allocated;
+    char            rel_emitted;
+  } tls_ldm_got;
 };
 
+#define elf_mn10300_hash_entry(ent) ((struct elf32_mn10300_link_hash_entry *)(ent))
+
+struct elf_mn10300_obj_tdata
+{
+  struct elf_obj_tdata root;
+
+  /* tls_type for each local got entry.  */
+  char * local_got_tls_type;
+};
+
+#define elf_mn10300_tdata(abfd) \
+  ((struct elf_mn10300_obj_tdata *) (abfd)->tdata.any)
+
+#define elf_mn10300_local_got_tls_type(abfd) \
+  (elf_mn10300_tdata (abfd)->local_got_tls_type)
+
 #ifndef streq
 #define streq(a, b) (strcmp ((a),(b)) == 0)
 #endif
@@ -448,15 +479,131 @@ static reloc_howto_type elf_mn10300_howto_table[] =
         0xffffffff,            /* dst_mask */
         FALSE),                /* pcrel_offset */
 
-  EMPTY_HOWTO (24),
-  EMPTY_HOWTO (25),
-  EMPTY_HOWTO (26),
-  EMPTY_HOWTO (27),
-  EMPTY_HOWTO (28),
-  EMPTY_HOWTO (29),
-  EMPTY_HOWTO (30),
-  EMPTY_HOWTO (31),
-  EMPTY_HOWTO (32),
+  HOWTO (R_MN10300_TLS_GD,     /* 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, /* */
+        "R_MN10300_TLS_GD",    /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO (R_MN10300_TLS_LD,     /* 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, /* */
+        "R_MN10300_TLS_LD",    /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO (R_MN10300_TLS_LDO,    /* 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, /* */
+        "R_MN10300_TLS_LDO",   /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO (R_MN10300_TLS_GOTIE,  /* 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, /* */
+        "R_MN10300_TLS_GOTIE", /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO (R_MN10300_TLS_IE,     /* 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, /* */
+        "R_MN10300_TLS_IE",    /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO (R_MN10300_TLS_LE,     /* 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, /* */
+        "R_MN10300_TLS_LE",    /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO (R_MN10300_TLS_DTPMOD, /* 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, /* */
+        "R_MN10300_TLS_DTPMOD",        /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO (R_MN10300_TLS_DTPOFF, /* 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, /* */
+        "R_MN10300_TLS_DTPOFF",        /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO (R_MN10300_TLS_TPOFF,  /* 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, /* */
+        "R_MN10300_TLS_TPOFF", /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
   
   HOWTO (R_MN10300_SYM_DIFF,   /* type */
         0,                     /* rightshift */
@@ -519,6 +666,15 @@ static const struct mn10300_reloc_map mn10300_reloc_map[] =
   { BFD_RELOC_MN10300_GLOB_DAT, R_MN10300_GLOB_DAT },
   { BFD_RELOC_MN10300_JMP_SLOT, R_MN10300_JMP_SLOT },
   { BFD_RELOC_MN10300_RELATIVE, R_MN10300_RELATIVE },
+  { BFD_RELOC_MN10300_TLS_GD, R_MN10300_TLS_GD },
+  { BFD_RELOC_MN10300_TLS_LD, R_MN10300_TLS_LD },
+  { BFD_RELOC_MN10300_TLS_LDO, R_MN10300_TLS_LDO },
+  { BFD_RELOC_MN10300_TLS_GOTIE, R_MN10300_TLS_GOTIE },
+  { BFD_RELOC_MN10300_TLS_IE, R_MN10300_TLS_IE },
+  { BFD_RELOC_MN10300_TLS_LE, R_MN10300_TLS_LE },
+  { BFD_RELOC_MN10300_TLS_DTPMOD, R_MN10300_TLS_DTPMOD },
+  { BFD_RELOC_MN10300_TLS_DTPOFF, R_MN10300_TLS_DTPOFF },
+  { BFD_RELOC_MN10300_TLS_TPOFF, R_MN10300_TLS_TPOFF },
   { BFD_RELOC_MN10300_SYM_DIFF, R_MN10300_SYM_DIFF },
   { BFD_RELOC_MN10300_ALIGN, R_MN10300_ALIGN }
 };
@@ -650,6 +806,223 @@ mn10300_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED,
   cache_ptr->howto = elf_mn10300_howto_table + r_type;
 }
 
+static int
+elf_mn10300_tls_transition (struct bfd_link_info *        info,
+                           int                           r_type,
+                           struct elf_link_hash_entry *  h,
+                           asection *                    sec,
+                           bfd_boolean                   counting)
+{
+  bfd_boolean is_local;
+
+  if (r_type == R_MN10300_TLS_GD
+      && h != NULL
+      && elf_mn10300_hash_entry (h)->tls_type == GOT_TLS_IE)
+    return R_MN10300_TLS_GOTIE;
+
+  if (info->shared)
+    return r_type;
+
+  if (! (sec->flags & SEC_CODE))
+    return r_type;
+
+  if (! counting && h != NULL && ! elf_hash_table (info)->dynamic_sections_created)
+    is_local = TRUE;
+  else
+    is_local = SYMBOL_CALLS_LOCAL (info, h);
+
+  /* For the main program, these are the transitions we do.  */
+  switch (r_type)
+    {
+    case R_MN10300_TLS_GD: return is_local ? R_MN10300_TLS_LE : R_MN10300_TLS_GOTIE;
+    case R_MN10300_TLS_LD: return R_MN10300_NONE;
+    case R_MN10300_TLS_LDO: return R_MN10300_TLS_LE;
+    case R_MN10300_TLS_IE:
+    case R_MN10300_TLS_GOTIE: return is_local ? R_MN10300_TLS_LE : r_type;
+    }
+
+  return r_type;
+}
+
+/* Return the relocation value for @tpoff relocation
+   if STT_TLS virtual address is ADDRESS.  */
+
+static bfd_vma
+dtpoff (struct bfd_link_info * info, bfd_vma address)
+{
+  struct elf_link_hash_table *htab = elf_hash_table (info);
+
+  /* If tls_sec is NULL, we should have signalled an error already.  */
+  if (htab->tls_sec == NULL)
+    return 0;
+  return address - htab->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);
+
+  /* If tls_sec is NULL, we should have signalled an error already.  */
+  if (htab->tls_sec == NULL)
+    return 0;
+  return address - (htab->tls_size + htab->tls_sec->vma);
+}
+
+/* Returns nonzero if there's a R_MN10300_PLT32 reloc that we now need
+   to skip, after this one.  The actual value is the offset between
+   this reloc and the PLT reloc.  */
+
+static int
+mn10300_do_tls_transition (bfd *         input_bfd,
+                          unsigned int  r_type,
+                          unsigned int  tls_r_type,
+                          bfd_byte *    contents,
+                          bfd_vma       offset)
+{
+  bfd_byte *op = contents + offset;
+  int gotreg = 0;
+
+#define TLS_PAIR(r1,r2) ((r1) * R_MN10300_MAX + (r2))
+
+  /* This is common to all GD/LD transitions, so break it out.  */
+  if (r_type == R_MN10300_TLS_GD
+      || r_type == R_MN10300_TLS_LD)
+    {
+      op -= 2;
+      /* mov imm,d0.  */
+      BFD_ASSERT (bfd_get_8 (input_bfd, op) == 0xFC);
+      BFD_ASSERT (bfd_get_8 (input_bfd, op + 1) == 0xCC);
+      /* add aN,d0.  */
+      BFD_ASSERT (bfd_get_8 (input_bfd, op + 6) == 0xF1);
+      gotreg = (bfd_get_8 (input_bfd, op + 7) & 0x0c) >> 2;
+      /* Call.  */
+      BFD_ASSERT (bfd_get_8 (input_bfd, op + 8) == 0xDD);
+    }
+
+  switch (TLS_PAIR (r_type, tls_r_type))
+    {
+    case TLS_PAIR (R_MN10300_TLS_GD, R_MN10300_TLS_GOTIE):
+      {
+       /* Keep track of which register we put GOTptr in.  */
+       /* mov (_x@indntpoff,a2),a0.  */
+       memcpy (op, "\xFC\x20\x00\x00\x00\x00", 6);
+       op[1] |= gotreg;
+       /* add e2,a0.  */
+       memcpy (op+6, "\xF9\x78\x28", 3);
+       /* or  0x00000000, d0 - six byte nop.  */
+       memcpy (op+9, "\xFC\xE4\x00\x00\x00\x00", 6);
+      }
+      return 7;
+
+    case TLS_PAIR (R_MN10300_TLS_GD, R_MN10300_TLS_LE):
+      {
+       /* Register is *always* a0.  */
+       /* mov _x@tpoff,a0.  */
+       memcpy (op, "\xFC\xDC\x00\x00\x00\x00", 6);
+       /* add e2,a0.  */
+       memcpy (op+6, "\xF9\x78\x28", 3);
+       /* or  0x00000000, d0 - six byte nop.  */
+       memcpy (op+9, "\xFC\xE4\x00\x00\x00\x00", 6);
+      }
+      return 7;
+    case TLS_PAIR (R_MN10300_TLS_LD, R_MN10300_NONE):
+      {
+       /* Register is *always* a0.  */
+       /* mov e2,a0.  */
+       memcpy (op, "\xF5\x88", 2);
+       /* or  0x00000000, d0 - six byte nop.  */
+       memcpy (op+2, "\xFC\xE4\x00\x00\x00\x00", 6);
+       /* or  0x00000000, e2 - seven byte nop.  */
+       memcpy (op+8, "\xFE\x19\x22\x00\x00\x00\x00", 7);
+      }
+      return 7;
+
+    case TLS_PAIR (R_MN10300_TLS_LDO, R_MN10300_TLS_LE):
+      /* No changes needed, just the reloc change.  */
+      return 0;
+
+    /*  These are a little tricky, because we have to detect which
+       opcode is being used (they're different sizes, with the reloc
+       at different offsets within the opcode) and convert each
+       accordingly, copying the operands as needed.  The conversions
+       we do are as follows (IE,GOTIE,LE):
+
+                  1111 1100  1010 01Dn  [-- abs32 --]  MOV (x@indntpoff),Dn
+                  1111 1100  0000 DnAm  [-- abs32 --]  MOV (x@gotntpoff,Am),Dn
+                  1111 1100  1100 11Dn  [-- abs32 --]  MOV x@tpoff,Dn
+
+                  1111 1100  1010 00An  [-- abs32 --]  MOV (x@indntpoff),An
+                  1111 1100  0010 AnAm  [-- abs32 --]  MOV (x@gotntpoff,Am),An
+                  1111 1100  1101 11An  [-- abs32 --]  MOV x@tpoff,An
+
+       1111 1110  0000 1110  Rnnn Xxxx  [-- abs32 --]  MOV (x@indntpoff),Rn
+       1111 1110  0000 1010  Rnnn Rmmm  [-- abs32 --]  MOV (x@indntpoff,Rm),Rn
+       1111 1110  0000 1000  Rnnn Xxxx  [-- abs32 --]  MOV x@tpoff,Rn
+
+       Since the GOT pointer is always $a2, we assume the last
+       normally won't happen, but let's be paranoid and plan for the
+       day that GCC optimizes it somewhow.  */
+
+    case TLS_PAIR (R_MN10300_TLS_IE, R_MN10300_TLS_LE):
+      if (op[-2] == 0xFC)
+       {
+         op -= 2;
+         if ((op[1] & 0xFC) == 0xA4) /* Dn */
+           {
+             op[1] &= 0x03; /* Leaves Dn.  */
+             op[1] |= 0xCC;
+           }
+         else /* An */
+           {
+             op[1] &= 0x03; /* Leaves An. */
+             op[1] |= 0xDC;
+           }
+       }
+      else if (op[-3] == 0xFE)
+       op[-2] = 0x08;
+      else
+       abort ();
+      break;
+
+    case TLS_PAIR (R_MN10300_TLS_GOTIE, R_MN10300_TLS_LE):
+      if (op[-2] == 0xFC)
+       {
+         op -= 2;
+         if ((op[1] & 0xF0) == 0x00) /* Dn */
+           {
+             op[1] &= 0x0C; /* Leaves Dn.  */
+             op[1] >>= 2;
+             op[1] |= 0xCC;
+           }
+         else /* An */
+           {
+             op[1] &= 0x0C; /* Leaves An.  */
+             op[1] >>= 2;
+             op[1] |= 0xDC;
+           }
+       }
+      else if (op[-3] == 0xFE)
+       op[-2] = 0x08;
+      else
+       abort ();
+      break;
+
+    default:
+      (*_bfd_error_handler)
+       (_("%s: Unsupported transition from %s to %s"),
+        bfd_get_filename (input_bfd),
+        elf_mn10300_howto_table[r_type].name,
+        elf_mn10300_howto_table[tls_r_type].name);
+      break;
+    }
+#undef TLS_PAIR
+  return 0;
+}
+
 /* Look through the relocs for a section during the first phase.
    Since we don't do .gots or .plts, we just need to consider the
    virtual table relocs for gc.  */
@@ -660,6 +1033,7 @@ mn10300_elf_check_relocs (bfd *abfd,
                          asection *sec,
                          const Elf_Internal_Rela *relocs)
 {
+  struct elf32_mn10300_link_hash_table * htab = elf32_mn10300_hash_table (info);
   bfd_boolean sym_diff_reloc_seen;
   Elf_Internal_Shdr *symtab_hdr;
   Elf_Internal_Sym * isymbuf = NULL;
@@ -694,6 +1068,7 @@ mn10300_elf_check_relocs (bfd *abfd,
       struct elf_link_hash_entry *h;
       unsigned long r_symndx;
       unsigned int r_type;
+      int tls_type = GOT_NORMAL;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
@@ -707,6 +1082,7 @@ mn10300_elf_check_relocs (bfd *abfd,
        }
 
       r_type = ELF32_R_TYPE (rel->r_info);
+      r_type = elf_mn10300_tls_transition (info, r_type, h, sec, TRUE);
 
       /* Some relocs require a global offset table.  */
       if (dynobj == NULL)
@@ -721,6 +1097,10 @@ mn10300_elf_check_relocs (bfd *abfd,
            case R_MN10300_GOTOFF16:
            case R_MN10300_GOTPC32:
            case R_MN10300_GOTPC16:
+           case R_MN10300_TLS_GD:
+           case R_MN10300_TLS_LD:
+           case R_MN10300_TLS_GOTIE:
+           case R_MN10300_TLS_IE:
              elf_hash_table (info)->dynobj = dynobj = abfd;
              if (! _bfd_mn10300_elf_create_got_section (dynobj, info))
                goto fail;
@@ -749,11 +1129,35 @@ mn10300_elf_check_relocs (bfd *abfd,
            goto fail;
          break;
 
+       case R_MN10300_TLS_LD:
+         htab->tls_ldm_got.refcount ++;
+         tls_type = GOT_TLS_LD;
+
+         if (htab->tls_ldm_got.got_allocated)
+           break;
+         goto create_got;
+
+       case R_MN10300_TLS_IE:
+       case R_MN10300_TLS_GOTIE:
+         if (info->shared)
+           info->flags |= DF_STATIC_TLS;
+         /* Fall through */
+         
+       case R_MN10300_TLS_GD:
        case R_MN10300_GOT32:
        case R_MN10300_GOT24:
        case R_MN10300_GOT16:
+       create_got:
          /* This symbol requires a global offset table entry.  */
 
+         switch (r_type)
+           {
+           case R_MN10300_TLS_IE:
+           case R_MN10300_TLS_GOTIE: tls_type = GOT_TLS_IE; break;
+           case R_MN10300_TLS_GD:    tls_type = GOT_TLS_GD; break;
+           default:                  tls_type = GOT_NORMAL; break;
+           }
+
          if (sgot == NULL)
            {
              sgot = bfd_get_section_by_name (dynobj, ".got");
@@ -780,22 +1184,48 @@ mn10300_elf_check_relocs (bfd *abfd,
                }
            }
 
-         if (h != NULL)
+         if (r_type == R_MN10300_TLS_LD)
+           {
+             htab->tls_ldm_got.offset = sgot->size;
+             htab->tls_ldm_got.got_allocated ++;
+           }
+         else if (h != NULL)
            {
+             if (elf_mn10300_hash_entry (h)->tls_type != tls_type
+                 && elf_mn10300_hash_entry (h)->tls_type != GOT_UNKNOWN)
+               {
+                 if (tls_type == GOT_TLS_IE
+                     && elf_mn10300_hash_entry (h)->tls_type == GOT_TLS_GD)
+                   /* No change - this is ok.  */;
+                 else if (tls_type == GOT_TLS_GD
+                     && elf_mn10300_hash_entry (h)->tls_type == GOT_TLS_IE)
+                   /* Transition GD->IE.  */
+                   tls_type = GOT_TLS_IE;
+                 else
+                   (*_bfd_error_handler)
+                     (_("%B: %s' accessed both as normal and thread local symbol"),
+                      abfd, h ? h->root.root.string : "<local>");
+               }
+
+             elf_mn10300_hash_entry (h)->tls_type = tls_type;
+
              if (h->got.offset != (bfd_vma) -1)
                /* We have already allocated space in the .got.  */
                break;
 
              h->got.offset = sgot->size;
 
-             /* Make sure this symbol is output as a dynamic symbol.  */
-             if (h->dynindx == -1)
+             if (ELF_ST_VISIBILITY (h->other) != STV_INTERNAL
+                 /* Make sure this symbol is output as a dynamic symbol.  */
+                 && h->dynindx == -1)
                {
                  if (! bfd_elf_link_record_dynamic_symbol (info, h))
                    goto fail;
                }
 
              srelgot->size += sizeof (Elf32_External_Rela);
+             if (r_type == R_MN10300_TLS_GD)
+               srelgot->size += sizeof (Elf32_External_Rela);
            }
          else
            {
@@ -806,13 +1236,15 @@ mn10300_elf_check_relocs (bfd *abfd,
                  size_t       size;
                  unsigned int i;
 
-                 size = symtab_hdr->sh_info * sizeof (bfd_vma);
+                 size = symtab_hdr->sh_info * (sizeof (bfd_vma) + sizeof (char));
                  local_got_offsets = bfd_alloc (abfd, size);
 
                  if (local_got_offsets == NULL)
                    goto fail;
 
                  elf_local_got_offsets (abfd) = local_got_offsets;
+                 elf_mn10300_local_got_tls_type (abfd)
+                     = (char *) (local_got_offsets + symtab_hdr->sh_info);
 
                  for (i = 0; i < symtab_hdr->sh_info; i++)
                    local_got_offsets[i] = (bfd_vma) -1;
@@ -825,14 +1257,26 @@ mn10300_elf_check_relocs (bfd *abfd,
              local_got_offsets[r_symndx] = sgot->size;
 
              if (info->shared)
-               /* If we are generating a shared object, we need to
-                  output a R_MN10300_RELATIVE reloc so that the dynamic
-                  linker can adjust this GOT entry.  */
-               srelgot->size += sizeof (Elf32_External_Rela);
+               {
+                 /* If we are generating a shared object, we need to
+                    output a R_MN10300_RELATIVE reloc so that the dynamic
+                    linker can adjust this GOT entry.  */
+                 srelgot->size += sizeof (Elf32_External_Rela);
+
+                 if (r_type == R_MN10300_TLS_GD)
+                   /* And a R_MN10300_TLS_DTPOFF reloc as well.  */
+                   srelgot->size += sizeof (Elf32_External_Rela);
+               }
+
+             elf_mn10300_local_got_tls_type (abfd) [r_symndx] = tls_type;
            }
 
          sgot->size += 4;
-         break;
+         if (r_type == R_MN10300_TLS_GD
+             || r_type == R_MN10300_TLS_LD)
+           sgot->size += 4;
+
+         goto need_shared_relocs;
 
        case R_MN10300_PLT32:
        case R_MN10300_PLT16:
@@ -873,6 +1317,7 @@ mn10300_elf_check_relocs (bfd *abfd,
          if (h != NULL)
            h->non_got_ref = 1;
 
+       need_shared_relocs:
          /* If we are creating a shared library, then we
             need to copy the reloc into the shared library.  */
          if (info->shared
@@ -981,6 +1426,7 @@ mn10300_elf_final_link_relocate (reloc_howto_type *howto,
                                 asection *sym_sec ATTRIBUTE_UNUSED,
                                 int is_local ATTRIBUTE_UNUSED)
 {
+  struct elf32_mn10300_link_hash_table * htab = elf32_mn10300_hash_table (info);
   static asection *  sym_diff_section;
   static bfd_vma     sym_diff_value;
   bfd_boolean is_sym_diff_reloc;
@@ -1012,6 +1458,17 @@ mn10300_elf_final_link_relocate (reloc_howto_type *howto,
          && h != NULL
          && ! SYMBOL_REFERENCES_LOCAL (info, h))
        return bfd_reloc_dangerous;
+    case R_MN10300_GOT32:
+      /* Issue 2052223:
+        Taking the address of a protected function in a shared library
+        is illegal.  Issue an error message here.  */
+      if (info->shared
+         && (input_section->flags & SEC_ALLOC) != 0
+         && h != NULL
+         && ELF_ST_VISIBILITY (h->other) == STV_PROTECTED
+         && (h->type == STT_FUNC || h->type == STT_GNU_IFUNC)
+         && ! SYMBOL_REFERENCES_LOCAL (info, h))
+       return bfd_reloc_dangerous;
     }
 
   is_sym_diff_reloc = FALSE;
@@ -1208,6 +1665,9 @@ mn10300_elf_final_link_relocate (reloc_howto_type *howto,
       return bfd_reloc_ok;
 
     case R_MN10300_GOTPC32:
+      if (dynobj == NULL)
+       return bfd_reloc_dangerous;
+
       /* Use global offset table as symbol value.  */
       value = bfd_get_section_by_name (dynobj,
                                       ".got")->output_section->vma;
@@ -1220,6 +1680,9 @@ mn10300_elf_final_link_relocate (reloc_howto_type *howto,
       return bfd_reloc_ok;
 
     case R_MN10300_GOTPC16:
+      if (dynobj == NULL)
+       return bfd_reloc_dangerous;
+
       /* Use global offset table as symbol value.  */
       value = bfd_get_section_by_name (dynobj,
                                       ".got")->output_section->vma;
@@ -1235,6 +1698,9 @@ mn10300_elf_final_link_relocate (reloc_howto_type *howto,
       return bfd_reloc_ok;
 
     case R_MN10300_GOTOFF32:
+      if (dynobj == NULL)
+       return bfd_reloc_dangerous;
+
       value -= bfd_get_section_by_name (dynobj,
                                        ".got")->output_section->vma;
       value += addend;
@@ -1243,6 +1709,9 @@ mn10300_elf_final_link_relocate (reloc_howto_type *howto,
       return bfd_reloc_ok;
 
     case R_MN10300_GOTOFF24:
+      if (dynobj == NULL)
+       return bfd_reloc_dangerous;
+
       value -= bfd_get_section_by_name (dynobj,
                                        ".got")->output_section->vma;
       value += addend;
@@ -1256,6 +1725,9 @@ mn10300_elf_final_link_relocate (reloc_howto_type *howto,
       return bfd_reloc_ok;
 
     case R_MN10300_GOTOFF16:
+      if (dynobj == NULL)
+       return bfd_reloc_dangerous;
+
       value -= bfd_get_section_by_name (dynobj,
                                        ".got")->output_section->vma;
       value += addend;
@@ -1272,6 +1744,9 @@ mn10300_elf_final_link_relocate (reloc_howto_type *howto,
          && ELF_ST_VISIBILITY (h->other) != STV_HIDDEN
          && h->plt.offset != (bfd_vma) -1)
        {
+         if (dynobj == NULL)
+           return bfd_reloc_dangerous;
+
          splt = bfd_get_section_by_name (dynobj, ".plt");
 
          value = (splt->output_section->vma
@@ -1293,6 +1768,9 @@ mn10300_elf_final_link_relocate (reloc_howto_type *howto,
          && ELF_ST_VISIBILITY (h->other) != STV_HIDDEN
          && h->plt.offset != (bfd_vma) -1)
        {
+         if (dynobj == NULL)
+           return bfd_reloc_dangerous;
+
          splt = bfd_get_section_by_name (dynobj, ".plt");
 
          value = (splt->output_section->vma
@@ -1311,41 +1789,102 @@ mn10300_elf_final_link_relocate (reloc_howto_type *howto,
       bfd_put_16 (input_bfd, value, hit_data);
       return bfd_reloc_ok;
 
+    case R_MN10300_TLS_LDO:
+      value = dtpoff (info, value);
+      bfd_put_32 (input_bfd, value + addend, hit_data);
+      return bfd_reloc_ok;
+
+    case R_MN10300_TLS_LE:
+      value = tpoff (info, value);
+      bfd_put_32 (input_bfd, value + addend, hit_data);
+      return bfd_reloc_ok;
+
+    case R_MN10300_TLS_LD:
+      if (dynobj == NULL)
+       return bfd_reloc_dangerous;
+
+      sgot = bfd_get_section_by_name (dynobj, ".got");
+
+      BFD_ASSERT (sgot != NULL);
+      value = htab->tls_ldm_got.offset + sgot->output_offset;
+      bfd_put_32 (input_bfd, value, hit_data);
+
+      if (!htab->tls_ldm_got.rel_emitted)
+       {
+         asection * srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
+         Elf_Internal_Rela rel;
+
+         BFD_ASSERT (srelgot != NULL);
+         htab->tls_ldm_got.rel_emitted ++;
+         rel.r_offset = (sgot->output_section->vma
+                         + sgot->output_offset
+                         + htab->tls_ldm_got.offset);
+         bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + htab->tls_ldm_got.offset);
+         bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + htab->tls_ldm_got.offset+4);
+         rel.r_info = ELF32_R_INFO (0, R_MN10300_TLS_DTPMOD);
+         rel.r_addend = 0;
+         bfd_elf32_swap_reloca_out (output_bfd, & rel,
+                                    (bfd_byte *) ((Elf32_External_Rela *) srelgot->contents
+                                                  + srelgot->reloc_count));
+         ++ srelgot->reloc_count;
+       }
+
+      return bfd_reloc_ok;
+
+    case R_MN10300_TLS_GOTIE:
+      value = tpoff (info, value);
+      /* Fall Through.  */
+
+    case R_MN10300_TLS_GD:
+    case R_MN10300_TLS_IE:
     case R_MN10300_GOT32:
     case R_MN10300_GOT24:
     case R_MN10300_GOT16:
-      {
-       sgot = bfd_get_section_by_name (dynobj, ".got");
+      if (dynobj == NULL)
+       return bfd_reloc_dangerous;
 
-         if (h != NULL)
-           {
-             bfd_vma off;
+      sgot = bfd_get_section_by_name (dynobj, ".got");
 
-             off = h->got.offset;
-             BFD_ASSERT (off != (bfd_vma) -1);
+      if (r_type == R_MN10300_TLS_GD)
+       value = dtpoff (info, value);
 
-             if (! elf_hash_table (info)->dynamic_sections_created
-                 || SYMBOL_REFERENCES_LOCAL (info, h))
-               /* This is actually a static link, or it is a
-                  -Bsymbolic link and the symbol is defined
-                  locally, or the symbol was forced to be local
-                  because of a version file.  We must initialize
-                  this entry in the global offset table.
-
-                  When doing a dynamic link, we create a .rela.got
-                  relocation entry to initialize the value.  This
-                  is done in the finish_dynamic_symbol routine.  */
-               bfd_put_32 (output_bfd, value,
-                           sgot->contents + off);
-
-             value = sgot->output_offset + off;
-           }
-         else
-           {
-             bfd_vma off;
+      if (h != NULL)
+       {
+         bfd_vma off;
+
+         off = h->got.offset;
+         /* Offsets in the GOT are allocated in check_relocs
+            which is not called for shared libraries... */
+         if (off == (bfd_vma) -1)
+           off = 0;
+
+         if (sgot->contents != NULL
+             && (! elf_hash_table (info)->dynamic_sections_created
+                 || SYMBOL_REFERENCES_LOCAL (info, h)))
+           /* This is actually a static link, or it is a
+              -Bsymbolic link and the symbol is defined
+              locally, or the symbol was forced to be local
+              because of a version file.  We must initialize
+              this entry in the global offset table.
+
+              When doing a dynamic link, we create a .rela.got
+              relocation entry to initialize the value.  This
+              is done in the finish_dynamic_symbol routine.  */
+           bfd_put_32 (output_bfd, value,
+                       sgot->contents + off);
+
+         value = sgot->output_offset + off;
+       }
+      else
+       {
+         bfd_vma off;
 
-             off = elf_local_got_offsets (input_bfd)[symndx];
+         off = elf_local_got_offsets (input_bfd)[symndx];
 
+         if (off & 1)
+           bfd_put_32 (output_bfd, value, sgot->contents + (off & ~ 1));
+         else
+           {
              bfd_put_32 (output_bfd, value, sgot->contents + off);
 
              if (info->shared)
@@ -1359,22 +1898,58 @@ mn10300_elf_final_link_relocate (reloc_howto_type *howto,
                  outrel.r_offset = (sgot->output_section->vma
                                     + sgot->output_offset
                                     + off);
-                 outrel.r_info = ELF32_R_INFO (0, R_MN10300_RELATIVE);
+                 switch (r_type)
+                   {
+                   case R_MN10300_TLS_GD:
+                     outrel.r_info = ELF32_R_INFO (0, R_MN10300_TLS_DTPOFF);
+                     outrel.r_offset = (sgot->output_section->vma
+                                        + sgot->output_offset
+                                        + off + 4);
+                     bfd_elf32_swap_reloca_out (output_bfd, & outrel,
+                                                (bfd_byte *) (((Elf32_External_Rela *)
+                                                               srelgot->contents)
+                                                              + srelgot->reloc_count));
+                     ++ srelgot->reloc_count;
+                     outrel.r_info = ELF32_R_INFO (0, R_MN10300_TLS_DTPMOD);
+                     break;
+                   case R_MN10300_TLS_GOTIE:
+                   case R_MN10300_TLS_IE:
+                     outrel.r_info = ELF32_R_INFO (0, R_MN10300_TLS_TPOFF);
+                     break;
+                   default:
+                     outrel.r_info = ELF32_R_INFO (0, R_MN10300_RELATIVE);
+                     break;
+                   }
+
                  outrel.r_addend = value;
                  bfd_elf32_swap_reloca_out (output_bfd, &outrel,
                                             (bfd_byte *) (((Elf32_External_Rela *)
                                                            srelgot->contents)
                                                           + srelgot->reloc_count));
                  ++ srelgot->reloc_count;
+                 elf_local_got_offsets (input_bfd)[symndx] |= 1;
                }
 
-             value = sgot->output_offset + off;
+             value = sgot->output_offset + (off & ~(bfd_vma) 1);
            }
-      }
+       }
 
       value += addend;
 
-      if (r_type == R_MN10300_GOT32)
+      if (r_type == R_MN10300_TLS_IE)
+       {
+         value += sgot->output_section->vma;
+         bfd_put_32 (input_bfd, value, hit_data);
+         return bfd_reloc_ok;
+       }
+      else if (r_type == R_MN10300_TLS_GOTIE
+              || r_type == R_MN10300_TLS_GD
+              || r_type == R_MN10300_TLS_LD)
+       {
+         bfd_put_32 (input_bfd, value, hit_data);
+         return bfd_reloc_ok;
+       }
+      else if (r_type == R_MN10300_GOT32)
        {
          bfd_put_32 (input_bfd, value, hit_data);
          return bfd_reloc_ok;
@@ -1419,6 +1994,7 @@ mn10300_elf_relocate_section (bfd *output_bfd,
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
   Elf_Internal_Rela *rel, *relend;
+  Elf_Internal_Rela * trel;
 
   symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (input_bfd);
@@ -1435,7 +2011,12 @@ mn10300_elf_relocate_section (bfd *output_bfd,
       struct elf32_mn10300_link_hash_entry *h;
       bfd_vma relocation;
       bfd_reloc_status_type r;
+      int tls_r_type;
+      bfd_boolean unresolved_reloc = FALSE;
+      bfd_boolean warned;
+      struct elf_link_hash_entry * hh;
 
+      relocation = 0;
       r_symndx = ELF32_R_SYM (rel->r_info);
       r_type = ELF32_R_TYPE (rel->r_info);
       howto = elf_mn10300_howto_table + r_type;
@@ -1449,24 +2030,42 @@ mn10300_elf_relocate_section (bfd *output_bfd,
       sym = NULL;
       sec = NULL;
       if (r_symndx < symtab_hdr->sh_info)
-       {
-         sym = local_syms + r_symndx;
-         sec = local_sections[r_symndx];
-         relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
-       }
+       hh = NULL;
       else
        {
-         bfd_boolean unresolved_reloc;
-         bfd_boolean warned;
-         struct elf_link_hash_entry *hh;
-
          RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
                                   r_symndx, symtab_hdr, sym_hashes,
                                   hh, sec, relocation,
                                   unresolved_reloc, warned);
+       }
+      h = elf_mn10300_hash_entry (hh);
 
-         h = (struct elf32_mn10300_link_hash_entry *) hh;
+      tls_r_type = elf_mn10300_tls_transition (info, r_type, hh, input_section, 0);
+      if (tls_r_type != r_type)
+       {
+         bfd_boolean had_plt;
+
+         had_plt = mn10300_do_tls_transition (input_bfd, r_type, tls_r_type,
+                                              contents, rel->r_offset);
+         r_type = tls_r_type;
+         howto = elf_mn10300_howto_table + r_type;
+
+         if (had_plt)
+           for (trel = rel+1; trel < relend; trel++)
+             if ((ELF32_R_TYPE (trel->r_info) == R_MN10300_PLT32
+                  || ELF32_R_TYPE (trel->r_info) == R_MN10300_PCREL32)
+                 && rel->r_offset + had_plt == trel->r_offset)
+               trel->r_info = ELF32_R_INFO (0, R_MN10300_NONE);
+       }
 
+      if (r_symndx < symtab_hdr->sh_info)
+       {
+         sym = local_syms + r_symndx;
+         sec = local_sections[r_symndx];
+         relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+       }
+      else
+       {
          if ((h->root.root.type == bfd_link_hash_defined
              || h->root.root.type == bfd_link_hash_defweak)
              && (   r_type == R_MN10300_GOTPC32
@@ -1478,6 +2077,10 @@ mn10300_elf_relocate_section (bfd *output_bfd,
                      && h->root.plt.offset != (bfd_vma) -1)
                  || ((   r_type == R_MN10300_GOT32
                       || r_type == R_MN10300_GOT24
+                      || r_type == R_MN10300_TLS_GD
+                      || r_type == R_MN10300_TLS_LD
+                      || r_type == R_MN10300_TLS_GOTIE
+                      || r_type == R_MN10300_TLS_IE
                       || r_type == R_MN10300_GOT16)
                      && elf_hash_table (info)->dynamic_sections_created
                      && !SYMBOL_REFERENCES_LOCAL (info, hh))
@@ -1571,6 +2174,9 @@ mn10300_elf_relocate_section (bfd *output_bfd,
              if (r_type == R_MN10300_PCREL32)
                msg = _("error: inappropriate relocation type for shared"
                        " library (did you forget -fpic?)");
+             else if (r_type == R_MN10300_GOT32)
+               msg = _("%B: taking the address of protected function"
+                       " '%s' cannot be done when making a shared library");
              else
                msg = _("internal error: suspicious relocation type used"
                        " in shared library");
@@ -1581,11 +2187,9 @@ mn10300_elf_relocate_section (bfd *output_bfd,
              /* Fall through.  */
 
            common_error:
-             if (!((*info->callbacks->warning)
-                   (info, msg, name, input_bfd, input_section,
-                    rel->r_offset)))
-               return FALSE;
-             break;
+             _bfd_error_handler (msg, input_bfd, name);
+             bfd_set_error (bfd_error_bad_value);
+             return FALSE;
            }
        }
     }
@@ -2581,6 +3185,7 @@ mn10300_elf_relax_section (bfd *abfd,
                    {
                      int bytes = 0;
                      bfd_vma symval;
+                     struct elf_link_hash_entry **hh;
 
                      /* Note that we've changed things.  */
                      elf_section_data (section)->relocs = internal_relocs;
@@ -2611,6 +3216,25 @@ mn10300_elf_relax_section (bfd *abfd,
                                                           bytes))
                        goto error_return;
 
+                     /* There may be other C++ functions symbols with the same
+                        address.  If so then mark these as having had their
+                        prologue bytes deleted as well.  */
+                     for (hh = elf_sym_hashes (input_bfd); hh < end_hashes; hh++)
+                       {
+                         struct elf32_mn10300_link_hash_entry *h;
+
+                         h = (struct elf32_mn10300_link_hash_entry *) * hh;
+
+                         if (h != sym_hash
+                             && (h->root.root.type == bfd_link_hash_defined
+                                 || h->root.root.type == bfd_link_hash_defweak)
+                             && h->root.root.u.def.section == section
+                             && ! (h->flags & MN10300_CONVERT_CALL_TO_CALLS)
+                             && h->root.root.u.def.value == symval
+                             && h->root.type == STT_FUNC)
+                           h->flags |= MN10300_DELETED_PROLOGUE_BYTES;
+                       }
+
                      /* Something changed.  Not strictly necessary, but
                         may lead to more relaxing opportunities.  */
                      *again = TRUE;
@@ -3949,11 +4573,38 @@ elf32_mn10300_link_hash_newfunc (struct bfd_hash_entry *entry,
       ret->movm_stack_size = 0;
       ret->flags = 0;
       ret->value = 0;
+      ret->tls_type = GOT_UNKNOWN;
     }
 
   return (struct bfd_hash_entry *) ret;
 }
 
+static void
+_bfd_mn10300_copy_indirect_symbol (struct bfd_link_info *        info,
+                                  struct elf_link_hash_entry *  dir,
+                                  struct elf_link_hash_entry *  ind)
+{
+  struct elf32_mn10300_link_hash_entry * edir;
+  struct elf32_mn10300_link_hash_entry * eind;
+
+  edir = elf_mn10300_hash_entry (dir);
+  eind = elf_mn10300_hash_entry (ind);
+
+  if (ind->root.type == bfd_link_hash_indirect
+      && dir->got.refcount <= 0)
+    {
+      edir->tls_type = eind->tls_type;
+      eind->tls_type = GOT_UNKNOWN;
+    }
+  edir->direct_calls = eind->direct_calls;
+  edir->stack_size = eind->stack_size;
+  edir->movm_args = eind->movm_args;
+  edir->movm_stack_size = eind->movm_stack_size;
+  edir->flags = eind->flags;
+
+  _bfd_elf_link_hash_copy_indirect (info, dir, ind);
+}
+
 /* Create an mn10300 ELF linker hash table.  */
 
 static struct bfd_link_hash_table *
@@ -3976,6 +4627,11 @@ elf32_mn10300_link_hash_table_create (bfd *abfd)
     }
 
   ret->flags = 0;
+  ret->tls_ldm_got.refcount = 0;
+  ret->tls_ldm_got.offset = -1;
+  ret->tls_ldm_got.got_allocated = 0;
+  ret->tls_ldm_got.rel_emitted = 0;
+
   amt = sizeof (struct elf_link_hash_table);
   ret->static_hash_table = bfd_malloc (amt);
   if (ret->static_hash_table == NULL)
@@ -4410,6 +5066,7 @@ static bfd_boolean
 _bfd_mn10300_elf_size_dynamic_sections (bfd * output_bfd,
                                        struct bfd_link_info * info)
 {
+  struct elf32_mn10300_link_hash_table *htab = elf32_mn10300_hash_table (info);
   bfd * dynobj;
   asection * s;
   bfd_boolean plt;
@@ -4442,6 +5099,13 @@ _bfd_mn10300_elf_size_dynamic_sections (bfd * output_bfd,
        s->size = 0;
     }
 
+  if (htab->tls_ldm_got.refcount > 0)
+    {
+      s = bfd_get_section_by_name (dynobj, ".rela.got");
+      BFD_ASSERT (s != NULL);
+      s->size += sizeof (Elf32_External_Rela);
+    }
+
   /* The check_relocs and adjust_dynamic_symbol entry points have
      determined the sizes of the various dynamic sections.  Allocate
      memory for them.  */
@@ -4685,31 +5349,64 @@ _bfd_mn10300_elf_finish_dynamic_symbol (bfd * output_bfd,
                      + sgot->output_offset
                      + (h->got.offset & ~1));
 
-      /* If this is a -Bsymbolic link, and the symbol is defined
-        locally, we just want to emit a RELATIVE reloc.  Likewise if
-        the symbol was forced to be local because of a version file.
-        The entry in the global offset table will already have been
-        initialized in the relocate_section function.  */
-      if (info->shared
-         && (info->symbolic || h->dynindx == -1)
-         && h->def_regular)
-       {
-         rel.r_info = ELF32_R_INFO (0, R_MN10300_RELATIVE);
-         rel.r_addend = (h->root.u.def.value
-                         + h->root.u.def.section->output_section->vma
-                         + h->root.u.def.section->output_offset);
-       }
-      else
+      switch (elf_mn10300_hash_entry (h)->tls_type)
        {
+       case GOT_TLS_GD:
          bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + h->got.offset);
-         rel.r_info = ELF32_R_INFO (h->dynindx, R_MN10300_GLOB_DAT);
+         bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + h->got.offset + 4);
+         rel.r_info = ELF32_R_INFO (h->dynindx, R_MN10300_TLS_DTPMOD);
+         rel.r_addend = 0;
+         bfd_elf32_swap_reloca_out (output_bfd, & rel,
+                                    (bfd_byte *) ((Elf32_External_Rela *) srel->contents
+                                                  + srel->reloc_count));
+         ++ srel->reloc_count;
+         rel.r_info = ELF32_R_INFO (h->dynindx, R_MN10300_TLS_DTPOFF);
+         rel.r_offset += 4;
          rel.r_addend = 0;
+         break;
+
+       case GOT_TLS_IE:
+         /* We originally stored the addend in the GOT, but at this
+            point, we want to move it to the reloc instead as that's
+            where the dynamic linker wants it.  */
+         rel.r_addend = bfd_get_32 (output_bfd, sgot->contents + h->got.offset);
+         bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + h->got.offset);
+         if (h->dynindx == -1)
+           rel.r_info = ELF32_R_INFO (0, R_MN10300_TLS_TPOFF);
+         else
+           rel.r_info = ELF32_R_INFO (h->dynindx, R_MN10300_TLS_TPOFF);
+         break;
+
+       default:
+         /* If this is a -Bsymbolic link, and the symbol is defined
+            locally, we just want to emit a RELATIVE reloc.  Likewise if
+            the symbol was forced to be local because of a version file.
+            The entry in the global offset table will already have been
+            initialized in the relocate_section function.  */
+         if (info->shared
+             && (info->symbolic || h->dynindx == -1)
+             && h->def_regular)
+           {
+             rel.r_info = ELF32_R_INFO (0, R_MN10300_RELATIVE);
+             rel.r_addend = (h->root.u.def.value
+                             + h->root.u.def.section->output_section->vma
+                             + h->root.u.def.section->output_offset);
+           }
+         else
+           {
+             bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + h->got.offset);
+             rel.r_info = ELF32_R_INFO (h->dynindx, R_MN10300_GLOB_DAT);
+             rel.r_addend = 0;
+           }
        }
 
-      bfd_elf32_swap_reloca_out (output_bfd, &rel,
-                                (bfd_byte *) ((Elf32_External_Rela *) srel->contents
-                                              + srel->reloc_count));
-      ++ srel->reloc_count;
+      if (ELF32_R_TYPE (rel.r_info) != R_MN10300_NONE)
+       {
+         bfd_elf32_swap_reloca_out (output_bfd, &rel,
+                                    (bfd_byte *) ((Elf32_External_Rela *) srel->contents
+                                                  + srel->reloc_count));
+         ++ srel->reloc_count;
+       }
     }
 
   if (h->needs_copy)
@@ -4846,6 +5543,14 @@ _bfd_mn10300_elf_finish_dynamic_sections (bfd * output_bfd,
          /* UnixWare sets the entsize of .plt to 4, although that doesn't
             really seem like the right value.  */
          elf_section_data (splt->output_section)->this_hdr.sh_entsize = 4;
+
+         /* UnixWare sets the entsize of .plt to 4, but this is incorrect
+            as it means that the size of the PLT0 section (15 bytes) is not
+            a multiple of the sh_entsize.  Some ELF tools flag this as an
+            error.  We could pad PLT0 to 16 bytes, but that would introduce
+            compatibilty issues with previous toolchains, so instead we
+            just set the entry size to 1.  */
+         elf_section_data (splt->output_section)->this_hdr.sh_entsize = 1;
        }
     }
 
@@ -4887,12 +5592,7 @@ _bfd_mn10300_elf_reloc_type_class (const Elf_Internal_Rela *rela)
 static bfd_boolean
 mn10300_elf_mkobject (bfd *abfd)
 {
-  /* We do not actually need any extra room in the bfd elf data structure.
-     But we do need the object_id of the structure to be set to
-     MN10300_ELF_DATA so that elflink.c:elf_link_add_object_symols() will call
-     our mn10300_elf_check_relocs function which will then allocate space in
-     the .got section for any GOT based relocs.  */
-  return bfd_elf_allocate_object (abfd, sizeof (struct elf_obj_tdata),
+  return bfd_elf_allocate_object (abfd, sizeof (struct elf_mn10300_obj_tdata),
                                  MN10300_ELF_DATA);
 }
 
@@ -4948,7 +5648,8 @@ mn10300_elf_mkobject (bfd *abfd)
   _bfd_mn10300_elf_finish_dynamic_symbol
 #define elf_backend_finish_dynamic_sections \
   _bfd_mn10300_elf_finish_dynamic_sections
-
+#define elf_backend_copy_indirect_symbol \
+  _bfd_mn10300_copy_indirect_symbol
 #define elf_backend_reloc_type_class \
   _bfd_mn10300_elf_reloc_type_class
 
index 78860ffe5c5f3da8a043df71f9d926ed77165a02..844d3a73d9c46038f949c3a883722eef8d9e69d0 100644 (file)
@@ -1225,6 +1225,17 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_MN10300_RELATIVE",
   "BFD_RELOC_MN10300_SYM_DIFF",
   "BFD_RELOC_MN10300_ALIGN",
+  "BFD_RELOC_MN10300_TLS_GD",
+  "BFD_RELOC_MN10300_TLS_LD",
+  "BFD_RELOC_MN10300_TLS_LDO",
+  "BFD_RELOC_MN10300_TLS_GOTIE",
+  "BFD_RELOC_MN10300_TLS_IE",
+  "BFD_RELOC_MN10300_TLS_LE",
+  "BFD_RELOC_MN10300_TLS_DTPMOD",
+  "BFD_RELOC_MN10300_TLS_DTPOFF",
+  "BFD_RELOC_MN10300_TLS_TPOFF",
+  "BFD_RELOC_MN10300_32_PCREL",
+  "BFD_RELOC_MN10300_16_PCREL",
 
   "BFD_RELOC_386_GOT32",
   "BFD_RELOC_386_PLT32",
@@ -1733,8 +1744,6 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_V850_32_GOTOFF",
   "BFD_RELOC_V850_CODE",
   "BFD_RELOC_V850_DATA",
-  "BFD_RELOC_MN10300_32_PCREL",
-  "BFD_RELOC_MN10300_16_PCREL",
   "BFD_RELOC_TIC30_LDP",
   "BFD_RELOC_TIC54X_PARTLS7",
   "BFD_RELOC_TIC54X_PARTMS9",
index 4e1958c6364a9b5b2fbc5ec646a13f7826e409b6..fb3aab26564b7876ae85414471caafb310774b1d 100644 (file)
@@ -2547,6 +2547,36 @@ ENUMDOC
   The addend of this reloc is an alignment power that must
   be honoured at the offset's location, regardless of linker
   relaxation.
+ENUM
+  BFD_RELOC_MN10300_TLS_GD
+ENUMX
+  BFD_RELOC_MN10300_TLS_LD
+ENUMX
+  BFD_RELOC_MN10300_TLS_LDO
+ENUMX
+  BFD_RELOC_MN10300_TLS_GOTIE
+ENUMX
+  BFD_RELOC_MN10300_TLS_IE
+ENUMX
+  BFD_RELOC_MN10300_TLS_LE
+ENUMX
+  BFD_RELOC_MN10300_TLS_DTPMOD
+ENUMX
+  BFD_RELOC_MN10300_TLS_DTPOFF
+ENUMX
+  BFD_RELOC_MN10300_TLS_TPOFF
+ENUMDOC
+  Various TLS-related relocations.
+ENUM
+  BFD_RELOC_MN10300_32_PCREL
+ENUMDOC
+  This is a 32bit pcrel reloc for the mn10300, offset by two bytes in the
+  instruction.
+ENUM
+  BFD_RELOC_MN10300_16_PCREL
+ENUMDOC
+  This is a 16bit pcrel reloc for the mn10300, offset by two bytes in the
+  instruction.
 COMMENT
 
 ENUM
@@ -3921,16 +3951,6 @@ ENUM
   BFD_RELOC_V850_DATA
 ENUMDOC
   start data in text.
-ENUM
-  BFD_RELOC_MN10300_32_PCREL
-ENUMDOC
-  This is a 32bit pcrel reloc for the mn10300, offset by two bytes in the
-  instruction.
-ENUM
-  BFD_RELOC_MN10300_16_PCREL
-ENUMDOC
-  This is a 16bit pcrel reloc for the mn10300, offset by two bytes in the
-  instruction.
 
 ENUM
   BFD_RELOC_TIC30_LDP
index cf01c4a7f12f00b7d1c3dcf7e6d011728029406a..425f9268b2ae8f962fe3648bdea79f4463e149df 100644 (file)
@@ -1,3 +1,7 @@
+2012-03-07  Nick Clifton  <nickc@redhat.com>
+
+       * readelf.c (is_16bit_abs_reloc): Add detection of R_MN10300_16.
+
 2012-02-29  Jeff Law  <law@redhat.com>
 
        * doc/binutils.texi (c++filt): Fix typos.
index 560df7ab80c3792fd5f787f3ac3a810252e4625c..f42039e76233a3c66c139b21c39a9d1e3a23aebe 100644 (file)
@@ -10084,6 +10084,9 @@ is_16bit_abs_reloc (unsigned int reloc_type)
     case EM_XC16X:
     case EM_C166:
       return reloc_type == 2; /* R_XC16C_ABS_16.  */
+    case EM_CYGNUS_MN10300:
+    case EM_MN10300:
+      return reloc_type == 2; /* R_MN10300_16.  */
     default:
       return FALSE;
     }
index be8e7c52b96127d4b9b165686721b55967c01b87..f60f94b2040043711c0249581980f7549945611d 100644 (file)
@@ -1,3 +1,9 @@
+2012-03-07  Nick Clifton  <nickc@redhat.com>
+
+       * config/tc-mn10300.c (other_registers): Add SSP and USP.
+       (md_assemble): Add support for TLS relocs.
+       (mn10300_parse_name): Likewise.
+
 2012-02-27  Alan Modra  <amodra@gmail.com>
 
        * config/tc-crx.c (check_range): Correct uint32_t misconceptions.
index 0a4f26cb59c9a8b00ab64562b45bc3eca16bb83a..4029c641b62a4f61b2554e7f275d708968a52c6e 100644 (file)
@@ -281,6 +281,8 @@ static const struct reg_name other_registers[] =
   { "pc", AM33 },
   { "psw", 0 },
   { "sp", 0 },
+  { "ssp", 0 },
+  { "usp", 0 },
 };
 
 #define OTHER_REG_NAME_CNT     ARRAY_SIZE (other_registers)
@@ -2063,6 +2065,12 @@ keep_going:
              && fixups[i].reloc != BFD_RELOC_32_GOT_PCREL
              && fixups[i].reloc != BFD_RELOC_32_GOTOFF
              && fixups[i].reloc != BFD_RELOC_32_PLT_PCREL
+             && fixups[i].reloc != BFD_RELOC_MN10300_TLS_GD
+             && fixups[i].reloc != BFD_RELOC_MN10300_TLS_LD
+             && fixups[i].reloc != BFD_RELOC_MN10300_TLS_LDO
+             && fixups[i].reloc != BFD_RELOC_MN10300_TLS_GOTIE
+             && fixups[i].reloc != BFD_RELOC_MN10300_TLS_IE
+             && fixups[i].reloc != BFD_RELOC_MN10300_TLS_LE
              && fixups[i].reloc != BFD_RELOC_MN10300_GOT32)
            {
              reloc_howto_type *reloc_howto;
@@ -2501,6 +2509,18 @@ mn10300_parse_name (char const *name,
     reloc_type = BFD_RELOC_MN10300_GOT32;
   else if ((next_end = mn10300_end_of_match (next + 1, "PLT")))
     reloc_type = BFD_RELOC_32_PLT_PCREL;
+  else if ((next_end = mn10300_end_of_match (next + 1, "tlsgd")))
+    reloc_type = BFD_RELOC_MN10300_TLS_GD;
+  else if ((next_end = mn10300_end_of_match (next + 1, "tlsldm")))
+    reloc_type = BFD_RELOC_MN10300_TLS_LD;
+  else if ((next_end = mn10300_end_of_match (next + 1, "dtpoff")))
+    reloc_type = BFD_RELOC_MN10300_TLS_LDO;
+  else if ((next_end = mn10300_end_of_match (next + 1, "gotntpoff")))
+    reloc_type = BFD_RELOC_MN10300_TLS_GOTIE;
+  else if ((next_end = mn10300_end_of_match (next + 1, "indntpoff")))
+    reloc_type = BFD_RELOC_MN10300_TLS_IE;
+  else if ((next_end = mn10300_end_of_match (next + 1, "tpoff")))
+    reloc_type = BFD_RELOC_MN10300_TLS_LE;
   else
     goto no_suffix;
 
index 1535dfb3395e3a04c4480f32fc0bd341e8f02362..2d8264ea33f0907465c5409d48dd2fe18305ab2c 100644 (file)
@@ -1,3 +1,10 @@
+2012-03-07  Nick Clifton  <nickc@redhat.com>
+
+       * mn10300.h (elf_mn10300_reloc_type): Add R_MN10300_TLS_GD,
+       R_MN10300_TLS_LD, R_MN10300_TLS_LDO, R_MN10300_TLS_GOTIE,
+       R_MN10300_TLS_IE, R_MN10300_TLS_LE, R_MN10300_TLS_DPTMOD,
+       R_MN10300_TLS_DTPOFF and R_MN10300_TLS_TPOFF.
+
 2012-02-25  Walter Lee  <walt@tilera.com>
 
        * tilegx.h (R_TILEGX_IMM16_X0_HW1_GOT): Delete.
index 444787b5f9525a96a5cbf231f0a44b478d7d2538..529b3897b4d1f74d50ec4f4a9f931ebcef4d6fc0 100644 (file)
@@ -51,6 +51,15 @@ START_RELOC_NUMBERS (elf_mn10300_reloc_type)
   RELOC_NUMBER (R_MN10300_GLOB_DAT, 21)
   RELOC_NUMBER (R_MN10300_JMP_SLOT, 22)
   RELOC_NUMBER (R_MN10300_RELATIVE, 23)
+  RELOC_NUMBER (R_MN10300_TLS_GD, 24)
+  RELOC_NUMBER (R_MN10300_TLS_LD, 25)
+  RELOC_NUMBER (R_MN10300_TLS_LDO, 26)
+  RELOC_NUMBER (R_MN10300_TLS_GOTIE, 27)
+  RELOC_NUMBER (R_MN10300_TLS_IE, 28)
+  RELOC_NUMBER (R_MN10300_TLS_LE, 29)
+  RELOC_NUMBER (R_MN10300_TLS_DTPMOD, 30)
+  RELOC_NUMBER (R_MN10300_TLS_DTPOFF, 31)
+  RELOC_NUMBER (R_MN10300_TLS_TPOFF, 32)
   RELOC_NUMBER (R_MN10300_SYM_DIFF, 33)
   RELOC_NUMBER (R_MN10300_ALIGN, 34)
 END_RELOC_NUMBERS (R_MN10300_MAX)