TLS support for MIPS

Daniel Jacobowitz drow@false.org
Tue Feb 8 09:30:00 GMT 2005


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?

-- 
Daniel Jacobowitz
CodeSourcery, LLC

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

	* elflink.c: Don't exclude sections based on a shared library input.

	* 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.
	(IS_TLS_RELOC): Define.
	(TP_OFFSET, DTP_OFFSET): Define.
	(dtpoff_base, tpoff_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_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-07  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-07  Daniel Jacobowitz  <dan@codesourcery.com>

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

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

	* config/tc-mips.c (percent_op): Add %tlsgd, %tlsldm, %dtpoff,
	%tpoff, and %gottpoff.
	(parse_relocation): Check for a word break after a relocation
	operator.
	(my_getSmallExpression): Handle TLS relocations.
	(md_apply_fix3): Handle TLS relocations, and mark thread-local
	symbols.

2005-02-07  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-01 16:59:39.000000000 -0500
+++ binutils/bfd/elfxx-mips.c	2005-02-07 16:21:35.253461706 -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 IS_TLS_RELOC(r_type) \
+  (r_type == R_MIPS_TLS_DTPMOD32		\
+   || r_type == R_MIPS_TLS_DTPMOD64		\
+   || r_type == R_MIPS_TLS_DTPOFF32		\
+   || r_type == R_MIPS_TLS_DTPOFF64		\
+   || r_type == R_MIPS_TLS_GD			\
+   || r_type == R_MIPS_TLS_LDM			\
+   || r_type == R_MIPS_TLS_LDO_HI16		\
+   || r_type == R_MIPS_TLS_LDO_LO16		\
+   || r_type == R_MIPS_TLS_TPOFF		\
+   || r_type == R_MIPS_TLS_TPOFF32		\
+   || r_type == R_MIPS_TLS_TPOFF64		\
+   || r_type == R_MIPS_TLS_TPOFF_HI16		\
+   || r_type == R_MIPS_TLS_TPOFF_LO16)
+
 /* Structure used to pass information to mips_elf_output_extsym.  */
 
 struct extsym_info
@@ -401,9 +463,10 @@ static asection *mips_elf_got_section
 static struct mips_got_info *mips_elf_got_info
   (bfd *, asection **);
 static bfd_vma mips_elf_local_got_index
-  (bfd *, bfd *, struct bfd_link_info *, bfd_vma);
+  (bfd *, bfd *, struct bfd_link_info *, bfd_vma, unsigned long,
+   struct mips_elf_link_hash_entry *, int);
 static bfd_vma mips_elf_global_got_index
-  (bfd *, bfd *, struct elf_link_hash_entry *);
+  (bfd *, bfd *, struct elf_link_hash_entry *, int, struct bfd_link_info *);
 static bfd_vma mips_elf_got_page
   (bfd *, bfd *, struct bfd_link_info *, bfd_vma, bfd_vma *);
 static bfd_vma mips_elf_got16_entry
@@ -411,16 +474,17 @@ static bfd_vma mips_elf_got16_entry
 static bfd_vma mips_elf_got_offset_from_index
   (bfd *, bfd *, bfd *, bfd_vma);
 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
   (struct bfd_link_info *, unsigned long);
 static bfd_boolean mips_elf_sort_hash_table_f
   (struct mips_elf_link_hash_entry *, void *);
 static bfd_boolean mips_elf_record_local_got_symbol
-  (bfd *, long, bfd_vma, struct mips_got_info *);
+  (bfd *, long, bfd_vma, struct mips_got_info *, unsigned char);
 static bfd_boolean mips_elf_record_global_got_symbol
   (struct elf_link_hash_entry *, bfd *, struct bfd_link_info *,
-   struct mips_got_info *);
+   struct mips_got_info *, unsigned char);
 static const Elf_Internal_Rela *mips_elf_next_relocation
   (bfd *, unsigned int, const Elf_Internal_Rela *, const Elf_Internal_Rela *);
 static bfd_boolean mips_elf_local_relocation_p
@@ -700,6 +764,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
+dtpoff_base (struct bfd_link_info *info)
+{
+  /* If tls_sec is NULL, we should have signalled an error already.  */
+  if (elf_hash_table (info)->tls_sec == NULL)
+    return 0;
+  return elf_hash_table (info)->tls_sec->vma + DTP_OFFSET;
+}
+
+static bfd_vma
+tpoff_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 *
@@ -735,6 +823,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;
@@ -1693,6 +1782,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)
@@ -1705,6 +1795,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
@@ -1725,8 +1819,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);
 }
 
