TLS support for MIPS

Daniel Jacobowitz drow@false.org
Thu Feb 24 21:56:00 GMT 2005


On Mon, Feb 07, 2005 at 06:00:14PM -0500, Daniel Jacobowitz wrote:
> This patch implements thread-local storage for MIPS.  The ABI can be found
> at <http://www.linux-mips.org/wiki/index.php/NPTL>; GCC, Linux kernel, and
> glibc bits will be along shortly.
> 
> First, there's one architecture independent fix in here (sorry).  While
> writing testcases I discovered that if the first object on the command line
> was a shared library with a .reginfo section, then the resulting object
> would not contain .reginfo.  We'd select the copy from the shared library
> as the only one to hold on to, and obligingly exclude all the others.  Oops!
> 
> The bulk of the code is dealing with the unique MIPS GOT infrastructure.
> I've tried to parallel the existing design as closely as possible.  TLS
> symbols are placed after the global and local entries for each GOT, to avoid
> automatic relocation by the dynamic linker.  There may be multiple
> mips_got_info structures for each global TLS symbol while in single-GOT
> mode, just as for normal symbols.  We record the relevant TLS access types
> on each GOT entry, and their union on the global symbol if applicable.
> 
> I had a lot of trouble with the GOT support, so I wrote exhaustive
> (exhausting) test cases.  I am especially proud of tls-multi-got-1.
> 
> No linker relaxations are supported, mostly because the MIPS linker didn't
> have an existing framework for same.  There may be some in the future,
> though I anticipate that new marker relocations would be introduced to
> support them.
> 
> Tested via cross builds to mips-elf, mips-linux, mips64-linux, and little
> endian versions of same.  Also tested using NPTL patches for GCC and glibc
> on mips-linux.
> 
> OK?  Comments, questions, things I failed to explain?

Here's a revised patch.  The changes are:

  - *blush* I forgot to add Joseph to most of the changelog entries. 
    He wrote the first version of this patch.

  - Operator names and relocation names updated.  They're basically
    consistent now.

  - A couple of code cleanups in BFD.

Retested using NPTL patches for GCC and glibc on mips-linux.  OK?


-- 
Daniel Jacobowitz
CodeSourcery, LLC

2005-02-24  Daniel Jacobowitz  <dan@codesourcery.com>
	    Joseph Myers  <joseph@codesourcery.com>

	* elfxx-mips.c (struct mips_got_entry): Add tls_type.
	(struct mips_got_info): Add tls_gotno, tls_assigned_gotno,
	and tls_ldm_offset.
	(struct mips_elf_got_per_bfd_arg): Add global_count.
	(struct mips_elf_count_tls_arg): New.
	(struct mips_elf_hash_sort_data): Update comment for min_got_dynindx.
	(struct mips_elf_link_hash_entry): Add tls_type and tls_got_offset.
	(GOT_NORMAL, GOT_TLS_GD, GOT_TLS_LDM, GOT_TLS_IE)
	(GOT_TLS_OFFSET_DONE, GOT_TLS_DONE): Define.
	(TLS_RELOC_P): Define.
	(TP_OFFSET, DTP_OFFSET): Define.
	(dtprel_base, tprel_base): New functions.
	(mips_elf_link_hash_newfunc): Initialize tls_type.
	(mips_elf_got_entry_hash, mips_elf_got_entry_eq)
	(mips_elf_multi_got_entry_hash, mips_elf_multi_got_entry_eq): Handle
	TLS entries.
	(mips_tls_got_relocs, mips_elf_count_local_tls_relocs)
	(mips_elf_count_global_tls_entries, mips_elf_count_global_tls_relocs)
	(mips_elf_output_dynamic_relocation, mips_elf_initialize_tls_slots)
	(mips_tls_got_index): New functions.
	(mips_elf_local_got_index): Add new R_SYMNDX, H, and R_TYPE
	arguments.  Pass them to mips_elf_create_local_got_entry.  Use
	mips_tls_got_index.
	(mips_elf_global_got_index): Add new R_TYPE and INFO arguments.
	Handle TLS entries.
	(mips_elf_got_page, mips_elf_got16_entry): Update calls to
	mips_elf_create_local_got_entry.
	(mips_elf_create_local_got_entry): Add new R_SYMNDX, H, and R_TYPE
	arguments.  Handle TLS entries.
	(mips_elf_sort_hash_table_f): Add non-TLS assertions.
	(mips_elf_record_local_got_symbol): Add new TLS_FLAG argument.  Handle
	TLS entries.
	(mips_elf_record_global_got_symbol): Likewise.
	(mips_elf_make_got_per_bfd): Initialize new mips_got_info members.
	Count TLS entries.
	(mips_elf_merge_gots): Handle TLS entries when merging.
	(mips_elf_initialize_tls_index): New function.
	(mips_elf_set_global_got_offset): Handle TLS entries.
	(mips_elf_adjust_gp): Handle TLS.
	(mips_elf_multi_got): Remove redundant call to
	mips_elf_resolve_final_got_entries.  Initialize global_count.
	Correct a comment.  Initialize new TLS members of mips_got_info.
	Assign TLS GOT indexes for new GOTs.
	(mips_elf_create_got_section): Initialize new TLS members of
	mips_got_info.
	(mips_elf_calculate_relocation): Handle TLS relocs.
	(_bfd_mips_elf_check_relocs): Likewise.  Update calls to changed
	functions.
	(_bfd_mips_elf_always_size_sections): Handle TLS.
	(_bfd_mips_elf_size_dynamic_sections): Likewise.
	(_bfd_mips_elf_finish_dynamic_symbol): Likewise.  Update calls to
	changed functions.
	(_bfd_mips_elf_copy_indirect_symbol): Copy tls_type.
	(_bfd_mips_elf_hide_symbol): Handle TLS.
	* elfn32-mips.c (elf_mips_howto_table_rel, elf_mips_howto_table_rela)
	(mips_reloc_map): Add TLS relocs.
	* elf32-mips.c (elf_mips_howto_table_rel, mips_reloc_map): Likewise.
	* elf64-mips.c (mips_elf64_howto_table_rel)
	(mips_elf64_howto_table_rela, mips_reloc_map): Likewise.
	* reloc.c: Define new MIPS TLS relocations.
	* libbfd.h, bfd-in2.h: Regenerated.

2005-02-24  Daniel Jacobowitz  <dan@codesourcery.com>

	* ld-mips-elf/tlsbin-o32.s, ld-mips-elf/mips-dyn.ld,
	ld-mips-elf/tlslib-o32.got, ld-mips-elf/tlslib-o32.d,
	ld-mips-elf/tlslib-o32.s, ld-mips-elf/mips-lib.ld,
	ld-mips-elf/tlsbin-o32.got, ld-mips-elf/tlsdyn-o32.d,
	ld-mips-elf/tlsdyn-o32.got, ld-mips-elf/tlsbin-o32.d,
	ld-mips-elf/tlsdyn-o32.s, ld-mips-elf/tls-multi-got-1.got,
	ld-mips-elf/tls-multi-got-1-1.s, ld-mips-elf/tls-multi-got-1.d,
	ld-mips-elf/tls-multi-got-1.r, ld-mips-elf/tls-multi-got-1-2.s,
	ld-mips-elf/tlslib-o32-ver.got, ld-mips-elf/tlslib.ver,
	ld-mips-elf/tlslib-o32-hidden.got, ld-mips-elf/tlslib-hidden.ver,
	ld-mips-elf/tlsdyn-o32-1.d, ld-mips-elf/tlsdyn-o32-3.got,
	ld-mips-elf/tlsdyn-o32-2.d, ld-mips-elf/tlsdyn-o32-2.s,
	ld-mips-elf/tlsdyn-o32-3.d, ld-mips-elf/tlsdyn-o32-1.got,
	ld-mips-elf/tlsdyn-o32-2.got: New files.
	* ld-mips-elf/mips-elf.exp: Run the new tests.

2005-02-24  Daniel Jacobowitz  <dan@codesourcery.com>
	    Joseph Myers  <joseph@codesourcery.com>

	* elf/mips.h: Define MIPS TLS relocations.

2005-02-24  Daniel Jacobowitz  <dan@codesourcery.com>
	    Joseph Myers  <joseph@codesourcery.com>

	* config/tc-mips.c (percent_op): Add %tlsgd, %tlsldm, %dtprel_hi,
	%dtprel_lo, %tprel_hi, %tprel_lo, and %gottprel.
	(parse_relocation): Check for a word break after a relocation
	operator.
	(md_apply_fix3): Handle TLS relocations, and mark thread-local
	symbols.

2005-02-24  Daniel Jacobowitz  <dan@codesourcery.com>

	* gas/mips/tls-o32.d, gas/mips/tls-o32.s, gas/mips/tls-ill.l,
	gas/mips/tls-ill.s: New files.
	* gas/mips/mips.exp: Run TLS tests.

Index: binutils/bfd/elfxx-mips.c
===================================================================
--- binutils.orig/bfd/elfxx-mips.c	2005-02-24 13:47:21.387287019 -0500
+++ binutils/bfd/elfxx-mips.c	2005-02-24 13:48:44.985881160 -0500
@@ -64,6 +64,14 @@ struct mips_got_entry
        h->forced_local).  */
     struct mips_elf_link_hash_entry *h;
   } d;
+
+  /* The TLS types included in this GOT entry (specifically, GD and
+     IE).  The GD and IE flags can be added as we encounter new
+     relocations.  LDM can also be set; it will always be alone, not
+     combined with any GD or IE flags.  An LDM GOT entry will be
+     a local symbol entry with r_symndx == 0.  */
+  unsigned char tls_type;
+
   /* The offset from the beginning of the .got section to the entry
      corresponding to this symbol+addend.  If it's a global symbol
      whose offset is yet to be decided, it's going to be -1.  */
@@ -79,6 +87,11 @@ struct mips_got_info
   struct elf_link_hash_entry *global_gotsym;
   /* The number of global .got entries.  */
   unsigned int global_gotno;
+  /* The number of .got slots used for TLS.  */
+  unsigned int tls_gotno;
+  /* The first unused TLS .got entry.  Used only during
+     mips_elf_initialize_tls_index.  */
+  unsigned int tls_assigned_gotno;
   /* The number of local .got entries.  */
   unsigned int local_gotno;
   /* The number of local .got entries we have used.  */
@@ -91,6 +104,11 @@ struct mips_got_info
   /* In multi-got links, a pointer to the next got (err, rather, most
      of the time, it points to the previous got).  */
   struct mips_got_info *next;
+  /* This is the GOT index of the TLS LDM entry for the GOT, MINUS_ONE
+     for none, or MINUS_TWO for not yet assigned.  This is needed
+     because a single-GOT link may have multiple hash table entries
+     for the LDM.  It does not get initialized in multi-GOT mode.  */
+  bfd_vma tls_ldm_offset;
 };
 
 /* Map an input bfd to a got in a multi-got link.  */
@@ -125,6 +143,11 @@ struct mips_elf_got_per_bfd_arg
   unsigned int primary_count;
   /* The number of local and global entries in the current got.  */
   unsigned int current_count;
+  /* The total number of global entries which will live in the
+     primary got and be automatically relocated.  This includes
+     those not referenced by the primary GOT but included in
+     the "master" GOT.  */
+  unsigned int global_count;
 };
 
 /* Another structure used to pass arguments for got entries traversal.  */
@@ -137,6 +160,15 @@ struct mips_elf_set_global_got_offset_ar
   struct bfd_link_info *info;
 };
 
+/* A structure used to count TLS relocations or GOT entries, for GOT
+   entry or ELF symbol table traversal.  */
+
+struct mips_elf_count_tls_arg
+{
+  struct bfd_link_info *info;
+  unsigned int needed;
+};
+
 struct _mips_elf_section_data
 {
   struct bfd_elf_section_data elf;
@@ -158,8 +190,8 @@ struct mips_elf_hash_sort_data
   /* The symbol in the global GOT with the lowest dynamic symbol table
      index.  */
   struct elf_link_hash_entry *low;
-  /* The least dynamic symbol table index corresponding to a symbol
-     with a GOT entry.  */
+  /* The least dynamic symbol table index corresponding to a non-TLS
+     symbol with a GOT entry.  */
   long min_got_dynindx;
   /* The greatest dynamic symbol table index corresponding to a symbol
      with a GOT entry that is not referenced (e.g., a dynamic symbol
@@ -212,6 +244,21 @@ struct mips_elf_link_hash_entry
 
   /* Are we forced local?  .*/
   bfd_boolean forced_local;
+
+#define GOT_NORMAL	0
+#define GOT_TLS_GD	1
+#define GOT_TLS_LDM	2
+#define GOT_TLS_IE	4
+#define GOT_TLS_OFFSET_DONE    0x40
+#define GOT_TLS_DONE    0x80
+  unsigned char tls_type;
+  /* This is only used in single-GOT mode; in multi-GOT mode there
+     is one mips_got_entry per GOT entry, so the offset is stored
+     there.  In single-GOT mode there may be many mips_got_entry
+     structures all referring to the same GOT slot.  It might be
+     possible to use root.got.offset instead, but that field is
+     overloaded already.  */
+  bfd_vma tls_got_offset;
 };
 
 /* MIPS ELF linker hash table.  */
@@ -237,6 +284,21 @@ struct mips_elf_link_hash_table
   bfd_boolean mips16_stubs_seen;
 };
 
+#define TLS_RELOC_P(r_type) \
+  (r_type == R_MIPS_TLS_DTPMOD32		\
+   || r_type == R_MIPS_TLS_DTPMOD64		\
+   || r_type == R_MIPS_TLS_DTPREL32		\
+   || r_type == R_MIPS_TLS_DTPREL64		\
+   || r_type == R_MIPS_TLS_GD			\
+   || r_type == R_MIPS_TLS_LDM			\
+   || r_type == R_MIPS_TLS_DTPREL_HI16		\
+   || r_type == R_MIPS_TLS_DTPREL_LO16		\
+   || r_type == R_MIPS_TLS_GOTTPREL		\
+   || r_type == R_MIPS_TLS_TPREL32		\
+   || r_type == R_MIPS_TLS_TPREL64		\
+   || r_type == R_MIPS_TLS_TPREL_HI16		\
+   || r_type == R_MIPS_TLS_TPREL_LO16)
+
 /* Structure used to pass information to mips_elf_output_extsym.  */
 
 struct extsym_info
@@ -370,7 +432,8 @@ typedef struct runtime_pdr {
 #define rpdNil ((pRPDR) 0)
 
 static struct mips_got_entry *mips_elf_create_local_got_entry
-  (bfd *, bfd *, struct mips_got_info *, asection *, bfd_vma);
+  (bfd *, bfd *, struct mips_got_info *, asection *, bfd_vma, unsigned long,
+   struct mips_elf_link_hash_entry *, int);
 static bfd_boolean mips_elf_sort_hash_table_f
   (struct mips_elf_link_hash_entry *, void *);
 static bfd_vma mips_elf_high
@@ -588,6 +651,30 @@ static bfd *reldyn_sorting_bfd;
 #define mips_elf_hash_table(p) \
   ((struct mips_elf_link_hash_table *) ((p)->hash))
 
+/* Find the base offsets for thread-local storage in this object,
+   for GD/LD and IE/LE respectively.  */
+
+#define TP_OFFSET 0x7000
+#define DTP_OFFSET 0x8000
+
+static bfd_vma
+dtprel_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 + DTP_OFFSET;
+}
+
+static bfd_vma
+tprel_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 + TP_OFFSET;
+}
+
 /* Create an entry in a MIPS ELF linker hash table.  */
 
 static struct bfd_hash_entry *
@@ -623,6 +710,7 @@ mips_elf_link_hash_newfunc (struct bfd_h
       ret->call_stub = NULL;
       ret->call_fp_stub = NULL;
       ret->forced_local = FALSE;
+      ret->tls_type = GOT_NORMAL;
     }
 
   return (struct bfd_hash_entry *) ret;