@@ -1736,6 +1832,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
@@ -1804,13 +1908,356 @@ 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;
+}
+
+/* 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;
+  Elf_Internal_Rela rel[3];
+  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)
+	{
+	  memset (rel, 0, sizeof (rel));
+	  if (ABI_64_P (abfd))
+	    rel[0].r_info = ELF_R_INFO (abfd, (unsigned long) indx,
+					R_MIPS_TLS_DTPMOD64);
+	  else
+	    rel[0].r_info = ELF_R_INFO (abfd, (unsigned long) indx,
+					R_MIPS_TLS_DTPMOD32);
+	  rel[0].r_offset = rel[1].r_offset = rel[2].r_offset
+	    = sgot->output_offset + sgot->output_section->vma + offset;
+	  rel[1].r_info = ELF_R_INFO (abfd, 0, R_MIPS_NONE);
+	  rel[2].r_info = ELF_R_INFO (abfd, 0, R_MIPS_NONE);
+	  if (ABI_64_P (abfd))
+	    {
+	      (*get_elf_backend_data (abfd)->s->swap_reloc_out)
+		(abfd, &rel[0],
+		 (sreloc->contents
+		  + sreloc->reloc_count * sizeof (Elf64_Mips_External_Rel)));
+	    }
+	  else
+	    bfd_elf32_swap_reloc_out
+	      (abfd, &rel[0],
+	       (sreloc->contents
+		+ sreloc->reloc_count * sizeof (Elf32_External_Rel)));
+	  ++sreloc->reloc_count;
+
+	  if (indx)
+	    {
+	      memset (rel, 0, sizeof (rel));
+	      if (ABI_64_P (abfd))
+		rel[0].r_info = ELF_R_INFO (abfd, (unsigned long) indx,
+					    R_MIPS_TLS_DTPOFF64);
+	      else
+		rel[0].r_info = ELF_R_INFO (abfd, (unsigned long) indx,
+					    R_MIPS_TLS_DTPOFF32);
+	      rel[0].r_offset = rel[1].r_offset = rel[2].r_offset
+		= sgot->output_offset + sgot->output_section->vma + offset2;
+	      rel[1].r_info = ELF_R_INFO (abfd, 0, R_MIPS_NONE);
+	      rel[2].r_info = ELF_R_INFO (abfd, 0, R_MIPS_NONE);
+	      if (ABI_64_P (abfd))
+		{
+		  (*get_elf_backend_data (abfd)->s->swap_reloc_out)
+		    (abfd, &rel[0],
+		     (sreloc->contents
+		      + sreloc->reloc_count * sizeof (Elf64_Mips_External_Rel)));
+		}
+	      else
+		bfd_elf32_swap_reloc_out
+		  (abfd, &rel[0],
+		   (sreloc->contents
+		    + sreloc->reloc_count * sizeof (Elf32_External_Rel)));
+	      ++sreloc->reloc_count;
+	    }
+	  else
+	    MIPS_ELF_PUT_WORD (abfd, value - dtpoff_base (info),
+			       sgot->contents + offset2);
+	}
+      else
+	{
+	  MIPS_ELF_PUT_WORD (abfd, 1,
+			     sgot->contents + offset);
+	  MIPS_ELF_PUT_WORD (abfd, value - dtpoff_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);
+
+	  memset (rel, 0, sizeof (rel));
+	  if (ABI_64_P (abfd))
+	    rel[0].r_info = ELF_R_INFO (abfd, (unsigned long) indx,
+					R_MIPS_TLS_TPOFF64);
+	  else
+	    rel[0].r_info = ELF_R_INFO (abfd, (unsigned long) indx,
+					R_MIPS_TLS_TPOFF32);
+	  rel[0].r_offset = rel[1].r_offset = rel[2].r_offset
+	    = sgot->output_offset + sgot->output_section->vma + offset;
+	  rel[1].r_info = ELF_R_INFO (abfd, 0, R_MIPS_NONE);
+	  rel[2].r_info = ELF_R_INFO (abfd, 0, R_MIPS_NONE);
+	  if (ABI_64_P (abfd))
+	    {
+	      (*get_elf_backend_data (abfd)->s->swap_reloc_out)
+		(abfd, &rel[0],
+		 (sreloc->contents
+		  + sreloc->reloc_count * sizeof (Elf64_Mips_External_Rel)));
+	    }
+	  else
+	    bfd_elf32_swap_reloc_out
+	      (abfd, &rel[0],
+	       (sreloc->contents
+		+ sreloc->reloc_count * sizeof (Elf32_External_Rel)));
+	  ++sreloc->reloc_count;
+	}
+      else
+	{
+	  MIPS_ELF_PUT_WORD (abfd, value - tpoff_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
+	{
+	  sreloc = mips_elf_rel_dyn_section (dynobj, FALSE);
+	  memset (rel, 0, sizeof (rel));
+	  if (ABI_64_P (abfd))
+	    rel[0].r_info = ELF_R_INFO (abfd, (unsigned long) 0,
+					R_MIPS_TLS_DTPMOD64);
+	  else
+	    rel[0].r_info = ELF_R_INFO (abfd, (unsigned long) 0,
+					R_MIPS_TLS_DTPMOD32);
+	  rel[0].r_offset = rel[1].r_offset = rel[2].r_offset
+	    = sgot->output_offset + sgot->output_section->vma + got_offset;
+	  rel[1].r_info = ELF_R_INFO (abfd, 0, R_MIPS_NONE);
+	  rel[2].r_info = ELF_R_INFO (abfd, 0, R_MIPS_NONE);
+	  if (ABI_64_P (abfd))
+	    {
+	      (*get_elf_backend_data (abfd)->s->swap_reloc_out)
+		(abfd, &rel[0],
+		 (sreloc->contents
+		  + sreloc->reloc_count * sizeof (Elf64_Mips_External_Rel)));
+	    }
+	  else
+	    bfd_elf32_swap_reloc_out
+	      (abfd, &rel[0],
+	       (sreloc->contents
+		+ sreloc->reloc_count * sizeof (Elf32_External_Rel)));
+	  ++sreloc->reloc_count;
+	}
+    }
+
+  *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_TPOFF || 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_TPOFF)
+    {
+      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;
@@ -1818,17 +2265,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 (IS_TLS_RELOC (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;
@@ -1843,29 +2296,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 || IS_TLS_RELOC (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 (IS_TLS_RELOC (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 (IS_TLS_RELOC (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;
@@ -1890,7 +2378,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;
@@ -1925,7 +2414,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
@@ -1951,12 +2441,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;
@@ -1964,6 +2458,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)
@@ -1972,12 +2467,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 (IS_TLS_RELOC (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);
 
@@ -2073,6 +2600,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++;
@@ -2081,6 +2610,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;
     }
@@ -2095,7 +2626,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;
 
@@ -2117,6 +2649,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);
@@ -2124,7 +2657,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);
 
@@ -2132,6 +2668,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)
@@ -2140,7 +2678,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;
 }
@@ -2150,20 +2689,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);
 
@@ -2263,6 +2834,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)
@@ -2282,7 +2856,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;
@@ -2305,11 +2886,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;
@@ -2317,12 +2913,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;
 
@@ -2339,17 +2936,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;
 
@@ -2363,9 +2962,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
@@ -2375,12 +2975,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.
@@ -2402,8 +3051,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)
 	{
@@ -2518,7 +3173,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
@@ -2545,7 +3201,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;
@@ -2558,6 +3213,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
@@ -2566,7 +3225,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 *)
@@ -2577,7 +3236,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);
@@ -2677,6 +3339,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;
 
@@ -2687,7 +3350,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.  */
@@ -2704,7 +3372,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;
 }
@@ -2923,10 +3592,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)
@@ -3222,8 +3893,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_TPOFF:
+    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
@@ -3232,11 +3913,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.
@@ -3253,7 +3936,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;
 	}
@@ -3359,6 +4043,24 @@ mips_elf_calculate_relocation (bfd *abfd
       value &= howto->dst_mask;
       break;
 
+    case R_MIPS_TLS_LDO_HI16:
+      value = (mips_elf_high (addend + symbol - dtpoff_base (info))
+	       & howto->dst_mask);
+      break;
+
+    case R_MIPS_TLS_LDO_LO16:
+      value = (symbol + addend - dtpoff_base (info)) & howto->dst_mask;
+      break;
+
+    case R_MIPS_TLS_TPOFF_HI16:
+      value = (mips_elf_high (addend + symbol - tpoff_base (info))
+	       & howto->dst_mask);
+      break;
+
+    case R_MIPS_TLS_TPOFF_LO16:
+      value = (symbol + addend - tpoff_base (info)) & howto->dst_mask;
+      break;
+
     case R_MIPS_HI16:
       if (!gp_disp_p)
 	{
@@ -3451,6 +4153,9 @@ mips_elf_calculate_relocation (bfd *abfd
 
       /* Fall through.  */
 
+    case R_MIPS_TLS_GD:
+    case R_MIPS_TLS_TPOFF:
+    case R_MIPS_TLS_LDM:
     case R_MIPS_GOT_DISP:
     got_disp:
       value = g;
@@ -5346,6 +6051,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))
@@ -5379,7 +6086,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;
 	}
 
@@ -5401,7 +6108,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
@@ -5438,11 +6145,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_TPOFF:
+	  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:
@@ -5495,7 +6243,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;
 		}
 	    }
@@ -5861,6 +6609,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");
@@ -5929,9 +6678,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;
 }
@@ -6046,6 +6816,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)
 		{
@@ -6071,13 +6844,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)
 	{
@@ -6709,11 +7497,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;
@@ -6724,6 +7512,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)
 	{
@@ -7036,7 +7825,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));
@@ -7650,6 +8440,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
@@ -7668,7 +8463,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;
@@ -7686,6 +8481,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	2004-12-09 01:32:41.000000000 -0500
+++ binutils/bfd/elfn32-mips.c	2005-02-07 16:21:54.871598869 -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_DTPOFF32,	/* 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_DTPOFF32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  EMPTY_HOWTO (R_MIPS_TLS_DTPMOD64),
+  EMPTY_HOWTO (R_MIPS_TLS_DTPOFF64),
+
+  /* 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_LDO_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_LDO_HI16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_LDO_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_LDO_LO16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF,	/* 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_TPOFF",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS IE dynamic relocations.  */
+  HOWTO (R_MIPS_TLS_TPOFF32,	/* 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_TPOFF32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  EMPTY_HOWTO (R_MIPS_TLS_TPOFF64),
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF_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_TPOFF_HI16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF_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_TPOFF_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_DTPOFF32,	/* 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_DTPOFF32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  EMPTY_HOWTO (R_MIPS_TLS_DTPMOD64),
+  EMPTY_HOWTO (R_MIPS_TLS_DTPOFF64),
+
+  /* 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_LDO_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_LDO_HI16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_LDO_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_LDO_LO16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF,	/* 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_TPOFF",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS IE dynamic relocations.  */
+  HOWTO (R_MIPS_TLS_TPOFF32,	/* 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_TPOFF32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  EMPTY_HOWTO (R_MIPS_TLS_TPOFF64),
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF_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_TPOFF_HI16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF_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_TPOFF_LO16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
 };
 
 /* The reloc used for the mips16 jump instruction.  */
@@ -1614,7 +1922,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_DTPOFF32, R_MIPS_TLS_DTPOFF32 },
+  { BFD_RELOC_MIPS_TLS_DTPMOD64, R_MIPS_TLS_DTPMOD64 },
+  { BFD_RELOC_MIPS_TLS_DTPOFF64, R_MIPS_TLS_DTPOFF64 },
+  { BFD_RELOC_MIPS_TLS_GD, R_MIPS_TLS_GD },
+  { BFD_RELOC_MIPS_TLS_LDM, R_MIPS_TLS_LDM },
+  { BFD_RELOC_MIPS_TLS_LDO_HI16, R_MIPS_TLS_LDO_HI16 },
+  { BFD_RELOC_MIPS_TLS_LDO_LO16, R_MIPS_TLS_LDO_LO16 },
+  { BFD_RELOC_MIPS_TLS_TPOFF, R_MIPS_TLS_TPOFF },
+  { BFD_RELOC_MIPS_TLS_TPOFF32, R_MIPS_TLS_TPOFF32 },
+  { BFD_RELOC_MIPS_TLS_TPOFF64, R_MIPS_TLS_TPOFF64 },
+  { BFD_RELOC_MIPS_TLS_TPOFF_HI16, R_MIPS_TLS_TPOFF_HI16 },
+  { BFD_RELOC_MIPS_TLS_TPOFF_LO16, R_MIPS_TLS_TPOFF_LO16 }
 };
 
 /* Given a BFD reloc type, return a howto structure.  */