@@ -1738,6 +1826,7 @@ mips_elf_got_entry_hash (const void *ent
   const struct mips_got_entry *entry = (struct mips_got_entry *)entry_;
 
   return entry->symndx
+    + ((entry->tls_type & GOT_TLS_LDM) << 17)
     + (! entry->abfd ? mips_elf_hash_bfd_vma (entry->d.address)
        : entry->abfd->id
          + (entry->symndx >= 0 ? mips_elf_hash_bfd_vma (entry->d.addend)
@@ -1750,6 +1839,10 @@ mips_elf_got_entry_eq (const void *entry
   const struct mips_got_entry *e1 = (struct mips_got_entry *)entry1;
   const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2;
 
+  /* An LDM entry can only match another LDM entry.  */
+  if ((e1->tls_type ^ e2->tls_type) & GOT_TLS_LDM)
+    return 0;
+
   return e1->abfd == e2->abfd && e1->symndx == e2->symndx
     && (! e1->abfd ? e1->d.address == e2->d.address
 	: e1->symndx >= 0 ? e1->d.addend == e2->d.addend
@@ -1770,8 +1863,10 @@ mips_elf_multi_got_entry_hash (const voi
     + (! entry->abfd
        ? mips_elf_hash_bfd_vma (entry->d.address)
        : entry->symndx >= 0
-       ? (entry->abfd->id
-	  + mips_elf_hash_bfd_vma (entry->d.addend))
+       ? ((entry->tls_type & GOT_TLS_LDM)
+	  ? (GOT_TLS_LDM << 17)
+	  : (entry->abfd->id
+	     + mips_elf_hash_bfd_vma (entry->d.addend)))
        : entry->d.h->root.root.root.hash);
 }
 
@@ -1781,6 +1876,14 @@ mips_elf_multi_got_entry_eq (const void 
   const struct mips_got_entry *e1 = (struct mips_got_entry *)entry1;
   const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2;
 
+  /* Any two LDM entries match.  */
+  if (e1->tls_type & e2->tls_type & GOT_TLS_LDM)
+    return 1;
+
+  /* Nothing else matches an LDM entry.  */
+  if ((e1->tls_type ^ e2->tls_type) & GOT_TLS_LDM)
+    return 0;
+
   return e1->symndx == e2->symndx
     && (e1->symndx >= 0 ? e1->abfd == e2->abfd && e1->d.addend == e2->d.addend
 	: e1->abfd == NULL || e2->abfd == NULL
@@ -1849,13 +1952,299 @@ mips_elf_got_info (bfd *abfd, asection *
   return g;
 }
 
+/* Count the number of relocations needed for a TLS GOT entry, with
+   access types from TLS_TYPE, and symbol H (or a local symbol if H
+   is NULL).  */
+
+static int
+mips_tls_got_relocs (struct bfd_link_info *info, unsigned char tls_type,
+		     struct elf_link_hash_entry *h)
+{
+  int indx = 0;
+  int ret = 0;
+  bfd_boolean need_relocs = FALSE;
+  bfd_boolean dyn = elf_hash_table (info)->dynamic_sections_created;
+
+  if (h && WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+      && (!info->shared || !SYMBOL_REFERENCES_LOCAL (info, h)))
+    indx = h->dynindx;
+
+  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 (!need_relocs)
+    return FALSE;
+
+  if (tls_type & GOT_TLS_GD)
+    {
+      ret++;
+      if (indx != 0)
+	ret++;
+    }
+
+  if (tls_type & GOT_TLS_IE)
+    ret++;
+
+  if ((tls_type & GOT_TLS_LDM) && info->shared)
+    ret++;
+
+  return ret;
+}
+
+/* Count the number of TLS relocations required for the GOT entry in
+   ARG1, if it describes a local symbol.  */
+
+static int
+mips_elf_count_local_tls_relocs (void **arg1, void *arg2)
+{
+  struct mips_got_entry *entry = * (struct mips_got_entry **) arg1;
+  struct mips_elf_count_tls_arg *arg = arg2;
+
+  if (entry->abfd != NULL && entry->symndx != -1)
+    arg->needed += mips_tls_got_relocs (arg->info, entry->tls_type, NULL);
+
+  return 1;
+}
+
+/* Count the number of TLS GOT entries required for the global (or
+   forced-local) symbol in ARG1.  */
+
+static int
+mips_elf_count_global_tls_entries (void *arg1, void *arg2)
+{
+  struct mips_elf_link_hash_entry *hm
+    = (struct mips_elf_link_hash_entry *) arg1;
+  struct mips_elf_count_tls_arg *arg = arg2;
+
+  if (hm->tls_type & GOT_TLS_GD)
+    arg->needed += 2;
+  if (hm->tls_type & GOT_TLS_IE)
+    arg->needed += 1;
+
+  return 1;
+}
+
+/* Count the number of TLS relocations required for the global (or
+   forced-local) symbol in ARG1.  */
+
+static int
+mips_elf_count_global_tls_relocs (void *arg1, void *arg2)
+{
+  struct mips_elf_link_hash_entry *hm
+    = (struct mips_elf_link_hash_entry *) arg1;
+  struct mips_elf_count_tls_arg *arg = arg2;
+
+  arg->needed += mips_tls_got_relocs (arg->info, hm->tls_type, &hm->root);
+
+  return 1;
+}
+
+/* Output a simple dynamic relocation into SRELOC.  */
+
+static void
+mips_elf_output_dynamic_relocation (bfd *output_bfd,
+				    asection *sreloc,
+				    unsigned long indx,
+				    int r_type,
+				    bfd_vma offset)
+{
+  Elf_Internal_Rela rel[3];
+
+  memset (rel, 0, sizeof (rel));
+
+  rel[0].r_info = ELF_R_INFO (output_bfd, indx, r_type);
+  rel[0].r_offset = rel[1].r_offset = rel[2].r_offset = offset;
+
+  if (ABI_64_P (output_bfd))
+    {
+      (*get_elf_backend_data (output_bfd)->s->swap_reloc_out)
+	(output_bfd, &rel[0],
+	 (sreloc->contents
+	  + sreloc->reloc_count * sizeof (Elf64_Mips_External_Rel)));
+    }
+  else
+    bfd_elf32_swap_reloc_out
+      (output_bfd, &rel[0],
+       (sreloc->contents
+	+ sreloc->reloc_count * sizeof (Elf32_External_Rel)));
+  ++sreloc->reloc_count;
+}
+
+/* Initialize a set of TLS GOT entries for one symbol.  */
+
+static void
+mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset,
+			       unsigned char *tls_type_p,
+			       struct bfd_link_info *info,
+			       struct mips_elf_link_hash_entry *h,
+			       bfd_vma value)
+{
+  int indx;
+  asection *sreloc, *sgot;
+  bfd_vma offset, offset2;
+  bfd *dynobj;
+  bfd_boolean need_relocs = FALSE;
+
+  dynobj = elf_hash_table (info)->dynobj;
+  sgot = mips_elf_got_section (dynobj, FALSE);
+
+  indx = 0;
+  if (h != NULL)
+    {
+      bfd_boolean dyn = elf_hash_table (info)->dynamic_sections_created;
+
+      if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, &h->root)
+	  && (!info->shared || !SYMBOL_REFERENCES_LOCAL (info, &h->root)))
+	indx = h->root.dynindx;
+    }
+
+  if (*tls_type_p & GOT_TLS_DONE)
+    return;
+
+  if ((info->shared || indx != 0)
+      && (h == NULL
+	  || ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT
+	  || h->root.type != bfd_link_hash_undefweak))
+    need_relocs = TRUE;
+
+  /* MINUS_ONE means the symbol is not defined in this object.  It may not
+     be defined at all; assume that the value doesn't matter in that
+     case.  Otherwise complain if we would use the value.  */
+  BFD_ASSERT (value != MINUS_ONE || (indx != 0 && need_relocs)
+	      || h->root.root.type == bfd_link_hash_undefweak);
+
+  /* Emit necessary relocations.  */
+  sreloc = mips_elf_rel_dyn_section (dynobj, FALSE);
+
+  /* General Dynamic.  */
+  if (*tls_type_p & GOT_TLS_GD)
+    {
+      offset = got_offset;
+      offset2 = offset + MIPS_ELF_GOT_SIZE (abfd);
+
+      if (need_relocs)
+	{
+	  mips_elf_output_dynamic_relocation
+	    (abfd, sreloc, indx,
+	     ABI_64_P (abfd) ? R_MIPS_TLS_DTPMOD64 : R_MIPS_TLS_DTPMOD32,
+	     sgot->output_offset + sgot->output_section->vma + offset);
+
+	  if (indx)
+	    mips_elf_output_dynamic_relocation
+	      (abfd, sreloc, indx,
+	       ABI_64_P (abfd) ? R_MIPS_TLS_DTPREL64 : R_MIPS_TLS_DTPREL32,
+	       sgot->output_offset + sgot->output_section->vma + offset2);
+	  else
+	    MIPS_ELF_PUT_WORD (abfd, value - dtprel_base (info),
+			       sgot->contents + offset2);
+	}
+      else
+	{
+	  MIPS_ELF_PUT_WORD (abfd, 1,
+			     sgot->contents + offset);
+	  MIPS_ELF_PUT_WORD (abfd, value - dtprel_base (info),
+			     sgot->contents + offset2);
+	}
+
+      got_offset += 2 * MIPS_ELF_GOT_SIZE (abfd);
+    }
+
+  /* Initial Exec model.  */
+  if (*tls_type_p & GOT_TLS_IE)
+    {
+      offset = got_offset;
+
+      if (need_relocs)
+	{
+	  if (indx == 0)
+	    MIPS_ELF_PUT_WORD (abfd, value - elf_hash_table (info)->tls_sec->vma,
+			       sgot->contents + offset);
+	  else
+	    MIPS_ELF_PUT_WORD (abfd, 0,
+			       sgot->contents + offset);
+
+	  mips_elf_output_dynamic_relocation
+	    (abfd, sreloc, indx,
+	     ABI_64_P (abfd) ? R_MIPS_TLS_TPREL64 : R_MIPS_TLS_TPREL32,
+	     sgot->output_offset + sgot->output_section->vma + offset);
+	}
+      else
+	MIPS_ELF_PUT_WORD (abfd, value - tprel_base (info),
+			   sgot->contents + offset);
+    }
+
+  if (*tls_type_p & GOT_TLS_LDM)
+    {
+      /* The initial offset is zero, and the LD offsets will include the
+	 bias by DTP_OFFSET.  */
+      MIPS_ELF_PUT_WORD (abfd, 0,
+			 sgot->contents + got_offset
+			 + MIPS_ELF_GOT_SIZE (abfd));
+
+      if (!info->shared)
+	MIPS_ELF_PUT_WORD (abfd, 1,
+			   sgot->contents + got_offset);
+      else
+	mips_elf_output_dynamic_relocation
+	  (abfd, sreloc, indx,
+	   ABI_64_P (abfd) ? R_MIPS_TLS_DTPMOD64 : R_MIPS_TLS_DTPMOD32,
+	   sgot->output_offset + sgot->output_section->vma + got_offset);
+    }
+
+  *tls_type_p |= GOT_TLS_DONE;
+}
+
+/* Return the GOT index to use for a relocation of type R_TYPE against
+   a symbol accessed using TLS_TYPE models.  The GOT entries for this
+   symbol in this GOT start at GOT_INDEX.  This function initializes the
+   GOT entries and corresponding relocations.  */
+
+static bfd_vma
+mips_tls_got_index (bfd *abfd, bfd_vma got_index, unsigned char *tls_type,
+		    int r_type, struct bfd_link_info *info,
+		    struct mips_elf_link_hash_entry *h, bfd_vma symbol)
+{
+  BFD_ASSERT (r_type == R_MIPS_TLS_GOTTPREL || r_type == R_MIPS_TLS_GD
+	      || r_type == R_MIPS_TLS_LDM);
+
+  mips_elf_initialize_tls_slots (abfd, got_index, tls_type, info, h, symbol);
+
+  if (r_type == R_MIPS_TLS_GOTTPREL)
+    {
+      BFD_ASSERT (*tls_type & GOT_TLS_IE);
+      if (*tls_type & GOT_TLS_GD)
+	return got_index + 2 * MIPS_ELF_GOT_SIZE (abfd);
+      else
+	return got_index;
+    }
+
+  if (r_type == R_MIPS_TLS_GD)
+    {
+      BFD_ASSERT (*tls_type & GOT_TLS_GD);
+      return got_index;
+    }
+
+  if (r_type == R_MIPS_TLS_LDM)
+    {
+      BFD_ASSERT (*tls_type & GOT_TLS_LDM);
+      return got_index;
+    }
+
+  return got_index;
+}
+
 /* Returns the GOT offset at which the indicated address can be found.
-   If there is not yet a GOT entry for this value, create one.  Returns
-   -1 if no satisfactory GOT offset can be found.  */
+   If there is not yet a GOT entry for this value, create one.  If
+   R_SYMNDX refers to a TLS symbol, create a TLS GOT entry instead.
+   Returns -1 if no satisfactory GOT offset can be found.  */
 
 static bfd_vma
 mips_elf_local_got_index (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
-			  bfd_vma value)
+			  bfd_vma value, unsigned long r_symndx,
+			  struct mips_elf_link_hash_entry *h, int r_type)
 {
   asection *sgot;
   struct mips_got_info *g;
@@ -1863,17 +2252,23 @@ mips_elf_local_got_index (bfd *abfd, bfd
 
   g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
 
-  entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value);
-  if (entry)
-    return entry->gotidx;
-  else
+  entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value,
+					   r_symndx, h, r_type);
+  if (!entry)
     return MINUS_ONE;
+
+  if (TLS_RELOC_P (r_type))
+    return mips_tls_got_index (abfd, entry->gotidx, &entry->tls_type, r_type,
+			       info, h, value);
+  else
+    return entry->gotidx;
 }
 
 /* Returns the GOT index for the global symbol indicated by H.  */
 
 static bfd_vma
-mips_elf_global_got_index (bfd *abfd, bfd *ibfd, struct elf_link_hash_entry *h)
+mips_elf_global_got_index (bfd *abfd, bfd *ibfd, struct elf_link_hash_entry *h,
+			   int r_type, struct bfd_link_info *info)
 {
   bfd_vma index;
   asection *sgot;
@@ -1888,29 +2283,64 @@ mips_elf_global_got_index (bfd *abfd, bf
       BFD_ASSERT (h->dynindx >= 0);
 
       g = mips_elf_got_for_ibfd (g, ibfd);
-      if (g->next != gg)
+      if (g->next != gg || TLS_RELOC_P (r_type))
 	{
 	  e.abfd = ibfd;
 	  e.symndx = -1;
 	  e.d.h = (struct mips_elf_link_hash_entry *)h;
+	  e.tls_type = 0;
 
 	  p = htab_find (g->got_entries, &e);
 
 	  BFD_ASSERT (p->gotidx > 0);
-	  return p->gotidx;
+
+	  if (TLS_RELOC_P (r_type))
+	    {
+	      bfd_vma value = MINUS_ONE;
+	      if ((h->root.type == bfd_link_hash_defined
+		   || h->root.type == bfd_link_hash_defweak)
+		  && h->root.u.def.section->output_section)
+		value = (h->root.u.def.value
+			 + h->root.u.def.section->output_offset
+			 + h->root.u.def.section->output_section->vma);
+
+	      return mips_tls_got_index (abfd, p->gotidx, &p->tls_type, r_type,
+					 info, e.d.h, value);
+	    }
+	  else
+	    return p->gotidx;
 	}
     }
 
   if (gg->global_gotsym != NULL)
     global_got_dynindx = gg->global_gotsym->dynindx;
 
-  /* Once we determine the global GOT entry with the lowest dynamic
-     symbol table index, we must put all dynamic symbols with greater
-     indices into the GOT.  That makes it easy to calculate the GOT
-     offset.  */
-  BFD_ASSERT (h->dynindx >= global_got_dynindx);
-  index = ((h->dynindx - global_got_dynindx + g->local_gotno)
-	   * MIPS_ELF_GOT_SIZE (abfd));
+  if (TLS_RELOC_P (r_type))
+    {
+      struct mips_elf_link_hash_entry *hm
+	= (struct mips_elf_link_hash_entry *) h;
+      bfd_vma value = MINUS_ONE;
+
+      if ((h->root.type == bfd_link_hash_defined
+	   || h->root.type == bfd_link_hash_defweak)
+	  && h->root.u.def.section->output_section)
+	value = (h->root.u.def.value
+		 + h->root.u.def.section->output_offset
+		 + h->root.u.def.section->output_section->vma);
+
+      index = mips_tls_got_index (abfd, hm->tls_got_offset, &hm->tls_type,
+				  r_type, info, hm, value);
+    }
+  else
+    {
+      /* Once we determine the global GOT entry with the lowest dynamic
+	 symbol table index, we must put all dynamic symbols with greater
+	 indices into the GOT.  That makes it easy to calculate the GOT
+	 offset.  */
+      BFD_ASSERT (h->dynindx >= global_got_dynindx);
+      index = ((h->dynindx - global_got_dynindx + g->local_gotno)
+	       * MIPS_ELF_GOT_SIZE (abfd));
+    }
   BFD_ASSERT (index < sgot->size);
 
   return index;
@@ -1935,7 +2365,8 @@ mips_elf_got_page (bfd *abfd, bfd *ibfd,
 
   entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot,
 					   (value + 0x8000)
-					   & (~(bfd_vma)0xffff));
+					   & (~(bfd_vma)0xffff), 0,
+					   NULL, R_MIPS_GOT_PAGE);
 
   if (!entry)
     return MINUS_ONE;
@@ -1970,7 +2401,8 @@ mips_elf_got16_entry (bfd *abfd, bfd *ib
 
   g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
 
-  entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value);
+  entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value, 0, NULL,
+					   R_MIPS_GOT16);
   if (entry)
     return entry->gotidx;
   else
@@ -1996,12 +2428,16 @@ mips_elf_got_offset_from_index (bfd *dyn
 }
 
 /* Create a local GOT entry for VALUE.  Return the index of the entry,
-   or -1 if it could not be created.  */
+   or -1 if it could not be created.  If R_SYMNDX refers to a TLS symbol,
+   create a TLS entry instead.  */
 
 static struct mips_got_entry *
 mips_elf_create_local_got_entry (bfd *abfd, bfd *ibfd,
 				 struct mips_got_info *gg,
-				 asection *sgot, bfd_vma value)
+				 asection *sgot, bfd_vma value,
+				 unsigned long r_symndx,
+				 struct mips_elf_link_hash_entry *h,
+				 int r_type)
 {
   struct mips_got_entry entry, **loc;
   struct mips_got_info *g;
@@ -2009,6 +2445,7 @@ mips_elf_create_local_got_entry (bfd *ab
   entry.abfd = NULL;
   entry.symndx = -1;
   entry.d.address = value;
+  entry.tls_type = 0;
 
   g = mips_elf_got_for_ibfd (gg, ibfd);
   if (g == NULL)
@@ -2017,12 +2454,44 @@ mips_elf_create_local_got_entry (bfd *ab
       BFD_ASSERT (g != NULL);
     }
 
+  /* We might have a symbol, H, if it has been forced local.  Use the
+     global entry then.  It doesn't matter whether an entry is local
+     or global for TLS, since the dynamic linker does not
+     automatically relocate TLS GOT entries.  */
+  BFD_ASSERT (h == NULL || h->forced_local);
+  if (TLS_RELOC_P (r_type))
+    {
+      struct mips_got_entry *p;
+
+      entry.abfd = ibfd;
+      if (r_type == R_MIPS_TLS_LDM)
+	{
+	  entry.tls_type = GOT_TLS_LDM;
+	  entry.symndx = 0;
+	  entry.d.addend = 0;
+	}
+      else if (h == NULL)
+	{
+	  entry.symndx = r_symndx;
+	  entry.d.addend = 0;
+	}
+      else
+	entry.d.h = h;
+
+      p = (struct mips_got_entry *)
+	htab_find (g->got_entries, &entry);
+
+      BFD_ASSERT (p);
+      return p;
+    }
+
   loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
 						   INSERT);
   if (*loc)
     return *loc;
 
   entry.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno++;
+  entry.tls_type = 0;
 
   *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
 
@@ -2118,6 +2587,8 @@ mips_elf_sort_hash_table_f (struct mips_
      -1.  */
   if (h->root.got.offset == 2)
     {
+      BFD_ASSERT (h->tls_type == GOT_NORMAL);
+
       if (hsd->max_unref_got_dynindx == hsd->min_got_dynindx)
 	hsd->low = (struct elf_link_hash_entry *) h;
       h->root.dynindx = hsd->max_unref_got_dynindx++;
@@ -2126,6 +2597,8 @@ mips_elf_sort_hash_table_f (struct mips_
     h->root.dynindx = hsd->max_non_got_dynindx++;
   else
     {
+      BFD_ASSERT (h->tls_type == GOT_NORMAL);
+
       h->root.dynindx = --hsd->min_got_dynindx;
       hsd->low = (struct elf_link_hash_entry *) h;
     }
@@ -2140,7 +2613,8 @@ mips_elf_sort_hash_table_f (struct mips_
 static bfd_boolean
 mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
 				   bfd *abfd, struct bfd_link_info *info,
-				   struct mips_got_info *g)
+				   struct mips_got_info *g,
+				   unsigned char tls_flag)
 {
   struct mips_got_entry entry, **loc;
 
@@ -2162,6 +2636,7 @@ mips_elf_record_global_got_symbol (struc
   entry.abfd = abfd;
   entry.symndx = -1;
   entry.d.h = (struct mips_elf_link_hash_entry *) h;
+  entry.tls_type = 0;
 
   loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
 						   INSERT);
@@ -2169,7 +2644,10 @@ mips_elf_record_global_got_symbol (struc
   /* If we've already marked this entry as needing GOT space, we don't
      need to do it again.  */
   if (*loc)
-    return TRUE;
+    {
+      (*loc)->tls_type |= tls_flag;
+      return TRUE;
+    }
 
   *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
 
@@ -2177,6 +2655,8 @@ mips_elf_record_global_got_symbol (struc
     return FALSE;
 
   entry.gotidx = -1;
+  entry.tls_type = tls_flag;
+
   memcpy (*loc, &entry, sizeof entry);
 
   if (h->got.offset != MINUS_ONE)
@@ -2185,7 +2665,8 @@ mips_elf_record_global_got_symbol (struc
   /* By setting this to a value other than -1, we are indicating that
      there needs to be a GOT entry for H.  Avoid using zero, as the
      generic ELF copy_indirect_symbol tests for <= 0.  */
-  h->got.offset = 1;
+  if (tls_flag == 0)
+    h->got.offset = 1;
 
   return TRUE;
 }
@@ -2195,20 +2676,52 @@ mips_elf_record_global_got_symbol (struc
 
 static bfd_boolean
 mips_elf_record_local_got_symbol (bfd *abfd, long symndx, bfd_vma addend,
-				  struct mips_got_info *g)
+				  struct mips_got_info *g,
+				  unsigned char tls_flag)
 {
   struct mips_got_entry entry, **loc;
 
   entry.abfd = abfd;
   entry.symndx = symndx;
   entry.d.addend = addend;
+  entry.tls_type = tls_flag;
   loc = (struct mips_got_entry **)
     htab_find_slot (g->got_entries, &entry, INSERT);
 
   if (*loc)
-    return TRUE;
+    {
+      if (tls_flag == GOT_TLS_GD && !((*loc)->tls_type & GOT_TLS_GD))
+	{
+	  g->tls_gotno += 2;
+	  (*loc)->tls_type |= tls_flag;
+	}
+      else if (tls_flag == GOT_TLS_IE && !((*loc)->tls_type & GOT_TLS_IE))
+	{
+	  g->tls_gotno += 1;
+	  (*loc)->tls_type |= tls_flag;
+	}
+      return TRUE;
+    }
 
-  entry.gotidx = g->local_gotno++;
+  if (tls_flag != 0)
+    {
+      entry.gotidx = -1;
+      entry.tls_type = tls_flag;
+      if (tls_flag == GOT_TLS_IE)
+	g->tls_gotno += 1;
+      else if (tls_flag == GOT_TLS_GD)
+	g->tls_gotno += 2;
+      else if (g->tls_ldm_offset == MINUS_ONE)
+	{
+	  g->tls_ldm_offset = MINUS_TWO;
+	  g->tls_gotno += 2;
+	}
+    }
+  else
+    {
+      entry.gotidx = g->local_gotno++;
+      entry.tls_type = 0;
+    }
 
   *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
 
@@ -2308,6 +2821,9 @@ mips_elf_make_got_per_bfd (void **entryp
       g->global_gotno = 0;
       g->local_gotno = 0;
       g->assigned_gotno = -1;
+      g->tls_gotno = 0;
+      g->tls_assigned_gotno = 0;
+      g->tls_ldm_offset = MINUS_ONE;
       g->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
 					mips_elf_multi_got_entry_eq, NULL);
       if (g->got_entries == NULL)
@@ -2327,7 +2843,14 @@ mips_elf_make_got_per_bfd (void **entryp
 
   *entryp = entry;
 
-  if (entry->symndx >= 0 || entry->d.h->forced_local)
+  if (entry->tls_type)
+    {
+      if (entry->tls_type & (GOT_TLS_GD | GOT_TLS_LDM))
+	g->tls_gotno += 2;
+      if (entry->tls_type & GOT_TLS_IE)
+	g->tls_gotno += 1;
+    }
+  else if (entry->symndx >= 0 || entry->d.h->forced_local)
     ++g->local_gotno;
   else
     ++g->global_gotno;
@@ -2350,11 +2873,26 @@ mips_elf_merge_gots (void **bfd2got_, vo
   struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
   unsigned int lcount = bfd2got->g->local_gotno;
   unsigned int gcount = bfd2got->g->global_gotno;
+  unsigned int tcount = bfd2got->g->tls_gotno;
   unsigned int maxcnt = arg->max_count;
+  bfd_boolean too_many_for_tls = FALSE;
+
+  /* We place TLS GOT entries after both locals and globals.  The globals
+     for the primary GOT may overflow the normal GOT size limit, so be
+     sure not to merge a GOT which requires TLS with the primary GOT in that
+     case.  This doesn't affect non-primary GOTs.  */
+  if (tcount > 0)
+    {
+      unsigned int primary_total = lcount + tcount + arg->global_count;
+      if (primary_total * MIPS_ELF_GOT_SIZE (bfd2got->bfd)
+	   >= MIPS_ELF_GOT_MAX_SIZE (bfd2got->bfd))
+	too_many_for_tls = TRUE;
+    }
 
   /* If we don't have a primary GOT and this is not too big, use it as
      a starting point for the primary GOT.  */
-  if (! arg->primary && lcount + gcount <= maxcnt)
+  if (! arg->primary && lcount + gcount + tcount <= maxcnt
+      && ! too_many_for_tls)
     {
       arg->primary = bfd2got->g;
       arg->primary_count = lcount + gcount;
@@ -2362,12 +2900,13 @@ mips_elf_merge_gots (void **bfd2got_, vo
   /* If it looks like we can merge this bfd's entries with those of
      the primary, merge them.  The heuristics is conservative, but we
      don't have to squeeze it too hard.  */
-  else if (arg->primary
-	   && (arg->primary_count + lcount + gcount) <= maxcnt)
+  else if (arg->primary && ! too_many_for_tls
+	   && (arg->primary_count + lcount + gcount + tcount) <= maxcnt)
     {
       struct mips_got_info *g = bfd2got->g;
       int old_lcount = arg->primary->local_gotno;
       int old_gcount = arg->primary->global_gotno;
+      int old_tcount = arg->primary->tls_gotno;
 
       bfd2got->g = arg->primary;
 
@@ -2384,17 +2923,19 @@ mips_elf_merge_gots (void **bfd2got_, vo
 
       BFD_ASSERT (old_lcount + lcount >= arg->primary->local_gotno);
       BFD_ASSERT (old_gcount + gcount >= arg->primary->global_gotno);
+      BFD_ASSERT (old_tcount + tcount >= arg->primary->tls_gotno);
 
       arg->primary_count = arg->primary->local_gotno
-	+ arg->primary->global_gotno;
+	+ arg->primary->global_gotno + arg->primary->tls_gotno;
     }
   /* If we can merge with the last-created got, do it.  */
   else if (arg->current
-	   && arg->current_count + lcount + gcount <= maxcnt)
+	   && arg->current_count + lcount + gcount + tcount <= maxcnt)
     {
       struct mips_got_info *g = bfd2got->g;
       int old_lcount = arg->current->local_gotno;
       int old_gcount = arg->current->global_gotno;
+      int old_tcount = arg->current->tls_gotno;
 
       bfd2got->g = arg->current;
 
@@ -2408,9 +2949,10 @@ mips_elf_merge_gots (void **bfd2got_, vo
 
       BFD_ASSERT (old_lcount + lcount >= arg->current->local_gotno);
       BFD_ASSERT (old_gcount + gcount >= arg->current->global_gotno);
+      BFD_ASSERT (old_tcount + tcount >= arg->current->tls_gotno);
 
       arg->current_count = arg->current->local_gotno
-	+ arg->current->global_gotno;
+	+ arg->current->global_gotno + arg->current->tls_gotno;
     }
   /* Well, we couldn't merge, so create a new GOT.  Don't check if it
      fits; if it turns out that it doesn't, we'll get relocation
@@ -2420,12 +2962,61 @@ mips_elf_merge_gots (void **bfd2got_, vo
       bfd2got->g->next = arg->current;
       arg->current = bfd2got->g;
 
-      arg->current_count = lcount + gcount;
+      arg->current_count = lcount + gcount + 2 * tcount;
     }
 
   return 1;
 }
 
+/* Set the TLS GOT index for the GOT entry in ENTRYP.  */
+
+static int
+mips_elf_initialize_tls_index (void **entryp, void *p)
+{
+  struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
+  struct mips_got_info *g = p;
+
+  /* We're only interested in TLS symbols.  */
+  if (entry->tls_type == 0)
+    return 1;
+
+  if (entry->symndx == -1)
+    {
+      /* There may be multiple mips_got_entry structs for a global variable
+	 if there is just one GOT.  Just do this once.  */
+      if (g->next == NULL)
+	{
+	  if (entry->d.h->tls_type & GOT_TLS_OFFSET_DONE)
+	    return 1;
+	  entry->d.h->tls_type |= GOT_TLS_OFFSET_DONE;
+	}
+    }
+  else if (entry->tls_type & GOT_TLS_LDM)
+    {
+      /* Similarly, there may be multiple structs for the LDM entry.  */
+      if (g->tls_ldm_offset != MINUS_TWO && g->tls_ldm_offset != MINUS_ONE)
+	{
+	  entry->gotidx = g->tls_ldm_offset;
+	  return 1;
+	}
+    }
+
+  /* Initialize the GOT offset.  */
+  entry->gotidx = MIPS_ELF_GOT_SIZE (entry->abfd) * (long) g->tls_assigned_gotno;
+  if (g->next == NULL && entry->symndx == -1)
+    entry->d.h->tls_got_offset = entry->gotidx;
+
+  if (entry->tls_type & (GOT_TLS_GD | GOT_TLS_LDM))
+    g->tls_assigned_gotno += 2;
+  if (entry->tls_type & GOT_TLS_IE)
+    g->tls_assigned_gotno += 1;
+
+  if (entry->tls_type & GOT_TLS_LDM)
+    g->tls_ldm_offset = entry->gotidx;
+
+  return 1;
+}
+
 /* If passed a NULL mips_got_info in the argument, set the marker used
    to tell whether a global symbol needs a got entry (in the primary
    got) to the given VALUE.
@@ -2447,8 +3038,14 @@ mips_elf_set_global_got_offset (void **e
     = (struct mips_elf_set_global_got_offset_arg *)p;
   struct mips_got_info *g = arg->g;
 
+  if (g && entry->tls_type != GOT_NORMAL)
+    arg->needed_relocs +=
+      mips_tls_got_relocs (arg->info, entry->tls_type,
+			   entry->symndx == -1 ? &entry->d.h->root : NULL);
+
   if (entry->abfd != NULL && entry->symndx == -1
-      && entry->d.h->root.dynindx != -1)
+      && entry->d.h->root.dynindx != -1
+      && entry->d.h->tls_type == GOT_NORMAL)
     {
       if (g)
 	{
@@ -2563,7 +3160,8 @@ mips_elf_adjust_gp (bfd *abfd, struct mi
 
   g = g->next;
 
-  return (g->local_gotno + g->global_gotno) * MIPS_ELF_GOT_SIZE (abfd);
+  return (g->local_gotno + g->global_gotno + g->tls_gotno)
+    * MIPS_ELF_GOT_SIZE (abfd);
 }
 
 /* Turn a single GOT that is too big for 16-bit addressing into
@@ -2590,7 +3188,6 @@ mips_elf_multi_got (bfd *abfd, struct bf
 
   /* Count how many GOT entries each input bfd requires, creating a
      map from bfd to got info while at that.  */
-  mips_elf_resolve_final_got_entries (g);
   htab_traverse (g->got_entries, mips_elf_make_got_per_bfd, &got_per_bfd_arg);
   if (got_per_bfd_arg.obfd == NULL)
     return FALSE;
@@ -2603,6 +3200,10 @@ mips_elf_multi_got (bfd *abfd, struct bf
   got_per_bfd_arg.max_count = ((MIPS_ELF_GOT_MAX_SIZE (abfd)
 				/ MIPS_ELF_GOT_SIZE (abfd))
 			       - MIPS_RESERVED_GOTNO - pages);
+  /* The number of globals that will be included in the primary GOT.
+     See the calls to mips_elf_set_global_got_offset below for more
+     information.  */
+  got_per_bfd_arg.global_count = g->global_gotno;
 
   /* Try to merge the GOTs of input bfds together, as long as they
      don't seem to exceed the maximum GOT size, choosing one of them
@@ -2611,7 +3212,7 @@ mips_elf_multi_got (bfd *abfd, struct bf
   if (got_per_bfd_arg.obfd == NULL)
     return FALSE;
 
-  /* If we find any suitable primary GOT, create an empty one.  */
+  /* If we do not find any suitable primary GOT, create an empty one.  */
   if (got_per_bfd_arg.primary == NULL)
     {
       g->next = (struct mips_got_info *)
@@ -2622,7 +3223,10 @@ mips_elf_multi_got (bfd *abfd, struct bf
       g->next->global_gotsym = NULL;
       g->next->global_gotno = 0;
       g->next->local_gotno = 0;
+      g->next->tls_gotno = 0;
       g->next->assigned_gotno = 0;
+      g->next->tls_assigned_gotno = 0;
+      g->next->tls_ldm_offset = MINUS_ONE;
       g->next->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
 					      mips_elf_multi_got_entry_eq,
 					      NULL);
@@ -2722,6 +3326,7 @@ mips_elf_multi_got (bfd *abfd, struct bf
      points back to the master GOT.  */
   gg->local_gotno = -g->global_gotno;
   gg->global_gotno = g->global_gotno;
+  gg->tls_gotno = 0;
   assign = 0;
   gg->next = gg;
 
@@ -2732,7 +3337,12 @@ mips_elf_multi_got (bfd *abfd, struct bf
       assign += MIPS_RESERVED_GOTNO;
       g->assigned_gotno = assign;
       g->local_gotno += assign + pages;
-      assign = g->local_gotno + g->global_gotno;
+      assign = g->local_gotno + g->global_gotno + g->tls_gotno;
+
+      /* Set up any TLS entries.  We always place the TLS entries after
+	 all non-TLS entries.  */
+      g->tls_assigned_gotno = g->local_gotno + g->global_gotno;
+      htab_traverse (g->got_entries, mips_elf_initialize_tls_index, g);
 
       /* Take g out of the direct list, and push it onto the reversed
 	 list that gg points to.  */
@@ -2749,7 +3359,8 @@ mips_elf_multi_got (bfd *abfd, struct bf
   while (g);
 
   got->size = (gg->next->local_gotno
-		    + gg->next->global_gotno) * MIPS_ELF_GOT_SIZE (abfd);
+		    + gg->next->global_gotno
+		    + gg->next->tls_gotno) * MIPS_ELF_GOT_SIZE (abfd);
 
   return TRUE;
 }
@@ -2968,10 +3579,12 @@ mips_elf_create_got_section (bfd *abfd, 
     return FALSE;
   g->global_gotsym = NULL;
   g->global_gotno = 0;
+  g->tls_gotno = 0;
   g->local_gotno = MIPS_RESERVED_GOTNO;
   g->assigned_gotno = MIPS_RESERVED_GOTNO;
   g->bfd2got = NULL;
   g->next = NULL;
+  g->tls_ldm_offset = MINUS_ONE;
   g->got_entries = htab_try_create (1, mips_elf_got_entry_hash,
 				    mips_elf_got_entry_eq, NULL);
   if (g->got_entries == NULL)
@@ -3268,8 +3881,18 @@ mips_elf_calculate_relocation (bfd *abfd
     case R_MIPS_CALL_HI16:
     case R_MIPS_GOT_LO16:
     case R_MIPS_CALL_LO16:
+    case R_MIPS_TLS_GD:
+    case R_MIPS_TLS_GOTTPREL:
+    case R_MIPS_TLS_LDM:
       /* Find the index into the GOT where this value is located.  */
-      if (!local_p)
+      if (r_type == R_MIPS_TLS_LDM)
+	{
+	  g = mips_elf_local_got_index (abfd, input_bfd, info, 0, 0, NULL,
+					r_type);
+	  if (g == MINUS_ONE)
+	    return bfd_reloc_outofrange;
+	}
+      else if (!local_p)
 	{
 	  /* GOT_PAGE may take a non-zero addend, that is ignored in a
 	     GOT_PAGE relocation that decays to GOT_DISP because the
@@ -3278,11 +3901,13 @@ mips_elf_calculate_relocation (bfd *abfd
 	  BFD_ASSERT (addend == 0 || r_type == R_MIPS_GOT_PAGE);
 	  g = mips_elf_global_got_index (elf_hash_table (info)->dynobj,
 					 input_bfd,
-					 (struct elf_link_hash_entry *) h);
-	  if (! elf_hash_table(info)->dynamic_sections_created
-	      || (info->shared
-		  && (info->symbolic || h->root.dynindx == -1)
-		  && h->root.def_regular))
+					 (struct elf_link_hash_entry *) h,
+					 r_type, info);
+	  if (h->tls_type == GOT_NORMAL
+	      && (! elf_hash_table(info)->dynamic_sections_created
+		  || (info->shared
+		      && (info->symbolic || h->root.dynindx == -1)
+		      && h->root.def_regular)))
 	    {
 	      /* This is a static link or a -Bsymbolic link.  The
 		 symbol is defined locally, or was forced to be local.
@@ -3299,7 +3924,8 @@ mips_elf_calculate_relocation (bfd *abfd
       else
 	{
 	  g = mips_elf_local_got_index (abfd, input_bfd,
-					info, symbol + addend);
+					info, symbol + addend, r_symndx, h,
+					r_type);
 	  if (g == MINUS_ONE)
 	    return bfd_reloc_outofrange;
 	}
@@ -3407,6 +4033,24 @@ mips_elf_calculate_relocation (bfd *abfd
       value &= howto->dst_mask;
       break;
 
+    case R_MIPS_TLS_DTPREL_HI16:
+      value = (mips_elf_high (addend + symbol - dtprel_base (info))
+	       & howto->dst_mask);
+      break;
+
+    case R_MIPS_TLS_DTPREL_LO16:
+      value = (symbol + addend - dtprel_base (info)) & howto->dst_mask;
+      break;
+
+    case R_MIPS_TLS_TPREL_HI16:
+      value = (mips_elf_high (addend + symbol - tprel_base (info))
+	       & howto->dst_mask);
+      break;
+
+    case R_MIPS_TLS_TPREL_LO16:
+      value = (symbol + addend - tprel_base (info)) & howto->dst_mask;
+      break;
+
     case R_MIPS_HI16:
     case R_MIPS16_HI16:
       if (!gp_disp_p)
@@ -3518,6 +4162,9 @@ mips_elf_calculate_relocation (bfd *abfd
 
       /* Fall through.  */
 
+    case R_MIPS_TLS_GD:
+    case R_MIPS_TLS_GOTTPREL:
+    case R_MIPS_TLS_LDM:
     case R_MIPS_GOT_DISP:
     got_disp:
       value = g;
@@ -5288,6 +5935,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, s
 	    case R_MIPS_GOT_PAGE:
 	    case R_MIPS_GOT_OFST:
 	    case R_MIPS_GOT_DISP:
+	    case R_MIPS_TLS_GD:
+	    case R_MIPS_TLS_LDM:
 	      if (dynobj == NULL)
 		elf_hash_table (info)->dynobj = dynobj = abfd;
 	      if (! mips_elf_create_got_section (dynobj, info, FALSE))
@@ -5321,7 +5970,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, s
 	     R_MIPS_CALL_HI16 because these are always followed by an
 	     R_MIPS_GOT_LO16 or R_MIPS_CALL_LO16.  */
 	  if (! mips_elf_record_local_got_symbol (abfd, r_symndx,
-						  rel->r_addend, g))
+						  rel->r_addend, g, 0))
 	    return FALSE;
 	}
 
@@ -5343,7 +5992,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, s
 	  if (h != NULL)
 	    {
 	      /* This symbol requires a global offset table entry.  */
-	      if (! mips_elf_record_global_got_symbol (h, abfd, info, g))
+	      if (! mips_elf_record_global_got_symbol (h, abfd, info, g, 0))
 		return FALSE;
 
 	      /* We need a stub, not a plt entry for the undefined
@@ -5380,11 +6029,52 @@ _bfd_mips_elf_check_relocs (bfd *abfd, s
 	case R_MIPS_GOT_HI16:
 	case R_MIPS_GOT_LO16:
 	case R_MIPS_GOT_DISP:
-	  /* This symbol requires a global offset table entry.  */
-	  if (h && ! mips_elf_record_global_got_symbol (h, abfd, info, g))
+	  if (h && ! mips_elf_record_global_got_symbol (h, abfd, info, g, 0))
 	    return FALSE;
 	  break;
 
+	case R_MIPS_TLS_GOTTPREL:
+	  if (info->shared)
+	    info->flags |= DF_STATIC_TLS;
+	  /* Fall through */
+
+	case R_MIPS_TLS_LDM:
+	  if (r_type == R_MIPS_TLS_LDM)
+	    {
+	      r_symndx = 0;
+	      h = NULL;
+	    }
+	  /* Fall through */
+
+	case R_MIPS_TLS_GD:
+	  /* This symbol requires a global offset table entry, or two
+	     for TLS GD relocations.  */
+	  {
+	    unsigned char flag = (r_type == R_MIPS_TLS_GD
+				  ? GOT_TLS_GD
+				  : r_type == R_MIPS_TLS_LDM
+				  ? GOT_TLS_LDM
+				  : GOT_TLS_IE);
+	    if (h != NULL)
+	      {
+		struct mips_elf_link_hash_entry *hmips =
+		  (struct mips_elf_link_hash_entry *) h;
+		hmips->tls_type |= flag;
+
+		if (h && ! mips_elf_record_global_got_symbol (h, abfd, info, g, flag))
+		  return FALSE;
+	      }
+	    else
+	      {
+		BFD_ASSERT (flag == GOT_TLS_LDM || r_symndx != 0);
+
+		if (! mips_elf_record_local_got_symbol (abfd, r_symndx,
+							rel->r_addend, g, flag))
+		  return FALSE;
+	      }
+	  }
+	  break;
+
 	case R_MIPS_32:
 	case R_MIPS_REL32:
 	case R_MIPS_64:
@@ -5437,7 +6127,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, s
 		  if (! mips_elf_create_got_section (dynobj, info, TRUE))
 		    return FALSE;
 		  g = mips_elf_got_info (dynobj, &sgot);
-		  if (! mips_elf_record_global_got_symbol (h, abfd, info, g))
+		  if (! mips_elf_record_global_got_symbol (h, abfd, info, g, 0))
 		    return FALSE;
 		}
 	    }
@@ -5803,6 +6493,7 @@ _bfd_mips_elf_always_size_sections (bfd 
   bfd_size_type loadable_size = 0;
   bfd_size_type local_gotno;
   bfd *sub;
+  struct mips_elf_count_tls_arg count_tls_arg;
 
   /* The .reginfo section has a fixed size.  */
   ri = bfd_get_section_by_name (output_bfd, ".reginfo");
@@ -5871,9 +6562,30 @@ _bfd_mips_elf_always_size_sections (bfd 
   g->global_gotno = i;
   s->size += i * MIPS_ELF_GOT_SIZE (output_bfd);
 
-  if (s->size > MIPS_ELF_GOT_MAX_SIZE (output_bfd)
-      && ! mips_elf_multi_got (output_bfd, info, g, s, local_gotno))
-    return FALSE;
+  /* We need to calculate tls_gotno for global symbols at this point
+     instead of building it up earlier, to avoid doublecounting
+     entries for one global symbol from multiple input files.  */
+  count_tls_arg.info = info;
+  count_tls_arg.needed = 0;
+  elf_link_hash_traverse (elf_hash_table (info),
+			  mips_elf_count_global_tls_entries,
+			  &count_tls_arg);
+  g->tls_gotno += count_tls_arg.needed;
+  s->size += g->tls_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
+
+  mips_elf_resolve_final_got_entries (g);
+
+  if (s->size > MIPS_ELF_GOT_MAX_SIZE (output_bfd))
+    {
+      if (! mips_elf_multi_got (output_bfd, info, g, s, local_gotno))
+	return FALSE;
+    }
+  else
+    {
+      /* Set up TLS entries for the first GOT.  */
+      g->tls_assigned_gotno = g->global_gotno + g->local_gotno;
+      htab_traverse (g->got_entries, mips_elf_initialize_tls_index, g);
+    }
 
   return TRUE;
 }
@@ -5988,6 +6700,9 @@ _bfd_mips_elf_size_dynamic_sections (bfd
 	      set_got_offset_arg.value = MIPS_ELF_GOT_SIZE (output_bfd);
 	      set_got_offset_arg.info = info;
 
+	      /* NOTE 2005-02-03: How can this call, or the next, ever
+		 find any indirect entries to resolve?  They were all
+		 resolved in mips_elf_multi_got.  */
 	      mips_elf_resolve_final_got_entries (gg);
 	      for (g = gg->next; g && g->next != gg; g = g->next)
 		{
@@ -6013,13 +6728,28 @@ _bfd_mips_elf_size_dynamic_sections (bfd
 		      needed_relocs += g->local_gotno - g->assigned_gotno;
 		      BFD_ASSERT (g->assigned_gotno == g->next->local_gotno
 				  + g->next->global_gotno
+				  + g->next->tls_gotno
 				  + MIPS_RESERVED_GOTNO);
 		    }
 		}
+	    }
+	  else
+	    {
+	      struct mips_elf_count_tls_arg arg;
+	      arg.info = info;
+	      arg.needed = 0;
 
-	      if (needed_relocs)
-		mips_elf_allocate_dynamic_relocations (dynobj, needed_relocs);
+	      htab_traverse (gg->got_entries, mips_elf_count_local_tls_relocs,
+			     &arg);
+	      elf_link_hash_traverse (elf_hash_table (info),
+				      mips_elf_count_global_tls_relocs,
+				      &arg);
+
+	      needed_relocs += arg.needed;
 	    }
+
+	  if (needed_relocs)
+	    mips_elf_allocate_dynamic_relocations (dynobj, needed_relocs);
 	}
       else if (strcmp (name, MIPS_ELF_STUB_SECTION_NAME (output_bfd)) == 0)
 	{
@@ -6646,11 +7376,11 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd
       bfd_vma value;
 
       value = sym->st_value;
-      offset = mips_elf_global_got_index (dynobj, output_bfd, h);
+      offset = mips_elf_global_got_index (dynobj, output_bfd, h, R_MIPS_GOT16, info);
       MIPS_ELF_PUT_WORD (output_bfd, value, sgot->contents + offset);
     }
 
-  if (g->next && h->dynindx != -1)
+  if (g->next && h->dynindx != -1 && h->type != STT_TLS)
     {
       struct mips_got_entry e, *p;
       bfd_vma entry;
@@ -6661,6 +7391,7 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd
       e.abfd = output_bfd;
       e.symndx = -1;
       e.d.h = (struct mips_elf_link_hash_entry *)h;
+      e.tls_type = 0;
 
       for (g = g->next; g->next != gg; g = g->next)
 	{
@@ -6973,7 +7704,8 @@ _bfd_mips_elf_finish_dynamic_sections (b
 
       for (g = gg->next; g->next != gg; g = g->next)
 	{
-	  bfd_vma index = g->next->local_gotno + g->next->global_gotno;
+	  bfd_vma index = g->next->local_gotno + g->next->global_gotno
+	    + g->next->tls_gotno;
 
 	  MIPS_ELF_PUT_WORD (output_bfd, 0, sgot->contents
 			     + index++ * MIPS_ELF_GOT_SIZE (output_bfd));
@@ -7587,6 +8319,11 @@ _bfd_mips_elf_copy_indirect_symbol (cons
     dirmips->readonly_reloc = TRUE;
   if (indmips->no_fn_stub)
     dirmips->no_fn_stub = TRUE;
+
+  if (dirmips->tls_type == 0)
+    dirmips->tls_type = indmips->tls_type;
+  else
+    BFD_ASSERT (indmips->tls_type == 0);
 }
 
 void
@@ -7605,7 +8342,7 @@ _bfd_mips_elf_hide_symbol (struct bfd_li
   h->forced_local = force_local;
 
   dynobj = elf_hash_table (info)->dynobj;
-  if (dynobj != NULL && force_local)
+  if (dynobj != NULL && force_local && h->root.type != STT_TLS)
     {
       got = mips_elf_got_section (dynobj, FALSE);
       g = mips_elf_section_data (got)->u.got_info;
@@ -7623,6 +8360,7 @@ _bfd_mips_elf_hide_symbol (struct bfd_li
 	  e.abfd = dynobj;
 	  e.symndx = -1;
 	  e.d.h = h;
+	  e.tls_type = 0;
 
 	  for (g = g->next; g != gg; g = g->next)
 	    if (htab_find (g->got_entries, &e))
Index: binutils/bfd/elfn32-mips.c
===================================================================
--- binutils.orig/bfd/elfn32-mips.c	2005-02-21 09:36:33.000000000 -0500
+++ binutils/bfd/elfn32-mips.c	2005-02-24 13:48:44.986880916 -0500
@@ -1,6 +1,6 @@
 /* MIPS-specific support for 32-bit ELF
    Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-   2003, 2004 Free Software Foundation, Inc.
+   2003, 2004, 2005 Free Software Foundation, Inc.
 
    Most of the information added by Ian Lance Taylor, Cygnus Support,
    <ian@cygnus.com>.
@@ -600,6 +600,160 @@ static reloc_howto_type elf_mips_howto_t
 	 0x00000000,		/* src_mask */
 	 0x00000000,		/* dst_mask */
 	 FALSE),		/* pcrel_offset */
+
+  /* TLS GD/LD dynamic relocations.  */
+  HOWTO (R_MIPS_TLS_DTPMOD32,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPMOD32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  HOWTO (R_MIPS_TLS_DTPREL32,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPREL32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  EMPTY_HOWTO (R_MIPS_TLS_DTPMOD64),
+  EMPTY_HOWTO (R_MIPS_TLS_DTPREL64),
+
+  /* TLS general dynamic variable reference.  */
+  HOWTO (R_MIPS_TLS_GD,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_GD",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic variable reference.  */
+  HOWTO (R_MIPS_TLS_LDM,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_LDM",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_DTPREL_HI16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPREL_HI16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_DTPREL_LO16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPREL_LO16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_GOTTPREL,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_GOTTPREL",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS IE dynamic relocations.  */
+  HOWTO (R_MIPS_TLS_TPREL32,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_TPREL32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  EMPTY_HOWTO (R_MIPS_TLS_TPREL64),
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPREL_HI16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_TPREL_HI16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPREL_LO16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_TPREL_LO16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
 };
 
 /* The relocation table used for SHT_RELA sections.  */
@@ -1117,6 +1271,160 @@ static reloc_howto_type elf_mips_howto_t
 	 0,			/* src_mask */
 	 0,			/* dst_mask */
 	 FALSE),		/* pcrel_offset */
+
+  /* TLS GD/LD dynamic relocations.  */
+  HOWTO (R_MIPS_TLS_DTPMOD32,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPMOD32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  HOWTO (R_MIPS_TLS_DTPREL32,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPREL32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  EMPTY_HOWTO (R_MIPS_TLS_DTPMOD64),
+  EMPTY_HOWTO (R_MIPS_TLS_DTPREL64),
+
+  /* TLS general dynamic variable reference.  */
+  HOWTO (R_MIPS_TLS_GD,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_GD",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic variable reference.  */
+  HOWTO (R_MIPS_TLS_LDM,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_LDM",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_DTPREL_HI16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPREL_HI16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_DTPREL_LO16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPREL_LO16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_GOTTPREL,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_GOTTPREL",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS IE dynamic relocations.  */
+  HOWTO (R_MIPS_TLS_TPREL32,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_TPREL32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  EMPTY_HOWTO (R_MIPS_TLS_TPREL64),
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPREL_HI16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_TPREL_HI16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPREL_LO16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_TPREL_LO16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
 };
 
 static reloc_howto_type elf_mips16_howto_table_rel[] =
@@ -1701,7 +2009,20 @@ static const struct elf_reloc_map mips_r
   { BFD_RELOC_MIPS_REL16, R_MIPS_REL16 },
   /* Use of R_MIPS_ADD_IMMEDIATE and R_MIPS_PJUMP is deprecated.  */
   { BFD_RELOC_MIPS_RELGOT, R_MIPS_RELGOT },
-  { BFD_RELOC_MIPS_JALR, R_MIPS_JALR }
+  { BFD_RELOC_MIPS_JALR, R_MIPS_JALR },
+  { BFD_RELOC_MIPS_TLS_DTPMOD32, R_MIPS_TLS_DTPMOD32 },
+  { BFD_RELOC_MIPS_TLS_DTPREL32, R_MIPS_TLS_DTPREL32 },
+  { BFD_RELOC_MIPS_TLS_DTPMOD64, R_MIPS_TLS_DTPMOD64 },
+  { BFD_RELOC_MIPS_TLS_DTPREL64, R_MIPS_TLS_DTPREL64 },
+  { BFD_RELOC_MIPS_TLS_GD, R_MIPS_TLS_GD },
+  { BFD_RELOC_MIPS_TLS_LDM, R_MIPS_TLS_LDM },
+  { BFD_RELOC_MIPS_TLS_DTPREL_HI16, R_MIPS_TLS_DTPREL_HI16 },
+  { BFD_RELOC_MIPS_TLS_DTPREL_LO16, R_MIPS_TLS_DTPREL_LO16 },
+  { BFD_RELOC_MIPS_TLS_GOTTPREL, R_MIPS_TLS_GOTTPREL },
+  { BFD_RELOC_MIPS_TLS_TPREL32, R_MIPS_TLS_TPREL32 },
+  { BFD_RELOC_MIPS_TLS_TPREL64, R_MIPS_TLS_TPREL64 },
+  { BFD_RELOC_MIPS_TLS_TPREL_HI16, R_MIPS_TLS_TPREL_HI16 },
+  { BFD_RELOC_MIPS_TLS_TPREL_LO16, R_MIPS_TLS_TPREL_LO16 }
 };
 
 static const struct elf_reloc_map mips16_reloc_map[] =
Index: binutils/bfd/elf32-mips.c
===================================================================
--- binutils.orig/bfd/elf32-mips.c	2005-02-21 09:36:26.000000000 -0500
+++ binutils/bfd/elf32-mips.c	2005-02-24 13:48:44.987880672 -0500
@@ -1,6 +1,6 @@
 /* MIPS-specific support for 32-bit ELF
    Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-   2003, 2004 Free Software Foundation, Inc.
+   2003, 2004, 2005 Free Software Foundation, Inc.
 
    Most of the information added by Ian Lance Taylor, Cygnus Support,
    <ian@cygnus.com>.
@@ -545,6 +545,160 @@ static reloc_howto_type elf_mips_howto_t
 	 0x00000000,		/* src_mask */
 	 0x00000000,		/* dst_mask */
 	 FALSE),		/* pcrel_offset */
+
+  /* TLS GD/LD dynamic relocations.  */
+  HOWTO (R_MIPS_TLS_DTPMOD32,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPMOD32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  HOWTO (R_MIPS_TLS_DTPREL32,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPREL32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  EMPTY_HOWTO (R_MIPS_TLS_DTPMOD64),
+  EMPTY_HOWTO (R_MIPS_TLS_DTPREL64),
+
+  /* TLS general dynamic variable reference.  */
+  HOWTO (R_MIPS_TLS_GD,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_GD",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic variable reference.  */
+  HOWTO (R_MIPS_TLS_LDM,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_LDM",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_DTPREL_HI16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPREL_HI16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_DTPREL_LO16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPREL_LO16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_GOTTPREL,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_GOTTPREL",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS IE dynamic relocations.  */
+  HOWTO (R_MIPS_TLS_TPREL32,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_TPREL32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  EMPTY_HOWTO (R_MIPS_TLS_TPREL64),
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPREL_HI16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_TPREL_HI16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPREL_LO16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_TPREL_LO16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
 };
 
 /* The reloc used for BFD_RELOC_CTOR when doing a 64 bit link.  This
@@ -1062,7 +1216,20 @@ static const struct elf_reloc_map mips_r
   { BFD_RELOC_MIPS_SUB, R_MIPS_SUB },
   { BFD_RELOC_MIPS_GOT_PAGE, R_MIPS_GOT_PAGE },
   { BFD_RELOC_MIPS_GOT_OFST, R_MIPS_GOT_OFST },
-  { BFD_RELOC_MIPS_GOT_DISP, R_MIPS_GOT_DISP }
+  { BFD_RELOC_MIPS_GOT_DISP, R_MIPS_GOT_DISP },
+  { BFD_RELOC_MIPS_TLS_DTPMOD32, R_MIPS_TLS_DTPMOD32 },
+  { BFD_RELOC_MIPS_TLS_DTPREL32, R_MIPS_TLS_DTPREL32 },
+  { BFD_RELOC_MIPS_TLS_DTPMOD64, R_MIPS_TLS_DTPMOD64 },
+  { BFD_RELOC_MIPS_TLS_DTPREL64, R_MIPS_TLS_DTPREL64 },
+  { BFD_RELOC_MIPS_TLS_GD, R_MIPS_TLS_GD },
+  { BFD_RELOC_MIPS_TLS_LDM, R_MIPS_TLS_LDM },
+  { BFD_RELOC_MIPS_TLS_DTPREL_HI16, R_MIPS_TLS_DTPREL_HI16 },
+  { BFD_RELOC_MIPS_TLS_DTPREL_LO16, R_MIPS_TLS_DTPREL_LO16 },
+  { BFD_RELOC_MIPS_TLS_GOTTPREL, R_MIPS_TLS_GOTTPREL },
+  { BFD_RELOC_MIPS_TLS_TPREL32, R_MIPS_TLS_TPREL32 },
+  { BFD_RELOC_MIPS_TLS_TPREL64, R_MIPS_TLS_TPREL64 },
+  { BFD_RELOC_MIPS_TLS_TPREL_HI16, R_MIPS_TLS_TPREL_HI16 },
+  { BFD_RELOC_MIPS_TLS_TPREL_LO16, R_MIPS_TLS_TPREL_LO16 }
 };
 
 static const struct elf_reloc_map mips16_reloc_map[] =
Index: binutils/ld/testsuite/ld-mips-elf/tlsbin-o32.s
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlsbin-o32.s	2005-02-24 13:48:44.988880429 -0500
@@ -0,0 +1,89 @@
+	.file	1 "tlsbin-o32.s"
+	.abicalls
+	.text
+	.align	2
+	.globl	__start
+	.ent	__start
+	.type	__start,@function
+__start:
+	.frame	$fp,16,$31
+	.mask	0x40000000,-8
+	.fmask	0x00000000,0
+	.set	noreorder
+	.cpload $25
+	.set	reorder
+	addiu	$sp,$sp,-16
+	sw	$fp,8($sp)
+	move	$fp,$sp
+	.cprestore	0
+
+	# General Dynamic
+	lw	$25,%call16(__tls_get_addr)($28)
+	addiu	$4,$28,%tlsgd(tlsvar_gd)
+	jal	$25
+
+	# Local Dynamic
+	lw	$25,%call16(__tls_get_addr)($28)
+	addiu	$4,$28,%tlsldm(tlsvar_ld)
+	jal	$25
+
+	move	$2,$2		# Arbitrary instructions
+
+	lui	$3,%dtprel_hi(tlsvar_ld)
+	addiu	$3,$3,%dtprel_lo(tlsvar_ld)
+	addu	$3,$3,$2
+
+	# Initial Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lw	$3,%gottprel(tlsvar_ie)($28)
+	addu	$3,$3,$2
+
+	# Local Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lui	$3,%tprel_hi(tlsvar_le)
+	addiu	$3,$3,%tprel_lo(tlsvar_le)
+	addu	$3,$3,$2
+
+	move	$sp,$fp
+	lw	$fp,8($sp)
+	addiu	$sp,$sp,16
+	j	$31
+	.end	__start
+
+	.globl __tls_get_addr
+__tls_get_addr:
+	j $31
+
+	.section		.tbss,"awT",@nobits
+	.align	2
+	.global	tlsvar_gd
+	.type	tlsvar_gd,@object
+	.size	tlsvar_gd,4
+tlsvar_gd:
+	.space	4
+	.global	tlsvar_ie
+	.type	tlsvar_ie,@object
+	.size	tlsvar_ie,4
+tlsvar_ie:
+	.space	4
+
+	.section		.tdata,"awT"
+	.align	2
+	.global	tlsvar_ld
+	.hidden	tlsvar_ld
+	.type	tlsvar_ld,@object
+	.size	tlsvar_ld,4
+tlsvar_ld:
+	.word	1
+	.global	tlsvar_le
+	.hidden	tlsvar_le
+	.type	tlsvar_le,@object
+	.size	tlsvar_le,4
+tlsvar_le:
+	.word	1
Index: binutils/ld/testsuite/ld-mips-elf/mips-dyn.ld
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/mips-dyn.ld	2005-02-24 13:48:45.003876771 -0500
@@ -0,0 +1,223 @@
+/* Script for -z combreloc: combine and sort reloc sections */
+OUTPUT_FORMAT("elf32-tradbigmips", "elf32-tradbigmips",
+	      "elf32-tradlittlemips")
+OUTPUT_ARCH(mips)
+ENTRY(__start)
+SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib");
+/* Do we need any of these for elf?
+   __DYNAMIC = 0;    */
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  PROVIDE (__executable_start = 0x0400000); . = 0x0400000 + SIZEOF_HEADERS;
+  .interp         : { *(.interp) }
+  .reginfo        : { *(.reginfo) }
+  .dynamic        : { *(.dynamic) }
+  .hash           : { *(.hash) }
+  .dynsym         : { *(.dynsym) }
+  .dynstr         : { *(.dynstr) }
+  .gnu.version    : { *(.gnu.version) }
+  .gnu.version_d  : { *(.gnu.version_d) }
+  .gnu.version_r  : { *(.gnu.version_r) }
+  .rel.dyn        :
+    {
+      *(.rel.init)
+      *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
+      *(.rel.fini)
+      *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
+      *(.rel.data.rel.ro*)
+      *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
+      *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
+      *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
+      *(.rel.ctors)
+      *(.rel.dtors)
+      *(.rel.got)
+      *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*)
+      *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*)
+      *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*)
+      *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*)
+      *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
+    }
+  .rela.dyn       :
+    {
+      *(.rela.init)
+      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+      *(.rela.fini)
+      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
+      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
+      *(.rela.ctors)
+      *(.rela.dtors)
+      *(.rela.got)
+      *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*)
+      *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*)
+      *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*)
+      *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*)
+      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+    }
+  .rel.plt        : { *(.rel.plt) }
+  .rela.plt       : { *(.rela.plt) }
+  .init           :
+  {
+    KEEP (*(.init))
+  } =0
+  .plt            : { *(.plt) }
+  .text           :
+  {
+    _ftext = . ;
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    KEEP (*(.text.*personality*))
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+    *(.mips16.fn.*) *(.mips16.call.*)
+  } =0
+  .fini           :
+  {
+    KEEP (*(.fini))
+  } =0
+  PROVIDE (__etext = .);
+  PROVIDE (_etext = .);
+  PROVIDE (etext = .);
+  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+  .rodata1        : { *(.rodata1) }
+  .sdata2         : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) }
+  .sbss2          : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+  .eh_frame_hdr : { *(.eh_frame_hdr) }
+  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+  .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.  */
+  . = 0x10000000;
+  /* Exception handling  */
+  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+  .gcc_except_table   : ONLY_IF_RW { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
+  /* Thread Local Storage sections  */
+  .tdata	  : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+  .tbss		  : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+  /* Ensure the __preinit_array_start label is properly aligned.  We
+     could instead move the label definition inside the section, but
+     the linker would then create the section even if it turns out to
+     be empty, which isn't pretty.  */
+  . = ALIGN(32 / 8);
+  PROVIDE (__preinit_array_start = .);
+  .preinit_array     : { KEEP (*(.preinit_array)) }
+  PROVIDE (__preinit_array_end = .);
+  PROVIDE (__init_array_start = .);
+  .init_array     : { KEEP (*(.init_array)) }
+  PROVIDE (__init_array_end = .);
+  PROVIDE (__fini_array_start = .);
+  .fini_array     : { KEEP (*(.fini_array)) }
+  PROVIDE (__fini_array_end = .);
+  .ctors          :
+  {
+    /* gcc uses crtbegin.o to find the start of
+       the constructors, so we make sure it is
+       first.  Because this is a wildcard, it
+       doesn't matter if the user does not
+       actually link against crtbegin.o; the
+       linker won't look for a file to match a
+       wildcard.  The wildcard also means that it
+       doesn't matter which directory crtbegin.o
+       is in.  */
+    KEEP (*crtbegin*.o(.ctors))
+    /* We don't want to include the .ctor section from
+       from the crtend.o file until after the sorted ctors.
+       The .ctor section from the crtend file contains the
+       end of ctors marker and it must be last */
+    KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+  }
+  .dtors          :
+  {
+    KEEP (*crtbegin*.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+  }
+  .jcr            : { KEEP (*(.jcr)) }
+  .data.rel.ro : { *(.data.rel.ro.local) *(.data.rel.ro*) }
+  .data           :
+  {
+    _fdata = . ;
+    *(.data .data.* .gnu.linkonce.d.*)
+    KEEP (*(.gnu.linkonce.d.*personality*))
+    SORT(CONSTRUCTORS)
+  }
+  .data1          : { *(.data1) }
+  _gp = ALIGN(16) + 0x7ff0;
+  .got            : { *(.got.plt) *(.got) }
+  /* We want the small data sections together, so single-instruction offsets
+     can access them all, and initialized data all before uninitialized, so
+     we can shorten the on-disk segment size.  */
+  .sdata          :
+  {
+    *(.sdata .sdata.* .gnu.linkonce.s.*)
+  }
+  .lit8           : { *(.lit8) }
+  .lit4           : { *(.lit4) }
+  _edata = .;
+  PROVIDE (edata = .);
+  __bss_start = .;
+  _fbss = .;
+  .sbss           :
+  {
+    PROVIDE (__sbss_start = .);
+    PROVIDE (___sbss_start = .);
+    *(.dynsbss)
+    *(.sbss .sbss.* .gnu.linkonce.sb.*)
+    *(.scommon)
+    PROVIDE (__sbss_end = .);
+    PROVIDE (___sbss_end = .);
+  }
+  .bss            :
+  {
+   *(.dynbss)
+   *(.bss .bss.* .gnu.linkonce.b.*)
+   *(COMMON)
+   /* Align here to ensure that the .bss section occupies space up to
+      _end.  Align after .bss to ensure correct alignment even if the
+      .bss section disappears because there are no input sections.  */
+   . = ALIGN(32 / 8);
+  }
+  . = ALIGN(32 / 8);
+  _end = .;
+  PROVIDE (end = .);
+  /* Stabs debugging sections.  */
+  .stab          0 : { *(.stab) }
+  .stabstr       0 : { *(.stabstr) }
+  .stab.excl     0 : { *(.stab.excl) }
+  .stab.exclstr  0 : { *(.stab.exclstr) }
+  .stab.index    0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment       0 : { *(.comment) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
+  .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
+  /DISCARD/ : { *(.note.GNU-stack) }
+}
Index: binutils/bfd/libbfd.h
===================================================================
--- binutils.orig/bfd/libbfd.h	2005-02-21 09:36:37.000000000 -0500
+++ binutils/bfd/libbfd.h	2005-02-24 13:48:45.004876527 -0500
@@ -928,6 +928,19 @@ static const char *const bfd_reloc_code_
   "BFD_RELOC_MIPS_REL16",
   "BFD_RELOC_MIPS_RELGOT",
   "BFD_RELOC_MIPS_JALR",
+  "BFD_RELOC_MIPS_TLS_DTPMOD32",
+  "BFD_RELOC_MIPS_TLS_DTPREL32",
+  "BFD_RELOC_MIPS_TLS_DTPMOD64",
+  "BFD_RELOC_MIPS_TLS_DTPREL64",
+  "BFD_RELOC_MIPS_TLS_GD",
+  "BFD_RELOC_MIPS_TLS_LDM",
+  "BFD_RELOC_MIPS_TLS_DTPREL_HI16",
+  "BFD_RELOC_MIPS_TLS_DTPREL_LO16",
+  "BFD_RELOC_MIPS_TLS_GOTTPREL",
+  "BFD_RELOC_MIPS_TLS_TPREL32",
+  "BFD_RELOC_MIPS_TLS_TPREL64",
+  "BFD_RELOC_MIPS_TLS_TPREL_HI16",
+  "BFD_RELOC_MIPS_TLS_TPREL_LO16",
 
   "BFD_RELOC_FRV_LABEL16",
   "BFD_RELOC_FRV_LABEL24",
Index: binutils/bfd/elf64-mips.c
===================================================================
--- binutils.orig/bfd/elf64-mips.c	2005-02-21 09:36:29.000000000 -0500
+++ binutils/bfd/elf64-mips.c	2005-02-24 13:48:45.005876283 -0500
@@ -1,5 +1,5 @@
 /* MIPS-specific support for 64-bit ELF
-   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
    Free Software Foundation, Inc.
    Ian Lance Taylor, Cygnus Support
    Linker support added by Mark Mitchell, CodeSourcery, LLC.
@@ -635,6 +635,160 @@ static reloc_howto_type mips_elf64_howto
 	 0,			/* src_mask */
 	 0x00000000,		/* dst_mask */
 	 FALSE),		/* pcrel_offset */
+
+  /* TLS relocations.  */
+  EMPTY_HOWTO (R_MIPS_TLS_DTPMOD32),
+  EMPTY_HOWTO (R_MIPS_TLS_DTPREL32),
+
+  HOWTO (R_MIPS_TLS_DTPMOD64,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size (0 = byte, 1 = short, 2 = long) */
+	 64,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPMOD64",	/* name */
+	 TRUE,			/* partial_inplace */
+	 MINUS_ONE,		/* src_mask */
+	 MINUS_ONE,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  HOWTO (R_MIPS_TLS_DTPREL64,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size (0 = byte, 1 = short, 2 = long) */
+	 64,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPREL64",	/* name */
+	 TRUE,			/* partial_inplace */
+	 MINUS_ONE,		/* src_mask */
+	 MINUS_ONE,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS general dynamic variable reference.  */
+  HOWTO (R_MIPS_TLS_GD,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_GD",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic variable reference.  */
+  HOWTO (R_MIPS_TLS_LDM,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_LDM",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_DTPREL_HI16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPREL_HI16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_DTPREL_LO16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPREL_LO16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_GOTTPREL,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_GOTTPREL",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS IE dynamic relocations.  */
+  EMPTY_HOWTO (R_MIPS_TLS_TPREL32),
+
+  HOWTO (R_MIPS_TLS_TPREL64,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size (0 = byte, 1 = short, 2 = long) */
+	 64,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_TPREL64",	/* name */
+	 TRUE,			/* partial_inplace */
+	 MINUS_ONE,		/* src_mask */
+	 MINUS_ONE,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPREL_HI16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_TPREL_HI16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPREL_LO16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_TPREL_LO16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
 };
 
 /* The relocation table used for SHT_RELA sections.  */
@@ -1151,6 +1305,120 @@ static reloc_howto_type mips_elf64_howto
 	 0,			/* src_mask */
 	 0x00000000,		/* dst_mask */
 	 FALSE),		/* pcrel_offset */
+
+  /* TLS relocations.  */
+  EMPTY_HOWTO (R_MIPS_TLS_DTPMOD32),
+  EMPTY_HOWTO (R_MIPS_TLS_DTPREL32),
+  EMPTY_HOWTO (R_MIPS_TLS_DTPMOD64),
+  EMPTY_HOWTO (R_MIPS_TLS_DTPREL64),
+
+  /* TLS general dynamic variable reference.  */
+  HOWTO (R_MIPS_TLS_GD,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_GD",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic variable reference.  */
+  HOWTO (R_MIPS_TLS_LDM,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_LDM",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_DTPREL_HI16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPREL_HI16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_DTPREL_LO16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_DTPREL_LO16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_GOTTPREL,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_GOTTPREL",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  EMPTY_HOWTO (R_MIPS_TLS_TPREL32),
+  EMPTY_HOWTO (R_MIPS_TLS_TPREL64),
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPREL_HI16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_TPREL_HI16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPREL_LO16,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc, /* special_function */
+	 "R_MIPS_TLS_TPREL_LO16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
 };
 
 static reloc_howto_type mips16_elf64_howto_table_rel[] =
@@ -1893,7 +2161,20 @@ static const struct elf_reloc_map mips_r
   { BFD_RELOC_MIPS_REL16, R_MIPS_REL16 },
   /* Use of R_MIPS_ADD_IMMEDIATE and R_MIPS_PJUMP is deprecated.  */
   { BFD_RELOC_MIPS_RELGOT, R_MIPS_RELGOT },
-  { BFD_RELOC_MIPS_JALR, R_MIPS_JALR }
+  { BFD_RELOC_MIPS_JALR, R_MIPS_JALR },
+  { BFD_RELOC_MIPS_TLS_DTPMOD32, R_MIPS_TLS_DTPMOD32 },
+  { BFD_RELOC_MIPS_TLS_DTPREL32, R_MIPS_TLS_DTPREL32 },
+  { BFD_RELOC_MIPS_TLS_DTPMOD64, R_MIPS_TLS_DTPMOD64 },
+  { BFD_RELOC_MIPS_TLS_DTPREL64, R_MIPS_TLS_DTPREL64 },
+  { BFD_RELOC_MIPS_TLS_GD, R_MIPS_TLS_GD },
+  { BFD_RELOC_MIPS_TLS_LDM, R_MIPS_TLS_LDM },
+  { BFD_RELOC_MIPS_TLS_DTPREL_HI16, R_MIPS_TLS_DTPREL_HI16 },
+  { BFD_RELOC_MIPS_TLS_DTPREL_LO16, R_MIPS_TLS_DTPREL_LO16 },
+  { BFD_RELOC_MIPS_TLS_GOTTPREL, R_MIPS_TLS_GOTTPREL },
+  { BFD_RELOC_MIPS_TLS_TPREL32, R_MIPS_TLS_TPREL32 },
+  { BFD_RELOC_MIPS_TLS_TPREL64, R_MIPS_TLS_TPREL64 },
+  { BFD_RELOC_MIPS_TLS_TPREL_HI16, R_MIPS_TLS_TPREL_HI16 },
+  { BFD_RELOC_MIPS_TLS_TPREL_LO16, R_MIPS_TLS_TPREL_LO16 }
 };
 
 static const struct elf_reloc_map mips16_reloc_map[] =
Index: binutils/ld/testsuite/ld-mips-elf/tlslib-o32.got
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlslib-o32.got	2005-02-24 13:48:45.005876283 -0500
@@ -0,0 +1,17 @@
+
+tmpdir/tlslib-o32.so:     file format elf32-tradbigmips
+
+DYNAMIC RELOCATION RECORDS
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_NONE       \*ABS\*
+00040534 R_MIPS_TLS_DTPMOD32  \*ABS\*
+0004053c R_MIPS_TLS_DTPMOD32  tlsvar_gd
+00040540 R_MIPS_TLS_DTPREL32  tlsvar_gd
+00040530 R_MIPS_TLS_TPREL32  tlsvar_ie
+
+
+Contents of section .got:
+ 40510 00000000 80000000 00000000 00000000  ................
+ 40520 00000000 00000000 00000000 000004e0  ................
+ 40530 00000000 00000000 00000000 00000000  ................
+ 40540 00000000                             ....            
Index: binutils/ld/testsuite/ld-mips-elf/tlslib-o32.d
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlslib-o32.d	2005-02-24 13:48:45.006876039 -0500
@@ -0,0 +1,45 @@
+
+.*:     file format elf32-tradbigmips
+
+Disassembly of section .text:
+
+.* <fn>:
+ .*:	3c1c0005 	lui	gp,0x5
+ .*:	279c80a0 	addiu	gp,gp,-32608
+ .*:	0399e021 	addu	gp,gp,t9
+ .*:	27bdfff0 	addiu	sp,sp,-16
+ .*:	afbe0008 	sw	s8,8\(sp\)
+ .*:	03a0f021 	move	s8,sp
+ .*:	afbc0000 	sw	gp,0\(sp\)
+ .*:	8f99802c 	lw	t9,-32724\(gp\)
+ .*:	2784803c 	addiu	a0,gp,-32708
+ .*:	0320f809 	jalr	t9
+ .*:	00000000 	nop
+ .*:	8fdc0000 	lw	gp,0\(s8\)
+ .*:	00000000 	nop
+ .*:	8f99802c 	lw	t9,-32724\(gp\)
+ .*:	27848034 	addiu	a0,gp,-32716
+ .*:	0320f809 	jalr	t9
+ .*:	00000000 	nop
+ .*:	8fdc0000 	lw	gp,0\(s8\)
+ .*:	00401021 	move	v0,v0
+ .*:	3c030000 	lui	v1,0x0
+ .*:	24638000 	addiu	v1,v1,-32768
+ .*:	00621821 	addu	v1,v1,v0
+ .*:	7c02283b 	rdhwr	v0,\$5
+ .*:	8f838030 	lw	v1,-32720\(gp\)
+ .*:	00000000 	nop
+ .*:	00621821 	addu	v1,v1,v0
+ .*:	03c0e821 	move	sp,s8
+ .*:	8fbe0008 	lw	s8,8\(sp\)
+ .*:	03e00008 	jr	ra
+ .*:	27bd0010 	addiu	sp,sp,16
+	...
+Disassembly of section .MIPS.stubs:
+
+.* <.MIPS.stubs>:
+ .*:	8f998010 	lw	t9,-32752\(gp\)
+ .*:	03e07821 	move	t7,ra
+ .*:	0320f809 	jalr	t9
+ .*:	241800.* 	li	t8,.*
+	...
Index: binutils/ld/testsuite/ld-mips-elf/tlslib-o32.s
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlslib-o32.s	2005-02-24 13:48:45.006876039 -0500
@@ -0,0 +1,70 @@
+	.file	1 "tlslib-o32.s"
+	.abicalls
+	.text
+	.align	2
+	.globl	fn
+	.ent	fn
+	.type	fn,@function
+fn:
+	.frame	$fp,16,$31
+	.mask	0x40000000,-8
+	.fmask	0x00000000,0
+	.set	noreorder
+	.cpload $25
+	.set	reorder
+	addiu	$sp,$sp,-16
+	sw	$fp,8($sp)
+	move	$fp,$sp
+	.cprestore	0
+
+	# General Dynamic
+	lw	$25,%call16(__tls_get_addr)($28)
+	addiu	$4,$28,%tlsgd(tlsvar_gd)
+	jal	$25
+
+	# Local Dynamic
+	lw	$25,%call16(__tls_get_addr)($28)
+	addiu	$4,$28,%tlsldm(tlsvar_ld)
+	jal	$25
+
+	move	$2,$2		# Arbitrary instructions
+
+	lui	$3,%dtprel_hi(tlsvar_ld)
+	addiu	$3,$3,%dtprel_lo(tlsvar_ld)
+	addu	$3,$3,$2
+
+	# Initial Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lw	$3,%gottprel(tlsvar_ie)($28)
+	addu	$3,$3,$2
+
+	move	$sp,$fp
+	lw	$fp,8($sp)
+	addiu	$sp,$sp,16
+	j	$31
+	.end	fn
+
+	.section		.tbss,"awT",@nobits
+	.align	2
+	.global	tlsvar_gd
+	.type	tlsvar_gd,@object
+	.size	tlsvar_gd,4
+tlsvar_gd:
+	.space	4
+	.global	tlsvar_ie
+	.type	tlsvar_ie,@object
+	.size	tlsvar_ie,4
+tlsvar_ie:
+	.space	4
+
+	.section		.tdata,"awT"
+	.align	2
+	.global	tlsvar_ld
+	.hidden	tlsvar_ld
+	.type	tlsvar_ld,@object
+	.size	tlsvar_ld,4
+tlsvar_ld:
+	.word	1
Index: binutils/ld/testsuite/ld-mips-elf/mips-elf.exp
===================================================================
--- binutils.orig/ld/testsuite/ld-mips-elf/mips-elf.exp	2005-02-21 09:37:20.000000000 -0500
+++ binutils/ld/testsuite/ld-mips-elf/mips-elf.exp	2005-02-24 13:48:45.006876039 -0500
@@ -1,5 +1,5 @@
 # Expect script for MIPS ELF linker tests
-#   Copyright 2002, 2003 Free Software Foundation, Inc.
+#   Copyright 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
 #
 # This file is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -99,3 +99,67 @@ run_dump_test "mips16-hilo"
 if {$has_newabi} {
     run_dump_test "mips16-hilo-n32"
 }
+
+# For tests which may involve multiple files, use run_ld_link_tests.
+
+# List contains test-items with 3 items followed by 2 lists:
+# 0:name 1:ld options 2:assembler options
+# 3:filenames of assembler files 4: action and options. 5: name of output file
+
+# Actions:
+# objdump: Apply objdump options on result.  Compare with regex (last arg).
+# nm: Apply nm options on result.  Compare with regex (last arg).
+# readelf: Apply readelf options on result.  Compare with regex (last arg).
+
+set mips_tls_tests {
+    {"Static executable with TLS" "-static -melf32btsmip -T mips-dyn.ld"
+     "-EB -march=mips1 -32 -KPIC" {tlsbin-o32.s}
+     {{objdump {-dr -m mips:isa32r2} tlsbin-o32.d} {objdump -srj.got tlsbin-o32.got}}
+     "tls-static-o32"}
+    {"Shared library with TLS" "-shared -melf32btsmip -T mips-lib.ld"
+     "-EB -march=mips1 -32 -KPIC" {tlslib-o32.s}
+     {{objdump {-dr -m mips:isa32r2} tlslib-o32.d} {objdump -Rsj.got tlslib-o32.got}}
+     "tlslib-o32.so"}
+    {"Dynamic executable with TLS"
+     "-melf32btsmip -T mips-dyn.ld tmpdir/tlslib-o32.so"
+     "-EB -march=mips1 -32 -KPIC" {tlsdyn-o32.s}
+     {{objdump {-dr -m mips:isa32r2} tlsdyn-o32.d} {objdump -Rsj.got tlsdyn-o32.got}}
+     "tls-dynamic-o32"}
+    {"Shared library with multiple GOTs and TLS"
+     "-shared -melf32btsmip -T mips-lib.ld"
+     "-EB -march=mips1 -32 -KPIC" {tls-multi-got-1-1.s tls-multi-got-1-2.s}
+     {{readelf {-d -r} tls-multi-got-1.r}
+      {objdump {-dr -m mips:isa32r2} tls-multi-got-1.d}
+      {objdump -Rsj.got tls-multi-got-1.got}}
+     "tlslib-multi.so"}
+    {"Shared library with TLS and versioning"
+     "-shared -melf32btsmip -T mips-lib.ld --version-script tlslib.ver"
+     "-EB -march=mips1 -32 -KPIC" {tlslib-o32.s}
+     {{objdump {-dr -m mips:isa32r2} tlslib-o32.d} {objdump -Rsj.got tlslib-o32-ver.got}}
+     "tlslib-o32-ver.so"}
+    {"Dynamic executable with TLS and versioning"
+     "-melf32btsmip -T mips-dyn.ld tmpdir/tlslib-o32-ver.so"
+     "-EB -march=mips1 -32 -KPIC" {tlsdyn-o32.s tlsdyn-o32-2.s}
+     {{objdump {-dr -m mips:isa32r2} tlsdyn-o32-1.d} {objdump -Rsj.got tlsdyn-o32-1.got}}
+     "tls-dynamic-o32-ver"}
+    {"Dynamic executable with TLS and versioning (order 2)"
+     "-melf32btsmip -T mips-dyn.ld tmpdir/tlsdyn-o32.o tmpdir/tlslib-o32-ver.so tmpdir/tlsdyn-o32-2.o"
+     "-EB -march=mips1 -32 -KPIC" {}
+     {{objdump {-dr -m mips:isa32r2} tlsdyn-o32-2.d} {objdump -Rsj.got tlsdyn-o32-2.got}}
+     "tls-dynamic-o32-ver-2"}
+    {"Dynamic executable with TLS and versioning (order 3)"
+     "-melf32btsmip -T mips-dyn.ld tmpdir/tlsdyn-o32-2.o tmpdir/tlslib-o32-ver.so tmpdir/tlsdyn-o32.o"
+     "-EB -march=mips1 -32 -KPIC" {}
+     {{objdump {-dr -m mips:isa32r2} tlsdyn-o32-3.d} {objdump -Rsj.got tlsdyn-o32-3.got}}
+     "tls-dynamic-o32-ver-3"}
+    {"Shared library with TLS and hidden symbols"
+     "-shared -melf32btsmip -T mips-lib.ld --version-script tlslib-hidden.ver"
+     "-EB -march=mips1 -32 -KPIC" {tlslib-o32.s}
+     {{objdump {-dr -m mips:isa32r2} tlslib-o32.d} {objdump -Rsj.got tlslib-o32-hidden.got}}
+     "tlslib-o32-hidden.so"}
+}
+
+if {[istarget mips*-*-linux*]} {
+    run_ld_link_tests $mips_tls_tests
+}
+
Index: binutils/gas/testsuite/gas/mips/tls-o32.d
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/gas/testsuite/gas/mips/tls-o32.d	2005-02-24 13:48:45.006876039 -0500
@@ -0,0 +1,55 @@
+#as: -EB -march=mips1 -mabi=32
+#objdump: -dr
+#name: MIPS ELF TLS o32
+
+dump.o:     file format elf32-.*bigmips
+
+Disassembly of section .text:
+
+00000000 <fn>:
+   0:	3c1c0000 	lui	gp,0x0
+			0: R_MIPS_HI16	_gp_disp
+   4:	279c0000 	addiu	gp,gp,0
+			4: R_MIPS_LO16	_gp_disp
+   8:	0399e021 	addu	gp,gp,t9
+   c:	27bdfff0 	addiu	sp,sp,-16
+  10:	afbe0008 	sw	s8,8\(sp\)
+  14:	03a0f021 	move	s8,sp
+  18:	afbc0000 	sw	gp,0\(sp\)
+  1c:	8f990000 	lw	t9,0\(gp\)
+			1c: R_MIPS_CALL16	__tls_get_addr
+  20:	27840000 	addiu	a0,gp,0
+			20: R_MIPS_TLS_GD	tlsvar_gd
+  24:	0320f809 	jalr	t9
+  28:	00000000 	nop
+  2c:	8fdc0000 	lw	gp,0\(s8\)
+  30:	00000000 	nop
+  34:	8f990000 	lw	t9,0\(gp\)
+			34: R_MIPS_CALL16	__tls_get_addr
+  38:	27840000 	addiu	a0,gp,0
+			38: R_MIPS_TLS_LDM	tlsvar_ld
+  3c:	0320f809 	jalr	t9
+  40:	00000000 	nop
+  44:	8fdc0000 	lw	gp,0\(s8\)
+  48:	00401021 	move	v0,v0
+  4c:	3c030000 	lui	v1,0x0
+			4c: R_MIPS_TLS_DTPREL_HI16	tlsvar_ld
+  50:	24630000 	addiu	v1,v1,0
+			50: R_MIPS_TLS_DTPREL_LO16	tlsvar_ld
+  54:	00621821 	addu	v1,v1,v0
+  58:	7c02283b 	0x7c02283b
+  5c:	8f830000 	lw	v1,0\(gp\)
+			5c: R_MIPS_TLS_GOTTPREL	tlsvar_ie
+  60:	00000000 	nop
+  64:	00621821 	addu	v1,v1,v0
+  68:	7c02283b 	0x7c02283b
+  6c:	3c030000 	lui	v1,0x0
+			6c: R_MIPS_TLS_TPREL_HI16	tlsvar_le
+  70:	34630000 	ori	v1,v1,0x0
+			70: R_MIPS_TLS_TPREL_LO16	tlsvar_le
+  74:	00621821 	addu	v1,v1,v0
+  78:	03c0e821 	move	sp,s8
+  7c:	8fbe0008 	lw	s8,8\(sp\)
+  80:	03e00008 	jr	ra
+  84:	27bd0010 	addiu	sp,sp,16
+#pass
Index: binutils/gas/testsuite/gas/mips/tls-o32.s
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/gas/testsuite/gas/mips/tls-o32.s	2005-02-24 13:48:45.007875796 -0500
@@ -0,0 +1,85 @@
+	.file	1 "tls.s"
+	.abicalls
+	.text
+	.align	2
+	.globl	fn
+	.ent	fn
+	.type	fn,@function
+fn:
+	.frame	$fp,16,$31
+	.mask	0x40000000,-8
+	.fmask	0x00000000,0
+	.set	noreorder
+	.cpload $25
+	.set	reorder
+	addiu	$sp,$sp,-16
+	sw	$fp,8($sp)
+	move	$fp,$sp
+	.cprestore	0
+
+	# General Dynamic
+	lw	$25,%call16(__tls_get_addr)($28)
+	addiu	$4,$28,%tlsgd(tlsvar_gd)
+	jal	$25
+
+	# Local Dynamic
+	lw	$25,%call16(__tls_get_addr)($28)
+	addiu	$4,$28,%tlsldm(tlsvar_ld)
+	jal	$25
+
+	move	$2,$2		# Arbitrary instructions
+
+	lui	$3,%dtprel_hi(tlsvar_ld)
+	addiu	$3,$3,%dtprel_lo(tlsvar_ld)
+	addu	$3,$3,$2
+
+	# Initial Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lw	$3,%gottprel(tlsvar_ie)($28)
+	addu	$3,$3,$2
+
+	# Local Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lui	$3,%tprel_hi(tlsvar_le)
+	ori	$3,$3,%tprel_lo(tlsvar_le)
+	addu	$3,$3,$2
+
+	move	$sp,$fp
+	lw	$fp,8($sp)
+	addiu	$sp,$sp,16
+	j	$31
+	.end	fn
+
+	.section		.tbss,"awT",@nobits
+	.align	2
+	.global	tlsvar_gd
+	.type	tlsvar_gd,@object
+	.size	tlsvar_gd,4
+tlsvar_gd:
+	.space	4
+	.global	tlsvar_ie
+	.type	tlsvar_ie,@object
+	.size	tlsvar_ie,4
+tlsvar_ie:
+	.space	4
+
+	.section		.tdata,"awT"
+	.align	2
+	.global	tlsvar_ld
+	.hidden	tlsvar_ld
+	.type	tlsvar_ld,@object
+	.size	tlsvar_ld,4
+tlsvar_ld:
+	.word	1
+	.global	tlsvar_le
+	.hidden	tlsvar_le
+	.type	tlsvar_le,@object
+	.size	tlsvar_le,4
+tlsvar_le:
+	.word	1
Index: binutils/bfd/reloc.c
===================================================================
--- binutils.orig/bfd/reloc.c	2005-02-21 09:36:39.000000000 -0500
+++ binutils/bfd/reloc.c	2005-02-24 13:48:45.019872870 -0500
@@ -1,6 +1,6 @@
 /* BFD support for handling relocation entries.
    Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-   2000, 2001, 2002, 2003, 2004
+   2000, 2001, 2002, 2003, 2004, 2005
    Free Software Foundation, Inc.
    Written by Cygnus Support.
 
@@ -2125,6 +2125,32 @@ ENUMX
   BFD_RELOC_MIPS_RELGOT
 ENUMX
   BFD_RELOC_MIPS_JALR
+ENUMX
+  BFD_RELOC_MIPS_TLS_DTPMOD32
+ENUMX
+  BFD_RELOC_MIPS_TLS_DTPREL32
+ENUMX
+  BFD_RELOC_MIPS_TLS_DTPMOD64
+ENUMX
+  BFD_RELOC_MIPS_TLS_DTPREL64
+ENUMX
+  BFD_RELOC_MIPS_TLS_GD
+ENUMX
+  BFD_RELOC_MIPS_TLS_LDM
+ENUMX
+  BFD_RELOC_MIPS_TLS_DTPREL_HI16
+ENUMX
+  BFD_RELOC_MIPS_TLS_DTPREL_LO16
+ENUMX
+  BFD_RELOC_MIPS_TLS_GOTTPREL
+ENUMX
+  BFD_RELOC_MIPS_TLS_TPREL32
+ENUMX
+  BFD_RELOC_MIPS_TLS_TPREL64
+ENUMX
+  BFD_RELOC_MIPS_TLS_TPREL_HI16
+ENUMX
+  BFD_RELOC_MIPS_TLS_TPREL_LO16
 ENUMDOC
   MIPS ELF relocations.
 COMMENT
Index: binutils/bfd/bfd-in2.h
===================================================================
--- binutils.orig/bfd/bfd-in2.h	2005-02-24 13:47:20.352539809 -0500
+++ binutils/bfd/bfd-in2.h	2005-02-24 13:48:45.021872382 -0500
@@ -2388,6 +2388,19 @@ to compensate for the borrow when the lo
   BFD_RELOC_MIPS_REL16,
   BFD_RELOC_MIPS_RELGOT,
   BFD_RELOC_MIPS_JALR,
+  BFD_RELOC_MIPS_TLS_DTPMOD32,
+  BFD_RELOC_MIPS_TLS_DTPREL32,
+  BFD_RELOC_MIPS_TLS_DTPMOD64,
+  BFD_RELOC_MIPS_TLS_DTPREL64,
+  BFD_RELOC_MIPS_TLS_GD,
+  BFD_RELOC_MIPS_TLS_LDM,
+  BFD_RELOC_MIPS_TLS_DTPREL_HI16,
+  BFD_RELOC_MIPS_TLS_DTPREL_LO16,
+  BFD_RELOC_MIPS_TLS_GOTTPREL,
+  BFD_RELOC_MIPS_TLS_TPREL32,
+  BFD_RELOC_MIPS_TLS_TPREL64,
+  BFD_RELOC_MIPS_TLS_TPREL_HI16,
+  BFD_RELOC_MIPS_TLS_TPREL_LO16,
 
 
 /* Fujitsu Frv Relocations.  */
Index: binutils/ld/testsuite/ld-mips-elf/mips-lib.ld
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/mips-lib.ld	2005-02-24 13:48:45.021872382 -0500
@@ -0,0 +1,218 @@
+/* Script for --shared -z combreloc: shared library, combine & sort relocs */
+OUTPUT_FORMAT("elf32-tradbigmips", "elf32-tradbigmips",
+	      "elf32-tradlittlemips")
+OUTPUT_ARCH(mips)
+ENTRY(__start)
+SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib");
+/* Do we need any of these for elf?
+   __DYNAMIC = 0;    */
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0 + SIZEOF_HEADERS;
+  .reginfo        : { *(.reginfo) }
+  .dynamic        : { *(.dynamic) }
+  .hash           : { *(.hash) }
+  .dynsym         : { *(.dynsym) }
+  .dynstr         : { *(.dynstr) }
+  .gnu.version    : { *(.gnu.version) }
+  .gnu.version_d  : { *(.gnu.version_d) }
+  .gnu.version_r  : { *(.gnu.version_r) }
+  .rel.dyn        :
+    {
+      *(.rel.init)
+      *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
+      *(.rel.fini)
+      *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
+      *(.rel.data.rel.ro*)
+      *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
+      *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
+      *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
+      *(.rel.ctors)
+      *(.rel.dtors)
+      *(.rel.got)
+      *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*)
+      *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*)
+      *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*)
+      *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*)
+      *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
+    }
+  .rela.dyn       :
+    {
+      *(.rela.init)
+      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+      *(.rela.fini)
+      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
+      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
+      *(.rela.ctors)
+      *(.rela.dtors)
+      *(.rela.got)
+      *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*)
+      *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*)
+      *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*)
+      *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*)
+      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+    }
+  .rel.plt        : { *(.rel.plt) }
+  .rela.plt       : { *(.rela.plt) }
+  .init           :
+  {
+    KEEP (*(.init))
+  } =0
+  .plt            : { *(.plt) }
+  .text           :
+  {
+    _ftext = . ;
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    KEEP (*(.text.*personality*))
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+    *(.mips16.fn.*) *(.mips16.call.*)
+  } =0
+  .fini           :
+  {
+    KEEP (*(.fini))
+  } =0
+  PROVIDE (__etext = .);
+  PROVIDE (_etext = .);
+  PROVIDE (etext = .);
+  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+  .rodata1        : { *(.rodata1) }
+  .eh_frame_hdr : { *(.eh_frame_hdr) }
+  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+  .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 (0x40000) - ((0x40000 - .) & (0x40000 - 1)); . = DATA_SEGMENT_ALIGN (0x40000, 0x1000);
+  /* Exception handling  */
+  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+  .gcc_except_table   : ONLY_IF_RW { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
+  /* Thread Local Storage sections  */
+  .tdata	  : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+  .tbss		  : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+  /* Ensure the __preinit_array_start label is properly aligned.  We
+     could instead move the label definition inside the section, but
+     the linker would then create the section even if it turns out to
+     be empty, which isn't pretty.  */
+  . = ALIGN(32 / 8);
+  .preinit_array     : { KEEP (*(.preinit_array)) }
+  .init_array     : { KEEP (*(.init_array)) }
+  .fini_array     : { KEEP (*(.fini_array)) }
+  .ctors          :
+  {
+    /* gcc uses crtbegin.o to find the start of
+       the constructors, so we make sure it is
+       first.  Because this is a wildcard, it
+       doesn't matter if the user does not
+       actually link against crtbegin.o; the
+       linker won't look for a file to match a
+       wildcard.  The wildcard also means that it
+       doesn't matter which directory crtbegin.o
+       is in.  */
+    KEEP (*crtbegin*.o(.ctors))
+    /* We don't want to include the .ctor section from
+       from the crtend.o file until after the sorted ctors.
+       The .ctor section from the crtend file contains the
+       end of ctors marker and it must be last */
+    KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+  }
+  .dtors          :
+  {
+    KEEP (*crtbegin*.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+  }
+  .jcr            : { KEEP (*(.jcr)) }
+  .data.rel.ro : { *(.data.rel.ro.local) *(.data.rel.ro*) }
+  . = DATA_SEGMENT_RELRO_END (0, .);
+  .data           :
+  {
+    _fdata = . ;
+    *(.data .data.* .gnu.linkonce.d.*)
+    KEEP (*(.gnu.linkonce.d.*personality*))
+    SORT(CONSTRUCTORS)
+  }
+  .data1          : { *(.data1) }
+  _gp = ALIGN(16) + 0x7ff0;
+  .got            : { *(.got.plt) *(.got) }
+  .sdata2         : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) }
+  .sbss2          : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+  /* We want the small data sections together, so single-instruction offsets
+     can access them all, and initialized data all before uninitialized, so
+     we can shorten the on-disk segment size.  */
+  .sdata          :
+  {
+    *(.sdata .sdata.* .gnu.linkonce.s.*)
+  }
+  .lit8           : { *(.lit8) }
+  .lit4           : { *(.lit4) }
+  _edata = .;
+  PROVIDE (edata = .);
+  __bss_start = .;
+  _fbss = .;
+  .sbss           :
+  {
+    PROVIDE (__sbss_start = .);
+    PROVIDE (___sbss_start = .);
+    *(.dynsbss)
+    *(.sbss .sbss.* .gnu.linkonce.sb.*)
+    *(.scommon)
+    PROVIDE (__sbss_end = .);
+    PROVIDE (___sbss_end = .);
+  }
+  .bss            :
+  {
+   *(.dynbss)
+   *(.bss .bss.* .gnu.linkonce.b.*)
+   *(COMMON)
+   /* Align here to ensure that the .bss section occupies space up to
+      _end.  Align after .bss to ensure correct alignment even if the
+      .bss section disappears because there are no input sections.  */
+   . = ALIGN(32 / 8);
+  }
+  . = ALIGN(32 / 8);
+  _end = .;
+  PROVIDE (end = .);
+  . = DATA_SEGMENT_END (.);
+  /* Stabs debugging sections.  */
+  .stab          0 : { *(.stab) }
+  .stabstr       0 : { *(.stabstr) }
+  .stab.excl     0 : { *(.stab.excl) }
+  .stab.exclstr  0 : { *(.stab.exclstr) }
+  .stab.index    0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment       0 : { *(.comment) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
+  .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
+  /DISCARD/ : { *(.note.GNU-stack) }
+}
Index: binutils/gas/testsuite/gas/mips/mips.exp
===================================================================
--- binutils.orig/gas/testsuite/gas/mips/mips.exp	2005-02-24 13:47:29.486308610 -0500
+++ binutils/gas/testsuite/gas/mips/mips.exp	2005-02-24 13:48:45.022872138 -0500
@@ -336,6 +336,11 @@ proc run_list_test_arches { name opts ar
     foreach arch $arch_list {
 	run_list_test_arch "$name" "$opts" "$arch"
     }
+
+    if $elf {
+	run_list_test "tls-ill" "-32"
+	run_dump_test "tls-o32"
+    }
 }
 
 
Index: binutils/ld/testsuite/ld-mips-elf/tlsbin-o32.got
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlsbin-o32.got	2005-02-24 13:48:45.022872138 -0500
@@ -0,0 +1,8 @@
+
+.*:     file format elf32-tradbigmips
+
+Contents of section .got:
+ 10000010 00000000 80000000 00000000 00000000  ................
+ 10000020 00000000 00000000 00000000 00400158  .............@.X
+ 10000030 ffff900c 00000001 00000000 00000001  ................
+ 10000040 ffff8008                             ....            
Index: binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32.d
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32.d	2005-02-24 13:48:45.022872138 -0500
@@ -0,0 +1,58 @@
+
+.*:     file format elf32-tradbigmips
+
+Disassembly of section .text:
+
+.* <__start>:
+  .*:	3c1c0fc0 	lui	gp,0xfc0
+  .*:	279c7b50 	addiu	gp,gp,31568
+  .*:	0399e021 	addu	gp,gp,t9
+  .*:	27bdfff0 	addiu	sp,sp,-16
+  .*:	afbe0008 	sw	s8,8\(sp\)
+  .*:	03a0f021 	move	s8,sp
+  .*:	afbc0000 	sw	gp,0\(sp\)
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848038 	addiu	a0,gp,-32712
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848048 	addiu	a0,gp,-32696
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848030 	addiu	a0,gp,-32720
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00401021 	move	v0,v0
+  .*:	3c030000 	lui	v1,0x0
+  .*:	24638000 	addiu	v1,v1,-32768
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	7c02283b 	rdhwr	v0,\$5
+  .*:	8f838044 	lw	v1,-32700\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	8f838040 	lw	v1,-32704\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	7c02283b 	rdhwr	v0,\$5
+  .*:	3c030000 	lui	v1,0x0
+  .*:	24639004 	addiu	v1,v1,-28668
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	03c0e821 	move	sp,s8
+  .*:	8fbe0008 	lw	s8,8\(sp\)
+  .*:	03e00008 	jr	ra
+  .*:	27bd0010 	addiu	sp,sp,16
+
+.* <__tls_get_addr>:
+  .*:	03e00008 	jr	ra
+  .*:	00000000 	nop
+	...
+Disassembly of section .MIPS.stubs:
+
+.* <.MIPS.stubs>:
+	...
Index: binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32.got
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32.got	2005-02-24 13:48:45.022872138 -0500
@@ -0,0 +1,19 @@
+
+tmpdir/tls-dynamic-o32:     file format elf32-tradbigmips
+
+DYNAMIC RELOCATION RECORDS
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_NONE       \*ABS\*
+10000048 R_MIPS_TLS_DTPMOD32  tlsbin_gd
+1000004c R_MIPS_TLS_DTPREL32  tlsbin_gd
+10000058 R_MIPS_TLS_DTPMOD32  tlsvar_gd
+1000005c R_MIPS_TLS_DTPREL32  tlsvar_gd
+10000054 R_MIPS_TLS_TPREL32  tlsbin_ie
+10000050 R_MIPS_TLS_TPREL32  tlsvar_ie
+
+
+Contents of section .got:
+ 10000020 00000000 80000000 00000000 00000000  ................
+ 10000030 00000000 00000000 00000000 0040056c  ................
+ 10000040 00000001 00000000 00000000 00000000  ................
+ 10000050 00000000 00000000 00000000 00000000  ................
Index: binutils/ld/testsuite/ld-mips-elf/tlsbin-o32.d
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlsbin-o32.d	2005-02-24 13:48:45.022872138 -0500
@@ -0,0 +1,43 @@
+.*:     file format elf32-tradbigmips
+
+Disassembly of section .text:
+
+004000d0 <__start>:
+  4000d0:	3c1c0fc0 	lui	gp,0xfc0
+  4000d4:	279c7f30 	addiu	gp,gp,32560
+  4000d8:	0399e021 	addu	gp,gp,t9
+  4000dc:	27bdfff0 	addiu	sp,sp,-16
+  4000e0:	afbe0008 	sw	s8,8\(sp\)
+  4000e4:	03a0f021 	move	s8,sp
+  4000e8:	afbc0000 	sw	gp,0\(sp\)
+  4000ec:	8f99802c 	lw	t9,-32724\(gp\)
+  4000f0:	2784803c 	addiu	a0,gp,-32708
+  4000f4:	0320f809 	jalr	t9
+  4000f8:	00000000 	nop
+  4000fc:	8fdc0000 	lw	gp,0\(s8\)
+  400100:	00000000 	nop
+  400104:	8f99802c 	lw	t9,-32724\(gp\)
+  400108:	27848034 	addiu	a0,gp,-32716
+  40010c:	0320f809 	jalr	t9
+  400110:	00000000 	nop
+  400114:	8fdc0000 	lw	gp,0\(s8\)
+  400118:	00401021 	move	v0,v0
+  40011c:	3c030000 	lui	v1,0x0
+  400120:	24638000 	addiu	v1,v1,-32768
+  400124:	00621821 	addu	v1,v1,v0
+  400128:	7c02283b 	rdhwr	v0,\$5
+  40012c:	8f838030 	lw	v1,-32720\(gp\)
+  400130:	00000000 	nop
+  400134:	00621821 	addu	v1,v1,v0
+  400138:	7c02283b 	rdhwr	v0,\$5
+  40013c:	3c030000 	lui	v1,0x0
+  400140:	24639004 	addiu	v1,v1,-28668
+  400144:	00621821 	addu	v1,v1,v0
+  400148:	03c0e821 	move	sp,s8
+  40014c:	8fbe0008 	lw	s8,8\(sp\)
+  400150:	03e00008 	jr	ra
+  400154:	27bd0010 	addiu	sp,sp,16
+
+00400158 <__tls_get_addr>:
+  400158:	03e00008 	jr	ra
+  40015c:	00000000 	nop
Index: binutils/include/elf/mips.h
===================================================================
--- binutils.orig/include/elf/mips.h	2005-02-21 09:36:43.000000000 -0500
+++ binutils/include/elf/mips.h	2005-02-24 13:56:56.718770094 -0500
@@ -1,5 +1,6 @@
 /* MIPS ELF support for BFD.
-   Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2003
+   Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2003,
+   2004, 2005
    Free Software Foundation, Inc.
 
    By Ian Lance Taylor, Cygnus Support, <ian@cygnus.com>, from
@@ -72,7 +73,21 @@ START_RELOC_NUMBERS (elf_mips_reloc_type
   RELOC_NUMBER (R_MIPS_PJUMP, 35)
   RELOC_NUMBER (R_MIPS_RELGOT, 36)
   RELOC_NUMBER (R_MIPS_JALR, 37)
-  FAKE_RELOC (R_MIPS_max, 38)
+  /* TLS relocations.  */
+  RELOC_NUMBER (R_MIPS_TLS_DTPMOD32, 38)
+  RELOC_NUMBER (R_MIPS_TLS_DTPREL32, 39)
+  RELOC_NUMBER (R_MIPS_TLS_DTPMOD64, 40)
+  RELOC_NUMBER (R_MIPS_TLS_DTPREL64, 41)
+  RELOC_NUMBER (R_MIPS_TLS_GD, 42)
+  RELOC_NUMBER (R_MIPS_TLS_LDM, 43)
+  RELOC_NUMBER (R_MIPS_TLS_DTPREL_HI16, 44)
+  RELOC_NUMBER (R_MIPS_TLS_DTPREL_LO16, 45)
+  RELOC_NUMBER (R_MIPS_TLS_GOTTPREL, 46)
+  RELOC_NUMBER (R_MIPS_TLS_TPREL32, 47)
+  RELOC_NUMBER (R_MIPS_TLS_TPREL64, 48)
+  RELOC_NUMBER (R_MIPS_TLS_TPREL_HI16, 49)
+  RELOC_NUMBER (R_MIPS_TLS_TPREL_LO16, 50)
+  FAKE_RELOC (R_MIPS_max, 51)
   /* These relocs are used for the mips16.  */
   FAKE_RELOC (R_MIPS16_min, 100)
   RELOC_NUMBER (R_MIPS16_26, 100)
Index: binutils/gas/config/tc-mips.c
===================================================================
--- binutils.orig/gas/config/tc-mips.c	2005-02-24 13:47:26.545027060 -0500
+++ binutils/gas/config/tc-mips.c	2005-02-24 13:48:45.028870675 -0500
@@ -9856,6 +9856,13 @@ static const struct percent_op_match mip
   {"%highest", BFD_RELOC_MIPS_HIGHEST},
   {"%higher", BFD_RELOC_MIPS_HIGHER},
   {"%neg", BFD_RELOC_MIPS_SUB},
+  {"%tlsgd", BFD_RELOC_MIPS_TLS_GD},
+  {"%tlsldm", BFD_RELOC_MIPS_TLS_LDM},
+  {"%dtprel_hi", BFD_RELOC_MIPS_TLS_DTPREL_HI16},
+  {"%dtprel_lo", BFD_RELOC_MIPS_TLS_DTPREL_LO16},
+  {"%tprel_hi", BFD_RELOC_MIPS_TLS_TPREL_HI16},
+  {"%tprel_lo", BFD_RELOC_MIPS_TLS_TPREL_LO16},
+  {"%gottprel", BFD_RELOC_MIPS_TLS_GOTTPREL},
 #endif
   {"%hi", BFD_RELOC_HI16_S}
 };
@@ -9892,6 +9899,11 @@ parse_relocation (char **str, bfd_reloc_
   for (i = 0; i < limit; i++)
     if (strncasecmp (*str, percent_op[i].str, strlen (percent_op[i].str)) == 0)
       {
+	int len = strlen (percent_op[i].str);
+
+	if (!ISSPACE ((*str)[len]) && (*str)[len] != '(')
+	  continue;
+
 	*str += strlen (percent_op[i].str);
 	*reloc = percent_op[i].reloc;
 
@@ -11028,6 +11040,16 @@ md_apply_fix3 (fixS *fixP, valueT *valP,
 
   switch (fixP->fx_r_type)
     {
+    case BFD_RELOC_MIPS_TLS_GD:
+    case BFD_RELOC_MIPS_TLS_LDM:
+    case BFD_RELOC_MIPS_TLS_DTPREL_HI16:
+    case BFD_RELOC_MIPS_TLS_DTPREL_LO16:
+    case BFD_RELOC_MIPS_TLS_GOTTPREL:
+    case BFD_RELOC_MIPS_TLS_TPREL_HI16:
+    case BFD_RELOC_MIPS_TLS_TPREL_LO16:
+      S_SET_THREAD_LOCAL (fixP->fx_addsy);
+      /* fall through */
+
     case BFD_RELOC_MIPS_JMP:
     case BFD_RELOC_MIPS_SHIFT5:
     case BFD_RELOC_MIPS_SHIFT6:
Index: binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32.s
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32.s	2005-02-24 13:48:45.029870431 -0500
@@ -0,0 +1,96 @@
+	.file	1 "tlsbin-o32.s"
+	.abicalls
+	.text
+	.align	2
+	.globl	__start
+	.ent	__start
+	.type	__start,@function
+__start:
+	.frame	$fp,16,$31
+	.mask	0x40000000,-8
+	.fmask	0x00000000,0
+	.set	noreorder
+	.cpload $25
+	.set	reorder
+	addiu	$sp,$sp,-16
+	sw	$fp,8($sp)
+	move	$fp,$sp
+	.cprestore	0
+
+	# General Dynamic
+	lw	$25,%call16(__tls_get_addr)($28)
+	addiu	$4,$28,%tlsgd(tlsbin_gd)
+	jal	$25
+
+	lw	$25,%call16(__tls_get_addr)($28)
+	addiu	$4,$28,%tlsgd(tlsvar_gd)
+	jal	$25
+
+	# Local Dynamic
+	lw	$25,%call16(__tls_get_addr)($28)
+	addiu	$4,$28,%tlsldm(tlsbin_ld)
+	jal	$25
+
+	move	$2,$2		# Arbitrary instructions
+
+	lui	$3,%dtprel_hi(tlsbin_ld)
+	addiu	$3,$3,%dtprel_lo(tlsbin_ld)
+	addu	$3,$3,$2
+
+	# Initial Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lw	$3,%gottprel(tlsbin_ie)($28)
+	addu	$3,$3,$2
+
+	lw	$3,%gottprel(tlsvar_ie)($28)
+	addu	$3,$3,$2
+
+	# Local Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lui	$3,%tprel_hi(tlsbin_le)
+	addiu	$3,$3,%tprel_lo(tlsbin_le)
+	addu	$3,$3,$2
+
+	move	$sp,$fp
+	lw	$fp,8($sp)
+	addiu	$sp,$sp,16
+	j	$31
+	.end	__start
+
+	.globl __tls_get_addr
+__tls_get_addr:
+	j $31
+
+	.section		.tbss,"awT",@nobits
+	.align	2
+	.global	tlsbin_gd
+	.type	tlsbin_gd,@object
+	.size	tlsbin_gd,4
+tlsbin_gd:
+	.space	4
+	.global	tlsbin_ie
+	.type	tlsbin_ie,@object
+	.size	tlsbin_ie,4
+tlsbin_ie:
+	.space	4
+
+	.section		.tdata,"awT"
+	.align	2
+	.global	tlsbin_ld
+	.hidden	tlsbin_ld
+	.type	tlsbin_ld,@object
+	.size	tlsbin_ld,4
+tlsbin_ld:
+	.word	1
+	.global	tlsbin_le
+	.hidden	tlsbin_le
+	.type	tlsbin_le,@object
+	.size	tlsbin_le,4
+tlsbin_le:
+	.word	1
Index: binutils/ld/testsuite/ld-mips-elf/tls-multi-got-1.got
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tls-multi-got-1.got	2005-02-24 13:48:45.029870431 -0500
@@ -0,0 +1,58 @@
+
+.*:     file format elf32-tradbigmips
+
+DYNAMIC RELOCATION RECORDS
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_NONE       \*ABS\*
+00149630 R_MIPS_TLS_DTPMOD32  \*ABS\*
+0013f9a8 R_MIPS_TLS_DTPMOD32  \*ABS\*
+0014963c R_MIPS_TLS_DTPMOD32  tlsvar_gd
+00149640 R_MIPS_TLS_DTPREL32  tlsvar_gd
+0013f9b4 R_MIPS_TLS_DTPMOD32  tlsvar_gd
+0013f9b8 R_MIPS_TLS_DTPREL32  tlsvar_gd
+00149638 R_MIPS_TLS_TPREL32  tlsvar_ie
+0013f9b0 R_MIPS_TLS_TPREL32  tlsvar_ie
+0013602c R_MIPS_REL32      sym_2_8355
+#...
+00142d4c R_MIPS_REL32      sym_1_0945
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+00000000 R_MIPS_NONE       \*ABS\*
+
+
+Contents of section .got:
+ 122480 00000000 80000000 00000000 00000000  ................
+ 122490 00000000 00000000 00000000 00000000  ................
+ 1224a0 00000000 00000000 00000000 00000000  ................
+ 1224b0 00000000 000e0aac 000d35f4 000d35e4  ..........5...5.
+#...
+ 13f990 00000000 00000000 00000000 00000000  ................
+ 13f9a0 00000000 00000000 00000000 00000000  ................
+ 13f9b0 00000000 00000000 00000000 00000000  ................
+ 13f9c0 80000000 00000000 00000000 00000000  ................
+#...
+ 149600 00000000 00000000 00000000 00000000  ................
+ 149610 00000000 00000000 00000000 00000000  ................
+ 149620 00000000 00000000 00000000 00000000  ................
+ 149630 00000000 00000000 00000000 00000000  ................
+ 149640 00000000                             ....            
+#pass
Index: binutils/ld/testsuite/ld-mips-elf/tls-multi-got-1-1.s
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tls-multi-got-1-1.s	2005-02-24 13:48:45.029870431 -0500
@@ -0,0 +1,39 @@
+.macro	one_sym	count
+.globl	sym_1_\count
+sym_1_\count:
+	la	$2, sym_1_\count
+.endm
+
+.irp	thou,0,1,2,3,4,5,6,7,8,9
+.irp	hund,0,1,2,3,4,5,6,7,8,9
+.irp	tens,0,1,2,3,4,5,6,7,8,9
+.irp	ones,0,1,2,3,4,5,6,7,8,9
+one_sym	\thou\hund\tens\ones
+.endr
+.endr
+.endr
+.endr
+
+tls_bits_1:
+	addiu	$4,$28,%tlsgd(tlsvar_gd)
+	addiu	$4,$28,%tlsldm(tlsvar_ld)
+	addiu	$4,$2,%gottprel(tlsvar_ie)
+
+        .section                .tbss,"awT",@nobits
+        .align  2
+        .global tlsvar_gd
+        .type   tlsvar_gd,@object
+        .size   tlsvar_gd,4
+tlsvar_gd:
+        .space  4
+        .global tlsvar_ie
+        .type   tlsvar_ie,@object
+        .size   tlsvar_ie,4
+tlsvar_ie:
+        .space  4
+        .global tlsvar_ld
+        .hidden tlsvar_ld
+        .type   tlsvar_ld,@object
+        .size   tlsvar_ld,4
+tlsvar_ld:
+        .word   1
Index: binutils/ld/testsuite/ld-mips-elf/tls-multi-got-1.d
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tls-multi-got-1.d	2005-02-24 13:48:45.029870431 -0500
@@ -0,0 +1,20 @@
+
+.*:     file format elf32-tradbigmips
+
+Disassembly of section .text:
+
+#...
+[0-9a-f]+ <tls_bits_1>:
+   [0-9a-f]+:	27841c90 	addiu	a0,gp,7312
+   [0-9a-f]+:	27841c84 	addiu	a0,gp,7300
+   [0-9a-f]+:	24441c8c 	addiu	a0,v0,7308
+   [0-9a-f]+:	00000000 	nop
+
+[0-9a-f]+ <sym_2_0000>:
+#...
+[0-9a-f]+ <tls_bits_2>:
+   [0-9a-f]+:	27841c90 	addiu	a0,gp,7312
+   [0-9a-f]+:	27841c84 	addiu	a0,gp,7300
+   [0-9a-f]+:	24441c8c 	addiu	a0,v0,7308
+   [0-9a-f]+:	00000000 	nop
+#pass
Index: binutils/ld/testsuite/ld-mips-elf/tls-multi-got-1.r
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tls-multi-got-1.r	2005-02-24 13:48:45.030870187 -0500
@@ -0,0 +1,61 @@
+
+Dynamic section at offset 0xec contains 19 entries:
+  Tag        Type                         Name/Value
+ 0x00000004 \(HASH\)                       0x1ac
+ 0x00000005 \(STRTAB\)                     0x71e08
+ 0x00000006 \(SYMTAB\)                     0x23ae8
+ 0x0000000a \(STRSZ\)                      220100 \(bytes\)
+ 0x0000000b \(SYMENT\)                     16 \(bytes\)
+ 0x00000015 \(DEBUG\)                      0x0
+ 0x00000003 \(PLTGOT\)                     0x122480
+ 0x00000011 \(REL\)                        0xa79cc
+ 0x00000012 \(RELSZ\)                      160072 \(bytes\)
+ 0x00000013 \(RELENT\)                     8 \(bytes\)
+ 0x70000001 \(MIPS_RLD_VERSION\)           1
+ 0x70000005 \(MIPS_FLAGS\)                 NOTPOT
+ 0x70000006 \(MIPS_BASE_ADDRESS\)          0
+ 0x7000000a \(MIPS_LOCAL_GOTNO\)           13
+ 0x70000011 \(MIPS_SYMTABNO\)              20018
+ 0x70000012 \(MIPS_UNREFEXTNO\)            15
+ 0x70000013 \(MIPS_GOTSYM\)                0x12
+ 0x0000001e \(FLAGS\)                      STATIC_TLS
+ 0x00000000 \(NULL\)                       0x0
+
+Relocation section '\.rel\.dyn' at offset 0x[0-9a-f]+ contains 20031 entries:
+ Offset     Info    Type            Sym.Value  Sym. Name
+00000000  00000000 R_MIPS_NONE      
+00149630  00000026 R_MIPS_TLS_DTPMOD
+0013f9a8  00000026 R_MIPS_TLS_DTPMOD
+0014963c  00000a26 R_MIPS_TLS_DTPMOD 00000000   tlsvar_gd
+00149640  00000a27 R_MIPS_TLS_DTPREL 00000000   tlsvar_gd
+0013f9b4  00000a26 R_MIPS_TLS_DTPMOD 00000000   tlsvar_gd
+0013f9b8  00000a27 R_MIPS_TLS_DTPREL 00000000   tlsvar_gd
+00149638  0000102f R_MIPS_TLS_TPREL3 00000004   tlsvar_ie
+0013f9b0  0000102f R_MIPS_TLS_TPREL3 00000004   tlsvar_ie
+0013602c  00001203 R_MIPS_REL32      000e0aac   sym_2_8355
+0014250c  00001303 R_MIPS_REL32      000d35f4   sym_1_4745
+#...
+00136a10  004e3003 R_MIPS_REL32      000da990   sym_2_2140
+00142d4c  004e3103 R_MIPS_REL32      000cfa94   sym_1_0945
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
+00000000  00000000 R_MIPS_NONE      
Index: binutils/ld/testsuite/ld-mips-elf/tls-multi-got-1-2.s
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tls-multi-got-1-2.s	2005-02-24 13:48:45.030870187 -0500
@@ -0,0 +1,20 @@
+.macro	one_sym	count
+.globl	sym_2_\count
+sym_2_\count:
+	la	$2, sym_2_\count
+.endm
+
+.irp	thou,0,1,2,3,4,5,6,7,8,9
+.irp	hund,0,1,2,3,4,5,6,7,8,9
+.irp	tens,0,1,2,3,4,5,6,7,8,9
+.irp	ones,0,1,2,3,4,5,6,7,8,9
+one_sym	\thou\hund\tens\ones
+.endr
+.endr
+.endr
+.endr
+
+tls_bits_2:
+	addiu	$4,$28,%tlsgd(tlsvar_gd)
+	addiu	$4,$28,%tlsldm(tlsvar_ld)
+	addiu	$4,$2,%gottprel(tlsvar_ie)
Index: binutils/gas/testsuite/gas/mips/tls-ill.l
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/gas/testsuite/gas/mips/tls-ill.l	2005-02-24 13:48:45.030870187 -0500
@@ -0,0 +1,11 @@
+.*: Assembler messages:
+.*:6: Error: bad expression
+.*:6: Error: illegal operands `addiu'
+.*:7: Error: bad expression
+.*:7: Error: illegal operands `addiu'
+.*:8: Error: bad expression
+.*:8: Error: missing '\)'
+.*:8: Error: illegal operands `addiu'
+.*:9: Error: bad expression
+.*:9: Error: missing '\)'
+.*:9: Error: illegal operands `addiu'
Index: binutils/gas/testsuite/gas/mips/tls-ill.s
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/gas/testsuite/gas/mips/tls-ill.s	2005-02-24 13:48:45.030870187 -0500
@@ -0,0 +1,9 @@
+	.abicalls
+	.text
+
+	/* These have obvious meanings, but we don't have currently defined
+	   relocations for them.  */
+	addiu	$4,$28,%dtprel(tlsvar)
+	addiu	$4,$28,%tprel(tlsvar)
+	addiu	$4,$28,%lo(%gottprel(tlsvar))
+	addiu	$4,$28,%hi(%gottprel(tlsvar))
Index: binutils/ld/testsuite/ld-mips-elf/tlslib-o32-ver.got
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlslib-o32-ver.got	2005-02-24 13:48:45.031869944 -0500
@@ -0,0 +1,17 @@
+
+.*:     file format elf32-tradbigmips
+
+DYNAMIC RELOCATION RECORDS
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_NONE       \*ABS\*
+000405e4 R_MIPS_TLS_DTPMOD32  \*ABS\*
+000405ec R_MIPS_TLS_DTPMOD32  tlsvar_gd
+000405f0 R_MIPS_TLS_DTPREL32  tlsvar_gd
+000405e0 R_MIPS_TLS_TPREL32  tlsvar_ie
+
+
+Contents of section .got:
+ 405c0 00000000 80000000 00000000 00000000  ................
+ 405d0 00000000 00000000 00000000 00000590  ................
+ 405e0 00000000 00000000 00000000 00000000  ................
+ 405f0 00000000                             ....            
Index: binutils/ld/testsuite/ld-mips-elf/tlslib.ver
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlslib.ver	2005-02-24 13:48:45.031869944 -0500
@@ -0,0 +1,3 @@
+VER_1 {
+  global: *;
+};
Index: binutils/ld/testsuite/ld-mips-elf/tlslib-o32-hidden.got
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlslib-o32-hidden.got	2005-02-24 13:48:45.031869944 -0500
@@ -0,0 +1,16 @@
+
+.*:     file format elf32-tradbigmips
+
+DYNAMIC RELOCATION RECORDS
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_NONE       \*ABS\*
+0004043c R_MIPS_TLS_DTPMOD32  \*ABS\*
+00040434 R_MIPS_TLS_DTPMOD32  \*ABS\*
+00040430 R_MIPS_TLS_TPREL32  \*ABS\*
+
+
+Contents of section .got:
+ 40410 00000000 80000000 00000000 00000000  ................
+ 40420 00000000 00000000 00000000 000003e0  ................
+ 40430 00000008 00000000 00000000 00000000  ................
+ 40440 ffff8004                             ....            
Index: binutils/ld/testsuite/ld-mips-elf/tlslib-hidden.ver
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlslib-hidden.ver	2005-02-24 13:48:45.031869944 -0500
@@ -0,0 +1,3 @@
+VER_1 {
+  local: *;
+};
Index: binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32-1.d
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32-1.d	2005-02-24 13:48:45.031869944 -0500
@@ -0,0 +1,104 @@
+
+.*:     file format elf32-tradbigmips
+
+Disassembly of section .text:
+
+.* <__start>:
+  .*:	3c1c0fc0 	lui	gp,0xfc0
+  .*:	279c7b60 	addiu	gp,gp,31584
+  .*:	0399e021 	addu	gp,gp,t9
+  .*:	27bdfff0 	addiu	sp,sp,-16
+  .*:	afbe0008 	sw	s8,8\(sp\)
+  .*:	03a0f021 	move	s8,sp
+  .*:	afbc0000 	sw	gp,0\(sp\)
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848044 	addiu	a0,gp,-32700
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848038 	addiu	a0,gp,-32712
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848030 	addiu	a0,gp,-32720
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00401021 	move	v0,v0
+  .*:	3c030000 	lui	v1,0x0
+  .*:	24638000 	addiu	v1,v1,-32768
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	7c02283b 	rdhwr	v0,\$5
+  .*:	8f83804c 	lw	v1,-32692\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	8f838040 	lw	v1,-32704\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	7c02283b 	rdhwr	v0,\$5
+  .*:	3c030000 	lui	v1,0x0
+  .*:	24639004 	addiu	v1,v1,-28668
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	03c0e821 	move	sp,s8
+  .*:	8fbe0008 	lw	s8,8\(sp\)
+  .*:	03e00008 	jr	ra
+  .*:	27bd0010 	addiu	sp,sp,16
+
+.* <__tls_get_addr>:
+  .*:	03e00008 	jr	ra
+  .*:	00000000 	nop
+	...
+
+.* <other>:
+  .*:	3c1c0fc0 	lui	gp,0xfc0
+  .*:	279c7aa0 	addiu	gp,gp,31392
+  .*:	0399e021 	addu	gp,gp,t9
+  .*:	27bdfff0 	addiu	sp,sp,-16
+  .*:	afbe0008 	sw	s8,8\(sp\)
+  .*:	03a0f021 	move	s8,sp
+  .*:	afbc0000 	sw	gp,0\(sp\)
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848044 	addiu	a0,gp,-32700
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848038 	addiu	a0,gp,-32712
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848030 	addiu	a0,gp,-32720
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00401021 	move	v0,v0
+  .*:	3c030000 	lui	v1,0x0
+  .*:	24638000 	addiu	v1,v1,-32768
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	7c02283b 	rdhwr	v0,\$5
+  .*:	8f83804c 	lw	v1,-32692\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	8f838040 	lw	v1,-32704\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	7c02283b 	rdhwr	v0,\$5
+  .*:	3c030000 	lui	v1,0x0
+  .*:	24639004 	addiu	v1,v1,-28668
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	03c0e821 	move	sp,s8
+  .*:	8fbe0008 	lw	s8,8\(sp\)
+  .*:	03e00008 	jr	ra
+  .*:	27bd0010 	addiu	sp,sp,16
+  .*:	00000000 	nop
+Disassembly of section .MIPS.stubs:
+
+.* <.MIPS.stubs>:
+	...
Index: binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32-3.got
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32-3.got	2005-02-24 13:48:45.032869700 -0500
@@ -0,0 +1,20 @@
+
+.*:     file format elf32-tradbigmips
+
+DYNAMIC RELOCATION RECORDS
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_NONE       \*ABS\*
+10000054 R_MIPS_TLS_DTPMOD32  tlsbin_gd
+10000058 R_MIPS_TLS_DTPREL32  tlsbin_gd
+10000048 R_MIPS_TLS_DTPMOD32  tlsvar_gd
+1000004c R_MIPS_TLS_DTPREL32  tlsvar_gd
+10000050 R_MIPS_TLS_TPREL32  tlsvar_ie
+1000005c R_MIPS_TLS_TPREL32  tlsbin_ie
+
+
+Contents of section .got:
+ 10000020 00000000 80000000 00000000 00000000  ................
+ 10000030 00000000 00000000 00000000 0040060c  .............@..
+ 10000040 00000001 00000000 00000000 00000000  ................
+ 10000050 00000000 00000000 00000000 00000000  ................
+ 10000060 00000000 00000000 00000000           ............    
Index: binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32-2.d
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32-2.d	2005-02-24 13:48:45.032869700 -0500
@@ -0,0 +1,104 @@
+
+.*:     file format elf32-tradbigmips
+
+Disassembly of section .text:
+
+.* <__start>:
+  .*:	3c1c0fc0 	lui	gp,0xfc0
+  .*:	279c7b60 	addiu	gp,gp,31584
+  .*:	0399e021 	addu	gp,gp,t9
+  .*:	27bdfff0 	addiu	sp,sp,-16
+  .*:	afbe0008 	sw	s8,8\(sp\)
+  .*:	03a0f021 	move	s8,sp
+  .*:	afbc0000 	sw	gp,0\(sp\)
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848044 	addiu	a0,gp,-32700
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848038 	addiu	a0,gp,-32712
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848030 	addiu	a0,gp,-32720
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00401021 	move	v0,v0
+  .*:	3c030000 	lui	v1,0x0
+  .*:	24638000 	addiu	v1,v1,-32768
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	7c02283b 	rdhwr	v0,\$5
+  .*:	8f83804c 	lw	v1,-32692\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	8f838040 	lw	v1,-32704\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	7c02283b 	rdhwr	v0,\$5
+  .*:	3c030000 	lui	v1,0x0
+  .*:	24639004 	addiu	v1,v1,-28668
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	03c0e821 	move	sp,s8
+  .*:	8fbe0008 	lw	s8,8\(sp\)
+  .*:	03e00008 	jr	ra
+  .*:	27bd0010 	addiu	sp,sp,16
+
+.* <__tls_get_addr>:
+  .*:	03e00008 	jr	ra
+  .*:	00000000 	nop
+	...
+
+.* <other>:
+  .*:	3c1c0fc0 	lui	gp,0xfc0
+  .*:	279c7aa0 	addiu	gp,gp,31392
+  .*:	0399e021 	addu	gp,gp,t9
+  .*:	27bdfff0 	addiu	sp,sp,-16
+  .*:	afbe0008 	sw	s8,8\(sp\)
+  .*:	03a0f021 	move	s8,sp
+  .*:	afbc0000 	sw	gp,0\(sp\)
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848044 	addiu	a0,gp,-32700
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848038 	addiu	a0,gp,-32712
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848030 	addiu	a0,gp,-32720
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00401021 	move	v0,v0
+  .*:	3c030000 	lui	v1,0x0
+  .*:	24638000 	addiu	v1,v1,-32768
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	7c02283b 	rdhwr	v0,\$5
+  .*:	8f83804c 	lw	v1,-32692\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	8f838040 	lw	v1,-32704\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	7c02283b 	rdhwr	v0,\$5
+  .*:	3c030000 	lui	v1,0x0
+  .*:	24639004 	addiu	v1,v1,-28668
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	03c0e821 	move	sp,s8
+  .*:	8fbe0008 	lw	s8,8\(sp\)
+  .*:	03e00008 	jr	ra
+  .*:	27bd0010 	addiu	sp,sp,16
+  .*:	00000000 	nop
+Disassembly of section .MIPS.stubs:
+
+.* <.MIPS.stubs>:
+	...
Index: binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32-2.s
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32-2.s	2005-02-24 13:48:45.032869700 -0500
@@ -0,0 +1,64 @@
+	.file	1 "tlsbin-o32.s"
+	.abicalls
+	.text
+	.align	2
+	.globl	other
+	.ent	other
+	.type	other,@function
+other:
+	.frame	$fp,16,$31
+	.mask	0x40000000,-8
+	.fmask	0x00000000,0
+	.set	noreorder
+	.cpload $25
+	.set	reorder
+	addiu	$sp,$sp,-16
+	sw	$fp,8($sp)
+	move	$fp,$sp
+	.cprestore	0
+
+	# General Dynamic
+	lw	$25,%call16(__tls_get_addr)($28)
+	addiu	$4,$28,%tlsgd(tlsbin_gd)
+	jal	$25
+
+	lw	$25,%call16(__tls_get_addr)($28)
+	addiu	$4,$28,%tlsgd(tlsvar_gd)
+	jal	$25
+
+	# Local Dynamic
+	lw	$25,%call16(__tls_get_addr)($28)
+	addiu	$4,$28,%tlsldm(tlsbin_ld)
+	jal	$25
+
+	move	$2,$2		# Arbitrary instructions
+
+	lui	$3,%dtprel_hi(tlsbin_ld)
+	addiu	$3,$3,%dtprel_lo(tlsbin_ld)
+	addu	$3,$3,$2
+
+	# Initial Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lw	$3,%gottprel(tlsbin_ie)($28)
+	addu	$3,$3,$2
+
+	lw	$3,%gottprel(tlsvar_ie)($28)
+	addu	$3,$3,$2
+
+	# Local Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lui	$3,%tprel_hi(tlsbin_le)
+	addiu	$3,$3,%tprel_lo(tlsbin_le)
+	addu	$3,$3,$2
+
+	move	$sp,$fp
+	lw	$fp,8($sp)
+	addiu	$sp,$sp,16
+	j	$31
+	.end	other
Index: binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32-3.d
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32-3.d	2005-02-24 13:48:45.032869700 -0500
@@ -0,0 +1,104 @@
+
+.*:     file format elf32-tradbigmips
+
+Disassembly of section .text:
+
+.* <other>:
+  .*:	3c1c0fc0 	lui	gp,0xfc0
+  .*:	279c7b60 	addiu	gp,gp,31584
+  .*:	0399e021 	addu	gp,gp,t9
+  .*:	27bdfff0 	addiu	sp,sp,-16
+  .*:	afbe0008 	sw	s8,8\(sp\)
+  .*:	03a0f021 	move	s8,sp
+  .*:	afbc0000 	sw	gp,0\(sp\)
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848044 	addiu	a0,gp,-32700
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848038 	addiu	a0,gp,-32712
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848030 	addiu	a0,gp,-32720
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00401021 	move	v0,v0
+  .*:	3c030000 	lui	v1,0x0
+  .*:	24638000 	addiu	v1,v1,-32768
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	7c02283b 	rdhwr	v0,\$5
+  .*:	8f83804c 	lw	v1,-32692\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	8f838040 	lw	v1,-32704\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	7c02283b 	rdhwr	v0,\$5
+  .*:	3c030000 	lui	v1,0x0
+  .*:	24639004 	addiu	v1,v1,-28668
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	03c0e821 	move	sp,s8
+  .*:	8fbe0008 	lw	s8,8\(sp\)
+  .*:	03e00008 	jr	ra
+  .*:	27bd0010 	addiu	sp,sp,16
+  .*:	00000000 	nop
+
+.* <__start>:
+  .*:	3c1c0fc0 	lui	gp,0xfc0
+  .*:	279c7ab0 	addiu	gp,gp,31408
+  .*:	0399e021 	addu	gp,gp,t9
+  .*:	27bdfff0 	addiu	sp,sp,-16
+  .*:	afbe0008 	sw	s8,8\(sp\)
+  .*:	03a0f021 	move	s8,sp
+  .*:	afbc0000 	sw	gp,0\(sp\)
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848044 	addiu	a0,gp,-32700
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848038 	addiu	a0,gp,-32712
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	8f99802c 	lw	t9,-32724\(gp\)
+  .*:	27848030 	addiu	a0,gp,-32720
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00401021 	move	v0,v0
+  .*:	3c030000 	lui	v1,0x0
+  .*:	24638000 	addiu	v1,v1,-32768
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	7c02283b 	rdhwr	v0,\$5
+  .*:	8f83804c 	lw	v1,-32692\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	8f838040 	lw	v1,-32704\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	7c02283b 	rdhwr	v0,\$5
+  .*:	3c030000 	lui	v1,0x0
+  .*:	24639004 	addiu	v1,v1,-28668
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	03c0e821 	move	sp,s8
+  .*:	8fbe0008 	lw	s8,8\(sp\)
+  .*:	03e00008 	jr	ra
+  .*:	27bd0010 	addiu	sp,sp,16
+
+.* <__tls_get_addr>:
+  .*:	03e00008 	jr	ra
+  .*:	00000000 	nop
+	...
+Disassembly of section .MIPS.stubs:
+
+.* <.MIPS.stubs>:
+	...
Index: binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32-1.got
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32-1.got	2005-02-24 13:48:45.033869456 -0500
@@ -0,0 +1,19 @@
+
+.*:     file format elf32-tradbigmips
+
+DYNAMIC RELOCATION RECORDS
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_NONE       \*ABS\*
+10000054 R_MIPS_TLS_DTPMOD32  tlsbin_gd
+10000058 R_MIPS_TLS_DTPREL32  tlsbin_gd
+10000048 R_MIPS_TLS_DTPMOD32  tlsvar_gd
+1000004c R_MIPS_TLS_DTPREL32  tlsvar_gd
+10000050 R_MIPS_TLS_TPREL32  tlsvar_ie
+1000005c R_MIPS_TLS_TPREL32  tlsbin_ie
+
+
+Contents of section .got:
+ 10000020 00000000 80000000 00000000 00000000  ................
+ 10000030 00000000 00000000 00000000 0040055c  .............@..
+ 10000040 00000001 00000000 00000000 00000000  ................
+ 10000050 00000000 00000000 00000000 00000000  ................
Index: binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32-2.got
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ binutils/ld/testsuite/ld-mips-elf/tlsdyn-o32-2.got	2005-02-24 13:48:45.033869456 -0500
@@ -0,0 +1,20 @@
+
+.*:     file format elf32-tradbigmips
+
+DYNAMIC RELOCATION RECORDS
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_NONE       \*ABS\*
+10000054 R_MIPS_TLS_DTPMOD32  tlsbin_gd
+10000058 R_MIPS_TLS_DTPREL32  tlsbin_gd
+10000048 R_MIPS_TLS_DTPMOD32  tlsvar_gd
+1000004c R_MIPS_TLS_DTPREL32  tlsvar_gd
+10000050 R_MIPS_TLS_TPREL32  tlsvar_ie
+1000005c R_MIPS_TLS_TPREL32  tlsbin_ie
+
+
+Contents of section .got:
+ 10000020 00000000 80000000 00000000 00000000  ................
+ 10000030 00000000 00000000 00000000 0040055c  .............@..
+ 10000040 00000001 00000000 00000000 00000000  ................
+ 10000050 00000000 00000000 00000000 00000000  ................
+ 10000060 00000000 00000000 00000000           ............    



More information about the Binutils mailing list