Index: binutils/bfd/elf32-mips.c
===================================================================
--- binutils.orig/bfd/elf32-mips.c	2004-06-29 09:46:30.000000000 -0400
+++ binutils/bfd/elf32-mips.c	2005-02-07 16:22:06.120815728 -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_DTPOFF32,	/* 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_DTPOFF32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  EMPTY_HOWTO (R_MIPS_TLS_DTPMOD64),
+  EMPTY_HOWTO (R_MIPS_TLS_DTPOFF64),
+
+  /* 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_LDO_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_LDO_HI16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_LDO_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_LDO_LO16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF,	/* 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_TPOFF",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS IE dynamic relocations.  */
+  HOWTO (R_MIPS_TLS_TPOFF32,	/* 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_TPOFF32",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  EMPTY_HOWTO (R_MIPS_TLS_TPOFF64),
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF_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_TPOFF_HI16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF_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_TPOFF_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
@@ -1046,7 +1200,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_DTPOFF32, R_MIPS_TLS_DTPOFF32 },
+  { BFD_RELOC_MIPS_TLS_DTPMOD64, R_MIPS_TLS_DTPMOD64 },
+  { BFD_RELOC_MIPS_TLS_DTPOFF64, R_MIPS_TLS_DTPOFF64 },
+  { BFD_RELOC_MIPS_TLS_GD, R_MIPS_TLS_GD },
+  { BFD_RELOC_MIPS_TLS_LDM, R_MIPS_TLS_LDM },
+  { BFD_RELOC_MIPS_TLS_LDO_HI16, R_MIPS_TLS_LDO_HI16 },
+  { BFD_RELOC_MIPS_TLS_LDO_LO16, R_MIPS_TLS_LDO_LO16 },
+  { BFD_RELOC_MIPS_TLS_TPOFF, R_MIPS_TLS_TPOFF },
+  { BFD_RELOC_MIPS_TLS_TPOFF32, R_MIPS_TLS_TPOFF32 },
+  { BFD_RELOC_MIPS_TLS_TPOFF64, R_MIPS_TLS_TPOFF64 },
+  { BFD_RELOC_MIPS_TLS_TPOFF_HI16, R_MIPS_TLS_TPOFF_HI16 },
+  { BFD_RELOC_MIPS_TLS_TPOFF_LO16, R_MIPS_TLS_TPOFF_LO16 }
 };
 
 /* Given a BFD reloc type, return a howto structure.  */
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-07 16:21:35.255461210 -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,%hi(%dtpoff(tlsvar_ld))
+	addiu	$3,$3,%lo(%dtpoff(tlsvar_ld))
+	addu	$3,$3,$2
+
+	# Initial Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lw	$3,%gottpoff(tlsvar_ie)($28)
+	addu	$3,$3,$2
+
+	# Local Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lui	$3,%hi(%tpoff(tlsvar_le))
+	addiu	$3,$3,%lo(%tpoff(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-07 16:21:35.256460962 -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-01-28 10:21:21.000000000 -0500
+++ binutils/bfd/libbfd.h	2005-02-07 16:21:35.257460714 -0500
@@ -924,6 +924,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_DTPOFF32",
+  "BFD_RELOC_MIPS_TLS_DTPMOD64",
+  "BFD_RELOC_MIPS_TLS_DTPOFF64",
+  "BFD_RELOC_MIPS_TLS_GD",
+  "BFD_RELOC_MIPS_TLS_LDM",
+  "BFD_RELOC_MIPS_TLS_LDO_HI16",
+  "BFD_RELOC_MIPS_TLS_LDO_LO16",
+  "BFD_RELOC_MIPS_TLS_TPOFF",
+  "BFD_RELOC_MIPS_TLS_TPOFF32",
+  "BFD_RELOC_MIPS_TLS_TPOFF64",
+  "BFD_RELOC_MIPS_TLS_TPOFF_HI16",
+  "BFD_RELOC_MIPS_TLS_TPOFF_LO16",
 
   "BFD_RELOC_FRV_LABEL16",
   "BFD_RELOC_FRV_LABEL24",
Index: binutils/bfd/elf64-mips.c
===================================================================
--- binutils.orig/bfd/elf64-mips.c	2005-02-01 16:59:32.000000000 -0500
+++ binutils/bfd/elf64-mips.c	2005-02-07 16:23:39.084950987 -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_DTPOFF32),
+
+  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_DTPOFF64,	/* 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_DTPOFF64",	/* 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_LDO_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_LDO_HI16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_LDO_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_LDO_LO16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF,	/* 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_TPOFF",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS IE dynamic relocations.  */
+  EMPTY_HOWTO (R_MIPS_TLS_TPOFF32),
+
+  HOWTO (R_MIPS_TLS_TPOFF64,	/* 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_TPOFF64",	/* name */
+	 TRUE,			/* partial_inplace */
+	 MINUS_ONE,		/* src_mask */
+	 MINUS_ONE,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF_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_TPOFF_HI16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF_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_TPOFF_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_DTPOFF32),
+  EMPTY_HOWTO (R_MIPS_TLS_DTPMOD64),
+  EMPTY_HOWTO (R_MIPS_TLS_DTPOFF64),
+
+  /* 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_LDO_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_LDO_HI16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS local dynamic offset.  */
+  HOWTO (R_MIPS_TLS_LDO_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_LDO_LO16",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF,	/* 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_TPOFF",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  EMPTY_HOWTO (R_MIPS_TLS_TPOFF32),
+  EMPTY_HOWTO (R_MIPS_TLS_TPOFF64),
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF_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_TPOFF_HI16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* TLS thread pointer offset.  */
+  HOWTO (R_MIPS_TLS_TPOFF_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_TPOFF_LO16", /* name */
+	 TRUE,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
 };
 
 /* The reloc used for the mips16 jump instruction.  */
@@ -1827,7 +2095,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_DTPOFF32, R_MIPS_TLS_DTPOFF32 },
+  { BFD_RELOC_MIPS_TLS_DTPMOD64, R_MIPS_TLS_DTPMOD64 },
+  { BFD_RELOC_MIPS_TLS_DTPOFF64, R_MIPS_TLS_DTPOFF64 },
+  { BFD_RELOC_MIPS_TLS_GD, R_MIPS_TLS_GD },
+  { BFD_RELOC_MIPS_TLS_LDM, R_MIPS_TLS_LDM },
+  { BFD_RELOC_MIPS_TLS_LDO_HI16, R_MIPS_TLS_LDO_HI16 },
+  { BFD_RELOC_MIPS_TLS_LDO_LO16, R_MIPS_TLS_LDO_LO16 },
+  { BFD_RELOC_MIPS_TLS_TPOFF, R_MIPS_TLS_TPOFF },
+  { BFD_RELOC_MIPS_TLS_TPOFF32, R_MIPS_TLS_TPOFF32 },
+  { BFD_RELOC_MIPS_TLS_TPOFF64, R_MIPS_TLS_TPOFF64 },
+  { BFD_RELOC_MIPS_TLS_TPOFF_HI16, R_MIPS_TLS_TPOFF_HI16 },
+  { BFD_RELOC_MIPS_TLS_TPOFF_LO16, R_MIPS_TLS_TPOFF_LO16 }
 };
 
 /* Given a BFD reloc type, return a howto structure.  */
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-07 16:21:35.259460217 -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_DTPOFF32  tlsvar_gd
+00040530 R_MIPS_TLS_TPOFF32  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-07 16:21:35.259460217 -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-07 16:21:35.259460217 -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,%hi(%dtpoff(tlsvar_ld))
+	addiu	$3,$3,%lo(%dtpoff(tlsvar_ld))
+	addu	$3,$3,$2
+
+	# Initial Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lw	$3,%gottpoff(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-07 16:21:35.225468653 -0500
+++ binutils/ld/testsuite/ld-mips-elf/mips-elf.exp	2005-02-07 16:24:01.379500645 -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
@@ -94,3 +94,67 @@ run_dump_test "jaloverflow-2"
 if {$has_newabi} {
     run_dump_test "jalbal"
 }
+
+# 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-07 16:21:35.260459969 -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_LDO_HI16	tlsvar_ld
+  50:	24630000 	addiu	v1,v1,0
+			50: R_MIPS_TLS_LDO_LO16	tlsvar_ld
+  54:	00621821 	addu	v1,v1,v0
+  58:	7c02283b 	0x7c02283b
+  5c:	8f830000 	lw	v1,0\(gp\)
+			5c: R_MIPS_TLS_TPOFF	tlsvar_ie
+  60:	00000000 	nop
+  64:	00621821 	addu	v1,v1,v0
+  68:	7c02283b 	0x7c02283b
+  6c:	3c030000 	lui	v1,0x0
+			6c: R_MIPS_TLS_TPOFF_HI16	tlsvar_le
+  70:	34630000 	ori	v1,v1,0x0
+			70: R_MIPS_TLS_TPOFF_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-07 16:21:35.260459969 -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,%hi(%dtpoff(tlsvar_ld))
+	addiu	$3,$3,%lo(%dtpoff(tlsvar_ld))
+	addu	$3,$3,$2
+
+	# Initial Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lw	$3,%gottpoff(tlsvar_ie)($28)
+	addu	$3,$3,$2
+
+	# Local Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lui	$3,%hi(%tpoff(tlsvar_le))
+	ori	$3,$3,%lo(%tpoff(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-01 16:59:44.000000000 -0500
+++ binutils/bfd/reloc.c	2005-02-07 16:24:09.004639231 -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.
 
@@ -2109,6 +2109,32 @@ ENUMX
   BFD_RELOC_MIPS_RELGOT
 ENUMX
   BFD_RELOC_MIPS_JALR
+ENUMX
+  BFD_RELOC_MIPS_TLS_DTPMOD32
+ENUMX
+  BFD_RELOC_MIPS_TLS_DTPOFF32
+ENUMX
+  BFD_RELOC_MIPS_TLS_DTPMOD64
+ENUMX
+  BFD_RELOC_MIPS_TLS_DTPOFF64
+ENUMX
+  BFD_RELOC_MIPS_TLS_GD
+ENUMX
+  BFD_RELOC_MIPS_TLS_LDM
+ENUMX
+  BFD_RELOC_MIPS_TLS_LDO_HI16
+ENUMX
+  BFD_RELOC_MIPS_TLS_LDO_LO16
+ENUMX
+  BFD_RELOC_MIPS_TLS_TPOFF
+ENUMX
+  BFD_RELOC_MIPS_TLS_TPOFF32
+ENUMX
+  BFD_RELOC_MIPS_TLS_TPOFF64
+ENUMX
+  BFD_RELOC_MIPS_TLS_TPOFF_HI16
+ENUMX
+  BFD_RELOC_MIPS_TLS_TPOFF_LO16
 ENUMDOC
   MIPS ELF relocations.
 COMMENT
Index: binutils/bfd/bfd-in2.h
===================================================================
--- binutils.orig/bfd/bfd-in2.h	2005-02-01 16:59:11.000000000 -0500
+++ binutils/bfd/bfd-in2.h	2005-02-07 16:21:35.263459225 -0500
@@ -2383,6 +2383,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_DTPOFF32,
+  BFD_RELOC_MIPS_TLS_DTPMOD64,
+  BFD_RELOC_MIPS_TLS_DTPOFF64,
+  BFD_RELOC_MIPS_TLS_GD,
+  BFD_RELOC_MIPS_TLS_LDM,
+  BFD_RELOC_MIPS_TLS_LDO_HI16,
+  BFD_RELOC_MIPS_TLS_LDO_LO16,
+  BFD_RELOC_MIPS_TLS_TPOFF,
+  BFD_RELOC_MIPS_TLS_TPOFF32,
+  BFD_RELOC_MIPS_TLS_TPOFF64,
+  BFD_RELOC_MIPS_TLS_TPOFF_HI16,
+  BFD_RELOC_MIPS_TLS_TPOFF_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-07 16:21:35.264458977 -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	2004-12-10 14:48:42.000000000 -0500
+++ binutils/gas/testsuite/gas/mips/mips.exp	2005-02-07 16:21:35.264458977 -0500
@@ -733,4 +733,9 @@ if { [istarget mips*-*-*] } then {
 	run_dump_test "macro-warn-1-n32"
 	run_dump_test "macro-warn-2-n32"
     }
+
+    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-07 16:21:35.264458977 -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-07 16:21:35.265458729 -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-07 16:21:35.265458729 -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_DTPOFF32  tlsbin_gd
+10000058 R_MIPS_TLS_DTPMOD32  tlsvar_gd
+1000005c R_MIPS_TLS_DTPOFF32  tlsvar_gd
+10000054 R_MIPS_TLS_TPOFF32  tlsbin_ie
+10000050 R_MIPS_TLS_TPOFF32  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-07 16:21:35.265458729 -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	2004-12-09 01:02:45.000000000 -0500
+++ binutils/include/elf/mips.h	2005-02-07 16:25:12.916088506 -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)
-  RELOC_NUMBER (R_MIPS_max, 38)
+  /* TLS relocs <http://www.linux-mips.org/wiki/index.php/NPTL>.  */
+  RELOC_NUMBER (R_MIPS_TLS_DTPMOD32, 38)
+  RELOC_NUMBER (R_MIPS_TLS_DTPOFF32, 39)
+  RELOC_NUMBER (R_MIPS_TLS_DTPMOD64, 40)
+  RELOC_NUMBER (R_MIPS_TLS_DTPOFF64, 41)
+  RELOC_NUMBER (R_MIPS_TLS_GD, 42)
+  RELOC_NUMBER (R_MIPS_TLS_LDM, 43)
+  RELOC_NUMBER (R_MIPS_TLS_LDO_HI16, 44)
+  RELOC_NUMBER (R_MIPS_TLS_LDO_LO16, 45)
+  RELOC_NUMBER (R_MIPS_TLS_TPOFF, 46)
+  RELOC_NUMBER (R_MIPS_TLS_TPOFF32, 47)
+  RELOC_NUMBER (R_MIPS_TLS_TPOFF64, 48)
+  RELOC_NUMBER (R_MIPS_TLS_TPOFF_HI16, 49)
+  RELOC_NUMBER (R_MIPS_TLS_TPOFF_LO16, 50)
+  RELOC_NUMBER (R_MIPS_max, 51)
   /* These relocs are used for the mips16.  */
   RELOC_NUMBER (R_MIPS16_26, 100)
   RELOC_NUMBER (R_MIPS16_GPREL, 101)
Index: binutils/gas/config/tc-mips.c
===================================================================
--- binutils.orig/gas/config/tc-mips.c	2005-02-01 17:00:04.000000000 -0500
+++ binutils/gas/config/tc-mips.c	2005-02-07 16:25:18.556720211 -0500
@@ -1,6 +1,6 @@
 /* tc-mips.c -- assemble code for a MIPS chip.
    Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-   2003, 2004 Free Software Foundation, Inc.
+   2003, 2004, 2005 Free Software Foundation, Inc.
    Contributed by the OSF and Ralph Campbell.
    Written by Keith Knowles and Ralph Campbell, working independently.
    Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus
@@ -9819,6 +9819,13 @@ static const struct percent_op_match
   {"%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},
+  /* These must always be used with %lo or %hi (for now).  */
+  {"%dtpoff", BFD_RELOC_MIPS_TLS_LDO_LO16},
+  {"%tpoff", BFD_RELOC_MIPS_TLS_TPOFF_LO16},
+  /* This one must be used without %lo or %hi (for now).  */
+  {"%gottpoff", BFD_RELOC_MIPS_TLS_TPOFF},
 #endif
   {"%hi", BFD_RELOC_HI16_S}
 };
@@ -9836,6 +9843,11 @@ parse_relocation (char **str, bfd_reloc_
   for (i = 0; i < ARRAY_SIZE (percent_op); 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;
 
@@ -9887,9 +9899,61 @@ my_getSmallExpression (expressionS *ep, 
 	  str_depth++;
     }
   while (*str == '%'
-	 && reloc_index < (HAVE_NEWABI ? 3 : 1)
+	 && reloc_index < 3
 	 && parse_relocation (&str, &reversed_reloc[reloc_index]));
 
+  /* %dtpoff may only be used immediately inside %hi or %lo, with no
+     other relocations.  However, use inside %hi and %lo is valid even
+     with the old ABI which permits only a single relocation.
+     Similarly, %tpoff may be used inside %hi and %lo even with the
+     old ABI, but may also be used on its own.  Convert these uses to
+     a single relocation and give an error for other uses.  */
+  for (i = 0; i < reloc_index; i++)
+    {
+      if (reversed_reloc[i] == BFD_RELOC_MIPS_TLS_LDO_LO16)
+	{
+	  if (i != 1
+	      || reloc_index != 2
+	      || (reversed_reloc[i - 1] != BFD_RELOC_LO16
+		  && reversed_reloc[i - 1] != BFD_RELOC_HI16_S))
+	    {
+	      as_bad (_("bad use of %%dtpoff"));
+	      reloc_index = 0;
+	      break;
+	    }
+	  i--;
+	  reloc_index--;
+	  if (reversed_reloc[i] == BFD_RELOC_LO16)
+	    reversed_reloc[i] = BFD_RELOC_MIPS_TLS_LDO_LO16;
+	  else
+	    reversed_reloc[i] = BFD_RELOC_MIPS_TLS_LDO_HI16;
+	}
+      if (reversed_reloc[i] == BFD_RELOC_MIPS_TLS_TPOFF_LO16)
+	{
+	  if (i != 1
+	      || reloc_index != 2
+	      || (reversed_reloc[i - 1] != BFD_RELOC_LO16
+		  && reversed_reloc[i - 1] != BFD_RELOC_HI16_S))
+	    {
+	      as_bad (_("bad use of %%tpoff"));
+	      reloc_index = 0;
+	      break;
+	    }
+	  i--;
+	  reloc_index--;
+	  if (reversed_reloc[i] == BFD_RELOC_LO16)
+	    reversed_reloc[i] = BFD_RELOC_MIPS_TLS_TPOFF_LO16;
+	  else
+	    reversed_reloc[i] = BFD_RELOC_MIPS_TLS_TPOFF_HI16;
+	}
+    }
+
+  if (reloc_index > 1 && !HAVE_NEWABI)
+    {
+      as_bad (_("multiple relocations not supported by current ABI"));
+      reloc_index = 0;
+    }
+
   my_getExpression (ep, crux);
   str = expr_end;
 
@@ -10972,6 +11036,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_LDO_HI16:
+    case BFD_RELOC_MIPS_TLS_LDO_LO16:
+    case BFD_RELOC_MIPS_TLS_TPOFF:
+    case BFD_RELOC_MIPS_TLS_TPOFF_HI16:
+    case BFD_RELOC_MIPS_TLS_TPOFF_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-07 16:21:35.272456992 -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,%hi(%dtpoff(tlsbin_ld))
+	addiu	$3,$3,%lo(%dtpoff(tlsbin_ld))
+	addu	$3,$3,$2
+
+	# Initial Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lw	$3,%gottpoff(tlsbin_ie)($28)
+	addu	$3,$3,$2
+
+	lw	$3,%gottpoff(tlsvar_ie)($28)
+	addu	$3,$3,$2
+
+	# Local Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lui	$3,%hi(%tpoff(tlsbin_le))
+	addiu	$3,$3,%lo(%tpoff(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-07 16:21:35.273456744 -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_DTPOFF32  tlsvar_gd
+0013f9b4 R_MIPS_TLS_DTPMOD32  tlsvar_gd
+0013f9b8 R_MIPS_TLS_DTPOFF32  tlsvar_gd
+00149638 R_MIPS_TLS_TPOFF32  tlsvar_ie
+0013f9b0 R_MIPS_TLS_TPOFF32  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-07 16:21:35.273456744 -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,%gottpoff(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-07 16:21:35.273456744 -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-07 16:21:35.273456744 -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_DTPOFF 00000000   tlsvar_gd
+0013f9b4  00000a26 R_MIPS_TLS_DTPMOD 00000000   tlsvar_gd
+0013f9b8  00000a27 R_MIPS_TLS_DTPOFF 00000000   tlsvar_gd
+00149638  0000102f R_MIPS_TLS_TPOFF3 00000004   tlsvar_ie
+0013f9b0  0000102f R_MIPS_TLS_TPOFF3 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-07 16:21:35.274456496 -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,%gottpoff(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-07 16:21:35.274456496 -0500
@@ -0,0 +1,5 @@
+.*: Assembler messages:
+.*:6: Error: bad use of %dtpoff
+.*:7: Error: bad use of %tpoff
+.*:8: Error: multiple relocations not supported by current ABI
+.*:9: Error: multiple relocations not supported by current ABI
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-07 16:21:35.274456496 -0500
@@ -0,0 +1,9 @@
+	.abicalls
+	.text
+
+	/* These have obvious meanings, but we don't have currently defined
+	   relocations for them.  */
+	addiu	$4,$28,%dtpoff(tlsvar)
+	addiu	$4,$28,%tpoff(tlsvar)
+	addiu	$4,$28,%lo(%gottpoff(tlsvar))
+	addiu	$4,$28,%hi(%gottpoff(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-07 16:21:35.274456496 -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_DTPOFF32  tlsvar_gd
+000405e0 R_MIPS_TLS_TPOFF32  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-07 16:21:35.274456496 -0500
@@ -0,0 +1,3 @@
+VER_1 {
+  global: *;
+};
Index: binutils/bfd/elflink.c
===================================================================
--- binutils.orig/bfd/elflink.c	2005-02-01 16:59:37.000000000 -0500
+++ binutils/bfd/elflink.c	2005-02-07 16:21:35.278455503 -0500
@@ -9461,6 +9461,10 @@ _bfd_elf_section_already_linked (bfd *ab
   if ((flags & SEC_LINK_ONCE) == 0 && group == NULL)
     return;
 
+  /* Don't exclude sections based on what we see in shared libraries.  */
+  if (sec->owner->flags & DYNAMIC)
+    return;
+
   if (group)
     {
       /* If this is the member of a single member comdat group, check if
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-07 16:21:35.278455503 -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_TPOFF32  \*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-07 16:21:35.278455503 -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-07 16:21:35.278455503 -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-07 16:21:35.279455255 -0500
@@ -0,0 +1,20 @@
+
+.*:     file format elf32-tradbigmips
+
+DYNAMIC RELOCATION RECORDS
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_NONE       \*ABS\*
+10000060 R_MIPS_TLS_DTPMOD32  tlsbin_gd
+10000064 R_MIPS_TLS_DTPOFF32  tlsbin_gd
+10000054 R_MIPS_TLS_DTPMOD32  tlsvar_gd
+10000058 R_MIPS_TLS_DTPOFF32  tlsvar_gd
+1000005c R_MIPS_TLS_TPOFF32  tlsvar_ie
+10000068 R_MIPS_TLS_TPOFF32  tlsbin_ie
+
+
+Contents of section .got:
+ 10000020 00000000 80000000 00000000 00000000  ................
+ 10000030 00000000 00000000 00000000 0040060c  .............@..
+ 10000040 00000000 00000001 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-07 16:21:35.279455255 -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\)
+  .*:	27848050 	addiu	a0,gp,-32688
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	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\)
+  .*:	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
+  .*:	8f838058 	lw	v1,-32680\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	8f83804c 	lw	v1,-32692\(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\)
+  .*:	27848050 	addiu	a0,gp,-32688
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	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\)
+  .*:	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
+  .*:	8f838058 	lw	v1,-32680\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	8f83804c 	lw	v1,-32692\(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-07 16:21:35.279455255 -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,%hi(%dtpoff(tlsbin_ld))
+	addiu	$3,$3,%lo(%dtpoff(tlsbin_ld))
+	addu	$3,$3,$2
+
+	# Initial Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lw	$3,%gottpoff(tlsbin_ie)($28)
+	addu	$3,$3,$2
+
+	lw	$3,%gottpoff(tlsvar_ie)($28)
+	addu	$3,$3,$2
+
+	# Local Exec
+	.set	push
+	.set	mips32r2
+	rdhwr	$2, $5
+	.set	pop
+	lui	$3,%hi(%tpoff(tlsbin_le))
+	addiu	$3,$3,%lo(%tpoff(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-07 16:21:35.279455255 -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\)
+  .*:	27848050 	addiu	a0,gp,-32688
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	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\)
+  .*:	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
+  .*:	8f838058 	lw	v1,-32680\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	8f83804c 	lw	v1,-32692\(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\)
+  .*:	27848050 	addiu	a0,gp,-32688
+  .*:	0320f809 	jalr	t9
+  .*:	00000000 	nop
+  .*:	8fdc0000 	lw	gp,0\(s8\)
+  .*:	00000000 	nop
+  .*:	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\)
+  .*:	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
+  .*:	8f838058 	lw	v1,-32680\(gp\)
+  .*:	00000000 	nop
+  .*:	00621821 	addu	v1,v1,v0
+  .*:	8f83804c 	lw	v1,-32692\(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-07 16:21:35.279455255 -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_DTPOFF32  tlsbin_gd
+10000048 R_MIPS_TLS_DTPMOD32  tlsvar_gd
+1000004c R_MIPS_TLS_DTPOFF32  tlsvar_gd
+10000050 R_MIPS_TLS_TPOFF32  tlsvar_ie
+1000005c R_MIPS_TLS_TPOFF32  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-07 16:21:35.280455007 -0500
@@ -0,0 +1,20 @@
+
+.*:     file format elf32-tradbigmips
+
+DYNAMIC RELOCATION RECORDS
+OFFSET   TYPE              VALUE 
+00000000 R_MIPS_NONE       \*ABS\*
+10000060 R_MIPS_TLS_DTPMOD32  tlsbin_gd
+10000064 R_MIPS_TLS_DTPOFF32  tlsbin_gd
+10000054 R_MIPS_TLS_DTPMOD32  tlsvar_gd
+10000058 R_MIPS_TLS_DTPOFF32  tlsvar_gd
+1000005c R_MIPS_TLS_TPOFF32  tlsvar_ie
+10000068 R_MIPS_TLS_TPOFF32  tlsbin_ie
+
+
+Contents of section .got:
+ 10000020 00000000 80000000 00000000 00000000  ................
+ 10000030 00000000 00000000 00000000 0040055c  .............@..
+ 10000040 00000000 00000001 00000000 00000000  ................
+ 10000050 00000000 00000000 00000000 00000000  ................
+ 10000060 00000000 00000000 00000000           ............    



More information about the Binutils mailing list