bfd/
[deliverable/binutils-gdb.git] / bfd / elfxx-mips.c
index d96400d15e32bf5c8527b19370508fd346e84b53..470bed9e37ab2f23915b193c21dd28f0529f13b8 100644 (file)
@@ -1,6 +1,6 @@
 /* MIPS-specific support for ELF
    Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-   2003, 2004, 2005 Free Software Foundation, Inc.
+   2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
    Most of the information added by Ian Lance Taylor, Cygnus Support,
    <ian@cygnus.com>.
@@ -13,7 +13,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
 
 /* This file handles functionality common to the different MIPS ABI's.  */
 
-#include "bfd.h"
 #include "sysdep.h"
+#include "bfd.h"
 #include "libbfd.h"
 #include "libiberty.h"
 #include "elf-bfd.h"
 #include "elfxx-mips.h"
 #include "elf/mips.h"
+#include "elf-vxworks.h"
 
 /* Get the ECOFF swapping routines.  */
 #include "coff/sym.h"
 
 #include "hashtab.h"
 
-/* This structure is used to hold .got entries while estimating got
-   sizes.  */
+/* This structure is used to hold information about one GOT entry.
+   There are three types of entry:
+
+      (1) absolute addresses
+           (abfd == NULL)
+      (2) SYMBOL + OFFSET addresses, where SYMBOL is local to an input bfd
+           (abfd != NULL, symndx >= 0)
+      (3) global and forced-local symbols
+           (abfd != NULL, symndx == -1)
+
+   Type (3) entries are treated differently for different types of GOT.
+   In the "master" GOT -- i.e.  the one that describes every GOT
+   reference needed in the link -- the mips_got_entry is keyed on both
+   the symbol and the input bfd that references it.  If it turns out
+   that we need multiple GOTs, we can then use this information to
+   create separate GOTs for each input bfd.
+
+   However, we want each of these separate GOTs to have at most one
+   entry for a given symbol, so their type (3) entries are keyed only
+   on the symbol.  The input bfd given by the "abfd" field is somewhat
+   arbitrary in this case.
+
+   This means that when there are multiple GOTs, each GOT has a unique
+   mips_got_entry for every symbol within it.  We can therefore use the
+   mips_got_entry fields (tls_type and gotidx) to track the symbol's
+   GOT index.
+
+   However, if it turns out that we need only a single GOT, we continue
+   to use the master GOT to describe it.  There may therefore be several
+   mips_got_entries for the same symbol, each with a different input bfd.
+   We want to make sure that each symbol gets a unique GOT entry, so when
+   there's a single GOT, we use the symbol's hash entry, not the
+   mips_got_entry fields, to track a symbol's GOT index.  */
 struct mips_got_entry
 {
   /* The input bfd in which the symbol is defined.  */
@@ -78,6 +112,30 @@ struct mips_got_entry
   long gotidx;
 };
 
+/* This structure describes a range of addends: [MIN_ADDEND, MAX_ADDEND].
+   The structures form a non-overlapping list that is sorted by increasing
+   MIN_ADDEND.  */
+struct mips_got_page_range
+{
+  struct mips_got_page_range *next;
+  bfd_signed_vma min_addend;
+  bfd_signed_vma max_addend;
+};
+
+/* This structure describes the range of addends that are applied to page
+   relocations against a given symbol.  */
+struct mips_got_page_entry
+{
+  /* The input bfd in which the symbol is defined.  */
+  bfd *abfd;
+  /* The index of the symbol, as stored in the relocation r_info.  */
+  long symndx;
+  /* The ranges for this page entry.  */
+  struct mips_got_page_range *ranges;
+  /* The maximum number of page entries needed for RANGES.  */
+  bfd_vma num_pages;
+};
+
 /* This structure is used to hold .got information when linking.  */
 
 struct mips_got_info
@@ -92,12 +150,16 @@ struct mips_got_info
   /* 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.  */
+  /* The number of local .got entries, eventually including page entries.  */
   unsigned int local_gotno;
+  /* The maximum number of page entries needed.  */
+  unsigned int page_gotno;
   /* The number of local .got entries we have used.  */
   unsigned int assigned_gotno;
   /* A hash table holding members of the got.  */
   struct htab *got_entries;
+  /* A hash table of mips_got_page_entry structures.  */
+  struct htab *got_page_entries;
   /* A hash table mapping input bfds to other mips_got_info.  NULL
      unless multi-got was necessary.  */
   struct htab *bfd2got;
@@ -139,10 +201,8 @@ struct mips_elf_got_per_bfd_arg
   /* The maximum number of got entries that can be addressed with a
      16-bit offset.  */
   unsigned int max_count;
-  /* The number of local and global entries in the primary got.  */
-  unsigned int primary_count;
-  /* The number of local and global entries in the current got.  */
-  unsigned int current_count;
+  /* The maximum number of page entries needed by each got.  */
+  unsigned int max_pages;
   /* 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
@@ -246,6 +306,12 @@ struct mips_elf_link_hash_entry
      the initial global GOT entry to a local GOT entry.  */
   bfd_boolean forced_local;
 
+  /* Are we referenced by some kind of relocation?  */
+  bfd_boolean is_relocation_target;
+
+  /* Are we referenced by branch relocations?  */
+  bfd_boolean is_branch_target;
+
 #define GOT_NORMAL     0
 #define GOT_TLS_GD     1
 #define GOT_TLS_LDM    2
@@ -283,6 +349,26 @@ struct mips_elf_link_hash_table
   bfd_vma rld_value;
   /* This is set if we see any mips16 stub sections.  */
   bfd_boolean mips16_stubs_seen;
+  /* True if we've computed the size of the GOT.  */
+  bfd_boolean computed_got_sizes;
+  /* True if we're generating code for VxWorks.  */
+  bfd_boolean is_vxworks;
+  /* True if we already reported the small-data section overflow.  */
+  bfd_boolean small_data_overflow_reported;
+  /* Shortcuts to some dynamic sections, or NULL if they are not
+     being used.  */
+  asection *srelbss;
+  asection *sdynbss;
+  asection *srelplt;
+  asection *srelplt2;
+  asection *sgotplt;
+  asection *splt;
+  /* The size of the PLT header in bytes (VxWorks only).  */
+  bfd_vma plt_header_size;
+  /* The size of a PLT entry in bytes (VxWorks only).  */
+  bfd_vma plt_entry_size;
+  /* The size of a function stub entry in bytes.  */
+  bfd_vma function_stub_size;
 };
 
 #define TLS_RELOC_P(r_type) \
@@ -433,13 +519,13 @@ typedef struct runtime_pdr {
 #define rpdNil ((pRPDR) 0)
 \f
 static struct mips_got_entry *mips_elf_create_local_got_entry
-  (bfd *, bfd *, struct mips_got_info *, asection *, bfd_vma, unsigned long,
-   struct mips_elf_link_hash_entry *, int);
+  (bfd *, struct bfd_link_info *, bfd *, struct mips_got_info *, asection *,
+   bfd_vma, unsigned long, struct mips_elf_link_hash_entry *, int);
 static bfd_boolean mips_elf_sort_hash_table_f
   (struct mips_elf_link_hash_entry *, void *);
 static bfd_vma mips_elf_high
   (bfd_vma);
-static bfd_boolean mips_elf_stub_section_p
+static bfd_boolean mips16_stub_section_p
   (bfd *, asection *);
 static bfd_boolean mips_elf_create_dynamic_relocation
   (bfd *, struct bfd_link_info *, const Elf_Internal_Rela *,
@@ -456,7 +542,6 @@ static struct mips_got_info *mips_elf_got_for_ibfd
 static bfd *reldyn_sorting_bfd;
 
 /* Nonzero if ABFD is using the N32 ABI.  */
-
 #define ABI_N32_P(abfd) \
   ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI2) != 0)
 
@@ -484,6 +569,11 @@ static bfd *reldyn_sorting_bfd;
 #define MIPS_ELF_OPTIONS_SECTION_NAME_P(NAME) \
   (strcmp (NAME, ".MIPS.options") == 0 || strcmp (NAME, ".options") == 0)
 
+/* Whether the section is readonly.  */
+#define MIPS_ELF_READONLY_SECTION(sec) \
+  ((sec->flags & (SEC_ALLOC | SEC_LOAD | SEC_READONLY))                \
+   == (SEC_ALLOC | SEC_LOAD | SEC_READONLY))
+
 /* The name of the stub section.  */
 #define MIPS_ELF_STUB_SECTION_NAME(abfd) ".MIPS.stubs"
 
@@ -491,6 +581,10 @@ static bfd *reldyn_sorting_bfd;
 #define MIPS_ELF_REL_SIZE(abfd) \
   (get_elf_backend_data (abfd)->s->sizeof_rel)
 
+/* The size of an external RELA relocation.  */
+#define MIPS_ELF_RELA_SIZE(abfd) \
+  (get_elf_backend_data (abfd)->s->sizeof_rela)
+
 /* The size of an external dynamic table entry.  */
 #define MIPS_ELF_DYN_SIZE(abfd) \
   (get_elf_backend_data (abfd)->s->sizeof_dyn)
@@ -541,36 +635,47 @@ static bfd *reldyn_sorting_bfd;
        == (ABI_64_P (abfd) ? sizeof (Elf64_External_Rela)              \
           : sizeof (Elf32_External_Rela))))
 
+/* The name of the dynamic relocation section.  */
+#define MIPS_ELF_REL_DYN_NAME(INFO) \
+  (mips_elf_hash_table (INFO)->is_vxworks ? ".rela.dyn" : ".rel.dyn")
+
 /* In case we're on a 32-bit machine, construct a 64-bit "-1" value
    from smaller values.  Start with zero, widen, *then* decrement.  */
 #define MINUS_ONE      (((bfd_vma)0) - 1)
 #define MINUS_TWO      (((bfd_vma)0) - 2)
 
 /* The number of local .got entries we reserve.  */
-#define MIPS_RESERVED_GOTNO (2)
+#define MIPS_RESERVED_GOTNO(INFO) \
+  (mips_elf_hash_table (INFO)->is_vxworks ? 3 : 2)
 
 /* The offset of $gp from the beginning of the .got section.  */
-#define ELF_MIPS_GP_OFFSET(abfd) (0x7ff0)
+#define ELF_MIPS_GP_OFFSET(INFO) \
+  (mips_elf_hash_table (INFO)->is_vxworks ? 0x0 : 0x7ff0)
 
 /* The maximum size of the GOT for it to be addressable using 16-bit
    offsets from $gp.  */
-#define MIPS_ELF_GOT_MAX_SIZE(abfd) (ELF_MIPS_GP_OFFSET(abfd) + 0x7fff)
+#define MIPS_ELF_GOT_MAX_SIZE(INFO) (ELF_MIPS_GP_OFFSET (INFO) + 0x7fff)
 
 /* Instructions which appear in a stub.  */
-#define STUB_LW(abfd)                                          \
-  ((ABI_64_P (abfd)                                            \
-    ? 0xdf998010               /* ld t9,0x8010(gp) */          \
-    : 0x8f998010))              /* lw t9,0x8010(gp) */
-#define STUB_MOVE(abfd)                                         \
-   ((ABI_64_P (abfd)                                           \
-     ? 0x03e0782d              /* daddu t7,ra */               \
-     : 0x03e07821))            /* addu t7,ra */
-#define STUB_JALR 0x0320f809   /* jalr t9,ra */
-#define STUB_LI16(abfd)                                         \
-  ((ABI_64_P (abfd)                                            \
-   ? 0x64180000                        /* daddiu t8,zero,0 */          \
-   : 0x24180000))              /* addiu t8,zero,0 */
-#define MIPS_FUNCTION_STUB_SIZE (16)
+#define STUB_LW(abfd)                                                  \
+  ((ABI_64_P (abfd)                                                    \
+    ? 0xdf998010                               /* ld t9,0x8010(gp) */  \
+    : 0x8f998010))                             /* lw t9,0x8010(gp) */
+#define STUB_MOVE(abfd)                                                        \
+   ((ABI_64_P (abfd)                                                   \
+     ? 0x03e0782d                              /* daddu t7,ra */       \
+     : 0x03e07821))                            /* addu t7,ra */
+#define STUB_LUI(VAL) (0x3c180000 + (VAL))     /* lui t8,VAL */
+#define STUB_JALR 0x0320f809                   /* jalr t9,ra */
+#define STUB_ORI(VAL) (0x37180000 + (VAL))     /* ori t8,t8,VAL */
+#define STUB_LI16U(VAL) (0x34180000 + (VAL))   /* ori t8,zero,VAL unsigned */
+#define STUB_LI16S(abfd, VAL)                                          \
+   ((ABI_64_P (abfd)                                                   \
+    ? (0x64180000 + (VAL))     /* daddiu t8,zero,VAL sign extended */  \
+    : (0x24180000 + (VAL))))   /* addiu t8,zero,VAL sign extended */
+
+#define MIPS_FUNCTION_STUB_NORMAL_SIZE 16
+#define MIPS_FUNCTION_STUB_BIG_SIZE 20
 
 /* The name of the dynamic interpreter.  This is put in the .interp
    section.  */
@@ -636,6 +741,48 @@ static bfd *reldyn_sorting_bfd;
 #define FN_STUB ".mips16.fn."
 #define CALL_STUB ".mips16.call."
 #define CALL_FP_STUB ".mips16.call.fp."
+
+#define FN_STUB_P(name) CONST_STRNEQ (name, FN_STUB)
+#define CALL_STUB_P(name) CONST_STRNEQ (name, CALL_STUB)
+#define CALL_FP_STUB_P(name) CONST_STRNEQ (name, CALL_FP_STUB)
+\f
+/* The format of the first PLT entry in a VxWorks executable.  */
+static const bfd_vma mips_vxworks_exec_plt0_entry[] = {
+  0x3c190000,  /* lui t9, %hi(_GLOBAL_OFFSET_TABLE_)           */
+  0x27390000,  /* addiu t9, t9, %lo(_GLOBAL_OFFSET_TABLE_)     */
+  0x8f390008,  /* lw t9, 8(t9)                                 */
+  0x00000000,  /* nop                                          */
+  0x03200008,  /* jr t9                                        */
+  0x00000000   /* nop                                          */
+};
+
+/* The format of subsequent PLT entries.  */
+static const bfd_vma mips_vxworks_exec_plt_entry[] = {
+  0x10000000,  /* b .PLT_resolver                      */
+  0x24180000,  /* li t8, <pltindex>                    */
+  0x3c190000,  /* lui t9, %hi(<.got.plt slot>)         */
+  0x27390000,  /* addiu t9, t9, %lo(<.got.plt slot>)   */
+  0x8f390000,  /* lw t9, 0(t9)                         */
+  0x00000000,  /* nop                                  */
+  0x03200008,  /* jr t9                                */
+  0x00000000   /* nop                                  */
+};
+
+/* The format of the first PLT entry in a VxWorks shared object.  */
+static const bfd_vma mips_vxworks_shared_plt0_entry[] = {
+  0x8f990008,  /* lw t9, 8(gp)         */
+  0x00000000,  /* nop                  */
+  0x03200008,  /* jr t9                */
+  0x00000000,  /* nop                  */
+  0x00000000,  /* nop                  */
+  0x00000000   /* nop                  */
+};
+
+/* The format of subsequent PLT entries.  */
+static const bfd_vma mips_vxworks_shared_plt_entry[] = {
+  0x10000000,  /* b .PLT_resolver      */
+  0x24180000   /* li t8, <pltindex>    */
+};
 \f
 /* Look up an entry in a MIPS ELF linker hash table.  */
 
@@ -716,6 +863,8 @@ mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
       ret->call_stub = NULL;
       ret->call_fp_stub = NULL;
       ret->forced_local = FALSE;
+      ret->is_branch_target = FALSE;
+      ret->is_relocation_target = FALSE;
       ret->tls_type = GOT_NORMAL;
     }
 
@@ -725,13 +874,16 @@ mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
 bfd_boolean
 _bfd_mips_elf_new_section_hook (bfd *abfd, asection *sec)
 {
-  struct _mips_elf_section_data *sdata;
-  bfd_size_type amt = sizeof (*sdata);
+  if (!sec->used_by_bfd)
+    {
+      struct _mips_elf_section_data *sdata;
+      bfd_size_type amt = sizeof (*sdata);
 
-  sdata = bfd_zalloc (abfd, amt);
-  if (sdata == NULL)
-    return FALSE;
-  sec->used_by_bfd = sdata;
+      sdata = bfd_zalloc (abfd, amt);
+      if (sdata == NULL)
+       return FALSE;
+      sec->used_by_bfd = sdata;
+    }
 
   return _bfd_elf_new_section_hook (abfd, sec);
 }
@@ -1576,11 +1728,20 @@ sort_dynamic_relocs (const void *arg1, const void *arg2)
 {
   Elf_Internal_Rela int_reloc1;
   Elf_Internal_Rela int_reloc2;
+  int diff;
 
   bfd_elf32_swap_reloc_in (reldyn_sorting_bfd, arg1, &int_reloc1);
   bfd_elf32_swap_reloc_in (reldyn_sorting_bfd, arg2, &int_reloc2);
 
-  return ELF32_R_SYM (int_reloc1.r_info) - ELF32_R_SYM (int_reloc2.r_info);
+  diff = ELF32_R_SYM (int_reloc1.r_info) - ELF32_R_SYM (int_reloc2.r_info);
+  if (diff != 0)
+    return diff;
+
+  if (int_reloc1.r_offset < int_reloc2.r_offset)
+    return -1;
+  if (int_reloc1.r_offset > int_reloc2.r_offset)
+    return 1;
+  return 0;
 }
 
 /* Like sort_dynamic_relocs, but used for elf64 relocations.  */
@@ -1598,8 +1759,16 @@ sort_dynamic_relocs_64 (const void *arg1 ATTRIBUTE_UNUSED,
   (*get_elf_backend_data (reldyn_sorting_bfd)->s->swap_reloc_in)
     (reldyn_sorting_bfd, arg2, int_reloc2);
 
-  return (ELF64_R_SYM (int_reloc1[0].r_info)
-         - ELF64_R_SYM (int_reloc2[0].r_info));
+  if (ELF64_R_SYM (int_reloc1[0].r_info) < ELF64_R_SYM (int_reloc2[0].r_info))
+    return -1;
+  if (ELF64_R_SYM (int_reloc1[0].r_info) > ELF64_R_SYM (int_reloc2[0].r_info))
+    return 1;
+
+  if (int_reloc1[0].r_offset < int_reloc2[0].r_offset)
+    return -1;
+  if (int_reloc1[0].r_offset > int_reloc2[0].r_offset)
+    return 1;
+  return 0;
 #else
   abort ();
 #endif
@@ -1896,15 +2065,39 @@ mips_elf_multi_got_entry_eq (const void *entry1, const void *entry2)
        ? e1->abfd == e2->abfd && e1->d.address == e2->d.address
        : e1->d.h == e2->d.h);
 }
+
+static hashval_t
+mips_got_page_entry_hash (const void *entry_)
+{
+  const struct mips_got_page_entry *entry;
+
+  entry = (const struct mips_got_page_entry *) entry_;
+  return entry->abfd->id + entry->symndx;
+}
+
+static int
+mips_got_page_entry_eq (const void *entry1_, const void *entry2_)
+{
+  const struct mips_got_page_entry *entry1, *entry2;
+
+  entry1 = (const struct mips_got_page_entry *) entry1_;
+  entry2 = (const struct mips_got_page_entry *) entry2_;
+  return entry1->abfd == entry2->abfd && entry1->symndx == entry2->symndx;
+}
 \f
-/* Returns the dynamic relocation section for DYNOBJ.  */
+/* Return the dynamic relocation section.  If it doesn't exist, try to
+   create a new it if CREATE_P, otherwise return NULL.  Also return NULL
+   if creation fails.  */
 
 static asection *
-mips_elf_rel_dyn_section (bfd *dynobj, bfd_boolean create_p)
+mips_elf_rel_dyn_section (struct bfd_link_info *info, bfd_boolean create_p)
 {
-  static const char dname[] = ".rel.dyn";
+  const char *dname;
   asection *sreloc;
+  bfd *dynobj;
 
+  dname = MIPS_ELF_REL_DYN_NAME (info);
+  dynobj = elf_hash_table (info)->dynobj;
   sreloc = bfd_get_section_by_name (dynobj, dname);
   if (sreloc == NULL && create_p)
     {
@@ -2122,7 +2315,7 @@ mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset,
              || h->root.root.type == bfd_link_hash_undefweak);
 
   /* Emit necessary relocations.  */
-  sreloc = mips_elf_rel_dyn_section (dynobj, FALSE);
+  sreloc = mips_elf_rel_dyn_section (info, FALSE);
 
   /* General Dynamic.  */
   if (*tls_type_p & GOT_TLS_GD)
@@ -2241,10 +2434,40 @@ mips_tls_got_index (bfd *abfd, bfd_vma got_index, unsigned char *tls_type,
   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.  If
-   R_SYMNDX refers to a TLS symbol, create a TLS GOT entry instead.
-   Returns -1 if no satisfactory GOT offset can be found.  */
+/* Return the offset from _GLOBAL_OFFSET_TABLE_ of the .got.plt entry
+   for global symbol H.  .got.plt comes before the GOT, so the offset
+   will be negative.  */
+
+static bfd_vma
+mips_elf_gotplt_index (struct bfd_link_info *info,
+                      struct elf_link_hash_entry *h)
+{
+  bfd_vma plt_index, got_address, got_value;
+  struct mips_elf_link_hash_table *htab;
+
+  htab = mips_elf_hash_table (info);
+  BFD_ASSERT (h->plt.offset != (bfd_vma) -1);
+
+  /* Calculate the index of the symbol's PLT entry.  */
+  plt_index = (h->plt.offset - htab->plt_header_size) / htab->plt_entry_size;
+
+  /* Calculate the address of the associated .got.plt entry.  */
+  got_address = (htab->sgotplt->output_section->vma
+                + htab->sgotplt->output_offset
+                + plt_index * 4);
+
+  /* Calculate the value of _GLOBAL_OFFSET_TABLE_.  */
+  got_value = (htab->root.hgot->root.u.def.section->output_section->vma
+              + htab->root.hgot->root.u.def.section->output_offset
+              + htab->root.hgot->root.u.def.value);
+
+  return got_address - got_value;
+}
+
+/* Return the GOT offset for address VALUE.   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.  Return -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,
@@ -2257,14 +2480,22 @@ mips_elf_local_got_index (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
 
   g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
 
-  entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value,
-                                          r_symndx, h, r_type);
+  entry = mips_elf_create_local_got_entry (abfd, info, ibfd, g, sgot,
+                                          value, r_symndx, h, r_type);
   if (!entry)
     return MINUS_ONE;
 
   if (TLS_RELOC_P (r_type))
-    return mips_tls_got_index (abfd, entry->gotidx, &entry->tls_type, r_type,
-                              info, h, value);
+    {
+      if (entry->symndx == -1 && g->next == NULL)
+       /* A type (3) entry in the single-GOT case.  We use the symbol's
+          hash table entry to track the index.  */
+       return mips_tls_got_index (abfd, h->tls_got_offset, &h->tls_type,
+                                  r_type, info, h, value);
+      else
+       return mips_tls_got_index (abfd, entry->gotidx, &entry->tls_type,
+                                  r_type, info, h, value);
+    }
   else
     return entry->gotidx;
 }
@@ -2351,11 +2582,11 @@ mips_elf_global_got_index (bfd *abfd, bfd *ibfd, struct elf_link_hash_entry *h,
   return index;
 }
 
-/* Find a GOT entry that is within 32KB of the VALUE.  These entries
-   are supposed to be placed at small offsets in the GOT, i.e.,
-   within 32KB of GP.  Return the index into the GOT for this page,
-   and store the offset from this entry to the desired address in
-   OFFSETP, if it is non-NULL.  */
+/* Find a GOT page entry that points to within 32KB of VALUE.  These
+   entries are supposed to be placed at small offsets in the GOT, i.e.,
+   within 32KB of GP.  Return the index of the GOT entry, or -1 if no
+   entry could be created.  If OFFSETP is nonnull, use it to return the
+   offset of the GOT entry from VALUE.  */
 
 static bfd_vma
 mips_elf_got_page (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
@@ -2363,15 +2594,14 @@ mips_elf_got_page (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
 {
   asection *sgot;
   struct mips_got_info *g;
-  bfd_vma index;
+  bfd_vma page, index;
   struct mips_got_entry *entry;
 
   g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
 
-  entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot,
-                                          (value + 0x8000)
-                                          & (~(bfd_vma)0xffff), 0,
-                                          NULL, R_MIPS_GOT_PAGE);
+  page = (value + 0x8000) & ~(bfd_vma) 0xffff;
+  entry = mips_elf_create_local_got_entry (abfd, info, ibfd, g, sgot,
+                                          page, 0, NULL, R_MIPS_GOT_PAGE);
 
   if (!entry)
     return MINUS_ONE;
@@ -2384,8 +2614,9 @@ mips_elf_got_page (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
   return index;
 }
 
-/* Find a GOT entry whose higher-order 16 bits are the same as those
-   for value.  Return the index into the GOT for this entry.  */
+/* Find a local GOT entry for an R_MIPS_GOT16 relocation against VALUE.
+   EXTERNAL is true if the relocation was against a global symbol
+   that has been forced local.  */
 
 static bfd_vma
 mips_elf_got16_entry (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
@@ -2395,19 +2626,17 @@ mips_elf_got16_entry (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
   struct mips_got_info *g;
   struct mips_got_entry *entry;
 
+  /* GOT16 relocations against local symbols are followed by a LO16
+     relocation; those against global symbols are not.  Thus if the
+     symbol was originally local, the GOT16 relocation should load the
+     equivalent of %hi(VALUE), otherwise it should load VALUE itself.  */
   if (! external)
-    {
-      /* Although the ABI says that it is "the high-order 16 bits" that we
-        want, it is really the %high value.  The complete value is
-        calculated with a `addiu' of a LO16 relocation, just as with a
-        HI16/LO16 pair.  */
-      value = mips_elf_high (value) << 16;
-    }
+    value = mips_elf_high (value) << 16;
 
   g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
 
-  entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value, 0, NULL,
-                                          R_MIPS_GOT16);
+  entry = mips_elf_create_local_got_entry (abfd, info, ibfd, g, sgot,
+                                          value, 0, NULL, R_MIPS_GOT16);
   if (entry)
     return entry->gotidx;
   else
@@ -2432,13 +2661,14 @@ mips_elf_got_offset_from_index (bfd *dynobj, bfd *output_bfd,
   return sgot->output_section->vma + sgot->output_offset + index - gp;
 }
 
-/* Create a local GOT entry for VALUE.  Return the index of the entry,
-   or -1 if it could not be created.  If R_SYMNDX refers to a TLS symbol,
-   create a TLS entry instead.  */
+/* Create and return a local GOT entry for VALUE, which was calculated
+   from a symbol belonging to INPUT_SECTON.  Return NULL 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,
+mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info,
+                                bfd *ibfd, struct mips_got_info *gg,
                                 asection *sgot, bfd_vma value,
                                 unsigned long r_symndx,
                                 struct mips_elf_link_hash_entry *h,
@@ -2446,6 +2676,9 @@ mips_elf_create_local_got_entry (bfd *abfd, bfd *ibfd,
 {
   struct mips_got_entry entry, **loc;
   struct mips_got_info *g;
+  struct mips_elf_link_hash_table *htab;
+
+  htab = mips_elf_hash_table (info);
 
   entry.abfd = NULL;
   entry.symndx = -1;
@@ -2505,7 +2738,7 @@ mips_elf_create_local_got_entry (bfd *abfd, bfd *ibfd,
 
   memcpy (*loc, &entry, sizeof entry);
 
-  if (g->assigned_gotno >= g->local_gotno)
+  if (g->assigned_gotno > g->local_gotno)
     {
       (*loc)->gotidx = -1;
       /* We didn't allocate enough space in the GOT.  */
@@ -2518,6 +2751,26 @@ mips_elf_create_local_got_entry (bfd *abfd, bfd *ibfd,
   MIPS_ELF_PUT_WORD (abfd, value,
                     (sgot->contents + entry.gotidx));
 
+  /* These GOT entries need a dynamic relocation on VxWorks.  */
+  if (htab->is_vxworks)
+    {
+      Elf_Internal_Rela outrel;
+      asection *s;
+      bfd_byte *loc;
+      bfd_vma got_address;
+
+      s = mips_elf_rel_dyn_section (info, FALSE);
+      got_address = (sgot->output_section->vma
+                    + sgot->output_offset
+                    + entry.gotidx);
+
+      loc = s->contents + (s->reloc_count++ * sizeof (Elf32_External_Rela));
+      outrel.r_offset = got_address;
+      outrel.r_info = ELF32_R_INFO (STN_UNDEF, R_MIPS_32);
+      outrel.r_addend = value;
+      bfd_elf32_swap_reloca_out (abfd, &outrel, loc);
+    }
+
   return *loc;
 }
 
@@ -2589,7 +2842,8 @@ mips_elf_sort_hash_table_f (struct mips_elf_link_hash_entry *h, void *data)
   /* Global symbols that need GOT entries that are not explicitly
      referenced are marked with got offset 2.  Those that are
      referenced get a 1, and those that don't need GOT entries get
-     -1.  */
+     -1.  Forced local symbols may also be marked with got offset 1,
+     but are never given global GOT entries.  */
   if (h->root.got.offset == 2)
     {
       BFD_ASSERT (h->tls_type == GOT_NORMAL);
@@ -2598,7 +2852,7 @@ mips_elf_sort_hash_table_f (struct mips_elf_link_hash_entry *h, void *data)
        hsd->low = (struct elf_link_hash_entry *) h;
       h->root.dynindx = hsd->max_unref_got_dynindx++;
     }
-  else if (h->root.got.offset != 1)
+  else if (h->root.got.offset != 1 || h->forced_local)
     h->root.dynindx = hsd->max_non_got_dynindx++;
   else
     {
@@ -2638,6 +2892,9 @@ mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
        return FALSE;
     }
 
+  /* Make sure we have a GOT to put this entry into.  */
+  BFD_ASSERT (g != NULL);
+
   entry.abfd = abfd;
   entry.symndx = -1;
   entry.d.h = (struct mips_elf_link_hash_entry *) h;
@@ -2667,11 +2924,15 @@ mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
   if (h->got.offset != MINUS_ONE)
     return TRUE;
 
-  /* 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.  */
   if (tls_flag == 0)
-    h->got.offset = 1;
+    {
+      /* 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 (h->forced_local)
+       g->local_gotno++;
+    }
 
   return TRUE;
 }
@@ -2737,6 +2998,104 @@ mips_elf_record_local_got_symbol (bfd *abfd, long symndx, bfd_vma addend,
 
   return TRUE;
 }
+
+/* Return the maximum number of GOT page entries required for RANGE.  */
+
+static bfd_vma
+mips_elf_pages_for_range (const struct mips_got_page_range *range)
+{
+  return (range->max_addend - range->min_addend + 0x1ffff) >> 16;
+}
+
+/* Record that ABFD has a page relocation against symbol SYMNDX and that
+   ADDEND is the addend for that relocation.  G is the GOT information.  */
+
+static bfd_boolean
+mips_elf_record_got_page_entry (bfd *abfd, long symndx, bfd_signed_vma addend,
+                               struct mips_got_info *g)
+{
+  struct mips_got_page_entry lookup, *entry;
+  struct mips_got_page_range **range_ptr, *range;
+  bfd_vma old_pages, new_pages;
+  void **loc;
+
+  /* Find the mips_got_page_entry hash table entry for this symbol.  */
+  lookup.abfd = abfd;
+  lookup.symndx = symndx;
+  loc = htab_find_slot (g->got_page_entries, &lookup, INSERT);
+  if (loc == NULL)
+    return FALSE;
+
+  /* Create a mips_got_page_entry if this is the first time we've
+     seen the symbol.  */
+  entry = (struct mips_got_page_entry *) *loc;
+  if (!entry)
+    {
+      entry = bfd_alloc (abfd, sizeof (*entry));
+      if (!entry)
+       return FALSE;
+
+      entry->abfd = abfd;
+      entry->symndx = symndx;
+      entry->ranges = NULL;
+      entry->num_pages = 0;
+      *loc = entry;
+    }
+
+  /* Skip over ranges whose maximum extent cannot share a page entry
+     with ADDEND.  */
+  range_ptr = &entry->ranges;
+  while (*range_ptr && addend > (*range_ptr)->max_addend + 0xffff)
+    range_ptr = &(*range_ptr)->next;
+
+  /* If we scanned to the end of the list, or found a range whose
+     minimum extent cannot share a page entry with ADDEND, create
+     a new singleton range.  */
+  range = *range_ptr;
+  if (!range || addend < range->min_addend - 0xffff)
+    {
+      range = bfd_alloc (abfd, sizeof (*range));
+      if (!range)
+       return FALSE;
+
+      range->next = *range_ptr;
+      range->min_addend = addend;
+      range->max_addend = addend;
+
+      *range_ptr = range;
+      entry->num_pages++;
+      g->page_gotno++;
+      return TRUE;
+    }
+
+  /* Remember how many pages the old range contributed.  */
+  old_pages = mips_elf_pages_for_range (range);
+
+  /* Update the ranges.  */
+  if (addend < range->min_addend)
+    range->min_addend = addend;
+  else if (addend > range->max_addend)
+    {
+      if (range->next && addend >= range->next->min_addend - 0xffff)
+       {
+         old_pages += mips_elf_pages_for_range (range->next);
+         range->max_addend = range->next->max_addend;
+         range->next = range->next->next;
+       }
+      else
+       range->max_addend = addend;
+    }
+
+  /* Record any change in the total estimate.  */
+  new_pages = mips_elf_pages_for_range (range);
+  if (old_pages != new_pages)
+    {
+      entry->num_pages += new_pages - old_pages;
+      g->page_gotno += new_pages - old_pages;
+    }
+
+  return TRUE;
+}
 \f
 /* Compute the hash value of the bfd in a bfd2got hash entry.  */
 
@@ -2762,7 +3121,7 @@ mips_elf_bfd2got_entry_eq (const void *entry1, const void *entry2)
   return e1->bfd == e2->bfd;
 }
 
-/* In a multi-got link, determine the GOT to be used for IBDF.  G must
+/* In a multi-got link, determine the GOT to be used for IBFD.  G must
    be the master GOT data.  */
 
 static struct mips_got_info *
@@ -2778,53 +3137,42 @@ mips_elf_got_for_ibfd (struct mips_got_info *g, bfd *ibfd)
   return p ? p->g : NULL;
 }
 
-/* Create one separate got for each bfd that has entries in the global
-   got, such that we can tell how many local and global entries each
-   bfd requires.  */
+/* Use BFD2GOT to find ABFD's got entry, creating one if none exists.
+   Return NULL if an error occured.  */
 
-static int
-mips_elf_make_got_per_bfd (void **entryp, void *p)
+static struct mips_got_info *
+mips_elf_get_got_for_bfd (struct htab *bfd2got, bfd *output_bfd,
+                         bfd *input_bfd)
 {
-  struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
-  struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
-  htab_t bfd2got = arg->bfd2got;
-  struct mips_got_info *g;
   struct mips_elf_bfd2got_hash bfdgot_entry, *bfdgot;
+  struct mips_got_info *g;
   void **bfdgotp;
 
-  /* Find the got_info for this GOT entry's input bfd.  Create one if
-     none exists.  */
-  bfdgot_entry.bfd = entry->abfd;
+  bfdgot_entry.bfd = input_bfd;
   bfdgotp = htab_find_slot (bfd2got, &bfdgot_entry, INSERT);
-  bfdgot = (struct mips_elf_bfd2got_hash *)*bfdgotp;
+  bfdgot = (struct mips_elf_bfd2got_hash *) *bfdgotp;
 
-  if (bfdgot != NULL)
-    g = bfdgot->g;
-  else
+  if (bfdgot == NULL)
     {
-      bfdgot = (struct mips_elf_bfd2got_hash *)bfd_alloc
-       (arg->obfd, sizeof (struct mips_elf_bfd2got_hash));
-
+      bfdgot = ((struct mips_elf_bfd2got_hash *)
+               bfd_alloc (output_bfd, sizeof (struct mips_elf_bfd2got_hash)));
       if (bfdgot == NULL)
-       {
-         arg->obfd = 0;
-         return 0;
-       }
+       return NULL;
 
       *bfdgotp = bfdgot;
 
-      bfdgot->bfd = entry->abfd;
-      bfdgot->g = g = (struct mips_got_info *)
-       bfd_alloc (arg->obfd, sizeof (struct mips_got_info));
+      g = ((struct mips_got_info *)
+          bfd_alloc (output_bfd, sizeof (struct mips_got_info)));
       if (g == NULL)
-       {
-         arg->obfd = 0;
-         return 0;
-       }
+       return NULL;
+
+      bfdgot->bfd = input_bfd;
+      bfdgot->g = g;
 
       g->global_gotsym = NULL;
       g->global_gotno = 0;
       g->local_gotno = 0;
+      g->page_gotno = 0;
       g->assigned_gotno = -1;
       g->tls_gotno = 0;
       g->tls_assigned_gotno = 0;
@@ -2832,15 +3180,39 @@ mips_elf_make_got_per_bfd (void **entryp, void *p)
       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)
-       {
-         arg->obfd = 0;
-         return 0;
-       }
+       return NULL;
+
+      g->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
+                                            mips_got_page_entry_eq, NULL);
+      if (g->got_page_entries == NULL)
+       return NULL;
 
       g->bfd2got = NULL;
       g->next = NULL;
     }
 
+  return bfdgot->g;
+}
+
+/* A htab_traverse callback for the entries in the master got.
+   Create one separate got for each bfd that has entries in the global
+   got, such that we can tell how many local and global entries each
+   bfd requires.  */
+
+static int
+mips_elf_make_got_per_bfd (void **entryp, void *p)
+{
+  struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
+  struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
+  struct mips_got_info *g;
+
+  g = mips_elf_get_got_for_bfd (arg->bfd2got, arg->obfd, entry->abfd);
+  if (g == NULL)
+    {
+      arg->obfd = NULL;
+      return 0;
+    }
+
   /* Insert the GOT entry in the bfd's got entry hash table.  */
   entryp = htab_find_slot (g->got_entries, entry, INSERT);
   if (*entryp != NULL)
@@ -2863,6 +3235,85 @@ mips_elf_make_got_per_bfd (void **entryp, void *p)
   return 1;
 }
 
+/* A htab_traverse callback for the page entries in the master got.
+   Associate each page entry with the bfd's got.  */
+
+static int
+mips_elf_make_got_pages_per_bfd (void **entryp, void *p)
+{
+  struct mips_got_page_entry *entry = (struct mips_got_page_entry *) *entryp;
+  struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *) p;
+  struct mips_got_info *g;
+
+  g = mips_elf_get_got_for_bfd (arg->bfd2got, arg->obfd, entry->abfd);
+  if (g == NULL)
+    {
+      arg->obfd = NULL;
+      return 0;
+    }
+
+  /* Insert the GOT entry in the bfd's got entry hash table.  */
+  entryp = htab_find_slot (g->got_page_entries, entry, INSERT);
+  if (*entryp != NULL)
+    return 1;
+
+  *entryp = entry;
+  g->page_gotno += entry->num_pages;
+  return 1;
+}
+
+/* Consider merging the got described by BFD2GOT with TO, using the
+   information given by ARG.  Return -1 if this would lead to overflow,
+   1 if they were merged successfully, and 0 if a merge failed due to
+   lack of memory.  (These values are chosen so that nonnegative return
+   values can be returned by a htab_traverse callback.)  */
+
+static int
+mips_elf_merge_got_with (struct mips_elf_bfd2got_hash *bfd2got,
+                        struct mips_got_info *to,
+                        struct mips_elf_got_per_bfd_arg *arg)
+{
+  struct mips_got_info *from = bfd2got->g;
+  unsigned int estimate;
+
+  /* Work out how many page entries we would need for the combined GOT.  */
+  estimate = arg->max_pages;
+  if (estimate >= from->page_gotno + to->page_gotno)
+    estimate = from->page_gotno + to->page_gotno;
+
+  /* And conservatively estimate how many local, global and TLS entries
+     would be needed.  */
+  estimate += (from->local_gotno
+              + from->global_gotno
+              + from->tls_gotno
+              + to->local_gotno
+              + to->global_gotno
+              + to->tls_gotno);
+
+  /* Bail out if the combined GOT might be too big.  */
+  if (estimate > arg->max_count)
+    return -1;
+
+  /* Commit to the merge.  Record that TO is now the bfd for this got.  */
+  bfd2got->g = to;
+
+  /* Transfer the bfd's got information from FROM to TO.  */
+  htab_traverse (from->got_entries, mips_elf_make_got_per_bfd, arg);
+  if (arg->obfd == NULL)
+    return 0;
+
+  htab_traverse (from->got_page_entries, mips_elf_make_got_pages_per_bfd, arg);
+  if (arg->obfd == NULL)
+    return 0;
+
+  /* We don't have to worry about releasing memory of the actual
+     got entries, since they're all in the master got_entries hash
+     table anyway.  */
+  htab_delete (from->got_entries);
+  htab_delete (from->got_page_entries);
+  return 1;
+}
+
 /* Attempt to merge gots of different input bfds.  Try to use as much
    as possible of the primary got, since it doesn't require explicit
    dynamic relocations, but don't use bfds that would reference global
@@ -2876,149 +3327,108 @@ mips_elf_merge_gots (void **bfd2got_, void *p)
   struct mips_elf_bfd2got_hash *bfd2got
     = (struct mips_elf_bfd2got_hash *)*bfd2got_;
   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;
+  struct mips_got_info *g;
+  unsigned int estimate;
+  int result;
+
+  g = bfd2got->g;
+
+  /* Work out the number of page, local and TLS entries.  */
+  estimate = arg->max_pages;
+  if (estimate > g->page_gotno)
+    estimate = g->page_gotno;
+  estimate += g->local_gotno + g->tls_gotno;
 
   /* 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;
-    }
+  estimate += (g->tls_gotno > 0 ? arg->global_count : g->global_gotno);
 
-  /* 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 + tcount <= maxcnt
-      && ! too_many_for_tls)
-    {
-      arg->primary = bfd2got->g;
-      arg->primary_count = lcount + gcount;
-    }
-  /* 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 && ! too_many_for_tls
-          && (arg->primary_count + lcount + gcount + tcount) <= maxcnt)
+  if (estimate <= arg->max_count)
     {
-      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;
-
-      htab_traverse (g->got_entries,
-                    mips_elf_make_got_per_bfd,
-                    arg);
-      if (arg->obfd == NULL)
-       return 0;
-
-      htab_delete (g->got_entries);
-      /* We don't have to worry about releasing memory of the actual
-        got entries, since they're all in the master got_entries hash
-        table anyway.  */
-
-      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);
+      /* If we don't have a primary GOT, use it as
+        a starting point for the primary GOT.  */
+      if (!arg->primary)
+       {
+         arg->primary = bfd2got->g;
+         return 1;
+       }
 
-      arg->primary_count = arg->primary->local_gotno
-       + arg->primary->global_gotno + arg->primary->tls_gotno;
+      /* Try merging with the primary GOT.  */
+      result = mips_elf_merge_got_with (bfd2got, arg->primary, arg);
+      if (result >= 0)
+       return result;
     }
+
   /* If we can merge with the last-created got, do it.  */
-  else if (arg->current
-          && arg->current_count + lcount + gcount + tcount <= maxcnt)
+  if (arg->current)
     {
-      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;
-
-      htab_traverse (g->got_entries,
-                    mips_elf_make_got_per_bfd,
-                    arg);
-      if (arg->obfd == NULL)
-       return 0;
-
-      htab_delete (g->got_entries);
-
-      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->tls_gotno;
+      result = mips_elf_merge_got_with (bfd2got, arg->current, arg);
+      if (result >= 0)
+       return result;
     }
+
   /* 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
      overflows anyway.  */
-  else
-    {
-      bfd2got->g->next = arg->current;
-      arg->current = bfd2got->g;
-
-      arg->current_count = lcount + gcount + 2 * tcount;
-    }
+  g->next = arg->current;
+  arg->current = g;
 
   return 1;
 }
 
-/* Set the TLS GOT index for the GOT entry in ENTRYP.  */
+/* Set the TLS GOT index for the GOT entry in ENTRYP.  ENTRYP's NEXT field
+   is null iff there is just a single GOT.  */
 
 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;
+  bfd_vma next_index;
+  unsigned char tls_type;
 
   /* We're only interested in TLS symbols.  */
   if (entry->tls_type == 0)
     return 1;
 
-  if (entry->symndx == -1)
+  next_index = MIPS_ELF_GOT_SIZE (entry->abfd) * (long) g->tls_assigned_gotno;
+
+  if (entry->symndx == -1 && g->next == NULL)
     {
-      /* 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;
-       }
+      /* A type (3) got entry in the single-GOT case.  We use the symbol's
+        hash table entry to track its index.  */
+      if (entry->d.h->tls_type & GOT_TLS_OFFSET_DONE)
+       return 1;
+      entry->d.h->tls_type |= GOT_TLS_OFFSET_DONE;
+      entry->d.h->tls_got_offset = next_index;
+      tls_type = entry->d.h->tls_type;
     }
-  else if (entry->tls_type & GOT_TLS_LDM)
+  else
     {
-      /* Similarly, there may be multiple structs for the LDM entry.  */
-      if (g->tls_ldm_offset != MINUS_TWO && g->tls_ldm_offset != MINUS_ONE)
+      if (entry->tls_type & GOT_TLS_LDM)
        {
-         entry->gotidx = g->tls_ldm_offset;
-         return 1;
+         /* There are separate mips_got_entry objects for each input bfd
+            that requires an LDM entry.  Make sure that all LDM entries in
+            a GOT resolve to the same index.  */
+         if (g->tls_ldm_offset != MINUS_TWO && g->tls_ldm_offset != MINUS_ONE)
+           {
+             entry->gotidx = g->tls_ldm_offset;
+             return 1;
+           }
+         g->tls_ldm_offset = next_index;
        }
+      entry->gotidx = next_index;
+      tls_type = entry->tls_type;
     }
 
-  /* 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))
+  /* Account for the entries we've just allocated.  */
+  if (tls_type & (GOT_TLS_GD | GOT_TLS_LDM))
     g->tls_assigned_gotno += 2;
-  if (entry->tls_type & GOT_TLS_IE)
+  if (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;
 }
 
@@ -3050,6 +3460,7 @@ mips_elf_set_global_got_offset (void **entryp, void *p)
 
   if (entry->abfd != NULL && entry->symndx == -1
       && entry->d.h->root.dynindx != -1
+      && !entry->d.h->forced_local
       && entry->d.h->tls_type == GOT_NORMAL)
     {
       if (g)
@@ -3197,14 +3608,18 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
   if (got_per_bfd_arg.obfd == NULL)
     return FALSE;
 
+  /* Also count how many page entries each input bfd requires.  */
+  htab_traverse (g->got_page_entries, mips_elf_make_got_pages_per_bfd,
+                &got_per_bfd_arg);
+  if (got_per_bfd_arg.obfd == NULL)
+    return FALSE;
+
   got_per_bfd_arg.current = NULL;
   got_per_bfd_arg.primary = NULL;
-  /* Taking out PAGES entries is a worst-case estimate.  We could
-     compute the maximum number of pages that each separate input bfd
-     uses, but it's probably not worth it.  */
-  got_per_bfd_arg.max_count = ((MIPS_ELF_GOT_MAX_SIZE (abfd)
+  got_per_bfd_arg.max_count = ((MIPS_ELF_GOT_MAX_SIZE (info)
                                / MIPS_ELF_GOT_SIZE (abfd))
-                              - MIPS_RESERVED_GOTNO - pages);
+                              - MIPS_RESERVED_GOTNO (info));
+  got_per_bfd_arg.max_pages = 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.  */
@@ -3228,6 +3643,7 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
       g->next->global_gotsym = NULL;
       g->next->global_gotno = 0;
       g->next->local_gotno = 0;
+      g->next->page_gotno = 0;
       g->next->tls_gotno = 0;
       g->next->assigned_gotno = 0;
       g->next->tls_assigned_gotno = 0;
@@ -3237,6 +3653,11 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
                                              NULL);
       if (g->next->got_entries == NULL)
        return FALSE;
+      g->next->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
+                                                  mips_got_page_entry_eq,
+                                                  NULL);
+      if (g->next->got_page_entries == NULL)
+       return FALSE;
       g->next->bfd2got = NULL;
     }
   else
@@ -3339,21 +3760,25 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
     {
       struct mips_got_info *gn;
 
-      assign += MIPS_RESERVED_GOTNO;
+      assign += MIPS_RESERVED_GOTNO (info);
       g->assigned_gotno = assign;
-      g->local_gotno += assign + pages;
+      g->local_gotno += assign;
+      g->local_gotno += (pages < g->page_gotno ? pages : g->page_gotno);
       assign = g->local_gotno + g->global_gotno + g->tls_gotno;
 
+      /* Take g out of the direct list, and push it onto the reversed
+        list that gg points to.  g->next is guaranteed to be nonnull after
+        this operation, as required by mips_elf_initialize_tls_index. */
+      gn = g->next;
+      g->next = gg->next;
+      gg->next = g;
+
       /* 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.  */
-      gn = g->next;
-      g->next = gg->next;
-      gg->next = g;
+      /* Move onto the next GOT.  It will be a secondary GOT if nonull.  */
       g = gn;
 
       /* Mark global symbols in every non-primary GOT as ineligible for
@@ -3379,16 +3804,18 @@ mips_elf_next_relocation (bfd *abfd ATTRIBUTE_UNUSED, unsigned int r_type,
                          const Elf_Internal_Rela *relocation,
                          const Elf_Internal_Rela *relend)
 {
+  unsigned long r_symndx = ELF_R_SYM (abfd, relocation->r_info);
+
   while (relocation < relend)
     {
-      if (ELF_R_TYPE (abfd, relocation->r_info) == r_type)
+      if (ELF_R_TYPE (abfd, relocation->r_info) == r_type
+         && ELF_R_SYM (abfd, relocation->r_info) == r_symndx)
        return relocation;
 
       ++relocation;
     }
 
   /* We didn't find it.  */
-  bfd_set_error (bfd_error_bad_value);
   return NULL;
 }
 
@@ -3535,6 +3962,9 @@ mips_elf_create_got_section (bfd *abfd, struct bfd_link_info *info,
   struct bfd_link_hash_entry *bh;
   struct mips_got_info *g;
   bfd_size_type amt;
+  struct mips_elf_link_hash_table *htab;
+
+  htab = mips_elf_hash_table (info);
 
   /* This function may be called more than once.  */
   s = mips_elf_got_section (abfd, TRUE);
@@ -3571,6 +4001,7 @@ mips_elf_create_got_section (bfd *abfd, struct bfd_link_info *info,
   h->non_elf = 0;
   h->def_regular = 1;
   h->type = STT_OBJECT;
+  elf_hash_table (info)->hgot = h;
 
   if (info->shared
       && ! bfd_elf_link_record_dynamic_symbol (info, h))
@@ -3583,8 +4014,9 @@ mips_elf_create_got_section (bfd *abfd, struct bfd_link_info *info,
   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->local_gotno = MIPS_RESERVED_GOTNO (info);
+  g->page_gotno = 0;
+  g->assigned_gotno = MIPS_RESERVED_GOTNO (info);
   g->bfd2got = NULL;
   g->next = NULL;
   g->tls_ldm_offset = MINUS_ONE;
@@ -3592,13 +4024,41 @@ mips_elf_create_got_section (bfd *abfd, struct bfd_link_info *info,
                                    mips_elf_got_entry_eq, NULL);
   if (g->got_entries == NULL)
     return FALSE;
+  g->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
+                                        mips_got_page_entry_eq, NULL);
+  if (g->got_page_entries == NULL)
+    return FALSE;
   mips_elf_section_data (s)->u.got_info = g;
   mips_elf_section_data (s)->elf.this_hdr.sh_flags
     |= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
 
+  /* VxWorks also needs a .got.plt section.  */
+  if (htab->is_vxworks)
+    {
+      s = bfd_make_section_with_flags (abfd, ".got.plt",
+                                      SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
+                                      | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+      if (s == NULL || !bfd_set_section_alignment (abfd, s, 4))
+       return FALSE;
+
+      htab->sgotplt = s;
+    }
   return TRUE;
 }
 \f
+/* Return true if H refers to the special VxWorks __GOTT_BASE__ or
+   __GOTT_INDEX__ symbols.  These symbols are only special for
+   shared objects; they are not used in executables.  */
+
+static bfd_boolean
+is_gott_symbol (struct bfd_link_info *info, struct elf_link_hash_entry *h)
+{
+  return (mips_elf_hash_table (info)->is_vxworks
+         && info->shared
+         && (strcmp (h->root.root.string, "__GOTT_BASE__") == 0
+             || strcmp (h->root.root.string, "__GOTT_INDEX__") == 0));
+}
+\f
 /* Calculate the value produced by the RELOCATION (which comes from
    the INPUT_BFD).  The ADDEND is the addend to use for this
    RELOCATION; RELOCATION->R_ADDEND is ignored.
@@ -3661,6 +4121,11 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
   bfd_boolean overflowed_p;
   /* TRUE if this relocation refers to a MIPS16 function.  */
   bfd_boolean target_is_16_bit_code_p = FALSE;
+  struct mips_elf_link_hash_table *htab;
+  bfd *dynobj;
+
+  dynobj = elf_hash_table (info)->dynobj;
+  htab = mips_elf_hash_table (info);
 
   /* Parse the relocation.  */
   r_symndx = ELF_R_SYM (input_bfd, relocation->r_info);
@@ -3792,6 +4257,17 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
          BFD_ASSERT (bfd_get_section_by_name (abfd, ".dynamic") == NULL);
          symbol = 0;
        }
+      else if (ELF_MIPS_IS_OPTIONAL (h->root.other))
+       {
+         /* This is an optional symbol - an Irix specific extension to the
+            ELF spec.  Ignore it for now.
+            XXX - FIXME - there is more to the spec for OPTIONAL symbols
+            than simply ignoring them, but we do not handle this for now.
+            For information see the "64-bit ELF Object File Specification"
+            which is available from here:
+            http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf  */
+         symbol = 0;
+       }
       else
        {
          if (! ((*info->callbacks->undefined_symbol)
@@ -3811,9 +4287,10 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
      a stub.  */
   if (r_type != R_MIPS16_26 && !info->relocatable
       && ((h != NULL && h->fn_stub != NULL)
-         || (local_p && elf_tdata (input_bfd)->local_stubs != NULL
+         || (local_p
+             && elf_tdata (input_bfd)->local_stubs != NULL
              && elf_tdata (input_bfd)->local_stubs[r_symndx] != NULL))
-      && !mips_elf_stub_section_p (input_bfd, input_section))
+      && !mips16_stub_section_p (input_bfd, input_section))
     {
       /* This is a 32- or 64-bit call to a 16-bit function.  We should
         have already noticed that we were going to need the
@@ -3827,38 +4304,46 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
        }
 
       symbol = sec->output_section->vma + sec->output_offset;
+      /* The target is 16-bit, but the stub isn't.  */
+      target_is_16_bit_code_p = FALSE;
     }
   /* If this is a 16-bit call to a 32- or 64-bit function with a stub, we
      need to redirect the call to the stub.  */
   else if (r_type == R_MIPS16_26 && !info->relocatable
-          && h != NULL
-          && (h->call_stub != NULL || h->call_fp_stub != NULL)
+          && ((h != NULL && (h->call_stub != NULL || h->call_fp_stub != NULL))
+              || (local_p
+                  && elf_tdata (input_bfd)->local_call_stubs != NULL
+                  && elf_tdata (input_bfd)->local_call_stubs[r_symndx] != NULL))
           && !target_is_16_bit_code_p)
     {
-      /* If both call_stub and call_fp_stub are defined, we can figure
-        out which one to use by seeing which one appears in the input
-        file.  */
-      if (h->call_stub != NULL && h->call_fp_stub != NULL)
-       {
-         asection *o;
-
-         sec = NULL;
-         for (o = input_bfd->sections; o != NULL; o = o->next)
+      if (local_p)
+       sec = elf_tdata (input_bfd)->local_call_stubs[r_symndx];
+      else
+       {
+         /* If both call_stub and call_fp_stub are defined, we can figure
+            out which one to use by checking which one appears in the input
+            file.  */
+         if (h->call_stub != NULL && h->call_fp_stub != NULL)
            {
-             if (strncmp (bfd_get_section_name (input_bfd, o),
-                          CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0)
+             asection *o;
+             
+             sec = NULL;
+             for (o = input_bfd->sections; o != NULL; o = o->next)
                {
-                 sec = h->call_fp_stub;
-                 break;
+                 if (CALL_FP_STUB_P (bfd_get_section_name (input_bfd, o)))
+                   {
+                     sec = h->call_fp_stub;
+                     break;
+                   }
                }
+             if (sec == NULL)
+               sec = h->call_stub;
            }
-         if (sec == NULL)
+         else if (h->call_stub != NULL)
            sec = h->call_stub;
-       }
-      else if (h->call_stub != NULL)
-       sec = h->call_stub;
-      else
-       sec = h->call_fp_stub;
+         else
+           sec = h->call_fp_stub;
+       }
 
       BFD_ASSERT (sec->size > 0);
       symbol = sec->output_section->vma + sec->output_offset;
@@ -3899,52 +4384,61 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
       /* Find the index into the GOT where this value is located.  */
       if (r_type == R_MIPS_TLS_LDM)
        {
-         g = mips_elf_local_got_index (abfd, input_bfd, info, 0, 0, NULL,
-                                       r_type);
+         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
-            symbol turns out to be global.  The addend is then added
-            as GOT_OFST.  */
-         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,
-                                        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)))
+         /* On VxWorks, CALL relocations should refer to the .got.plt
+            entry, which is initialized to point at the PLT stub.  */
+         if (htab->is_vxworks
+             && (r_type == R_MIPS_CALL_HI16
+                 || r_type == R_MIPS_CALL_LO16
+                 || r_type == R_MIPS_CALL16))
+           {
+             BFD_ASSERT (addend == 0);
+             BFD_ASSERT (h->root.needs_plt);
+             g = mips_elf_gotplt_index (info, &h->root);
+           }
+         else
            {
-             /* This is a static link or a -Bsymbolic link.  The
-                symbol is defined locally, or was forced to be local.
-                We must initialize this entry in the GOT.  */
-             bfd *tmpbfd = elf_hash_table (info)->dynobj;
-             asection *sgot = mips_elf_got_section (tmpbfd, FALSE);
-             MIPS_ELF_PUT_WORD (tmpbfd, symbol, sgot->contents + g);
+             /* GOT_PAGE may take a non-zero addend, that is ignored in a
+                GOT_PAGE relocation that decays to GOT_DISP because the
+                symbol turns out to be global.  The addend is then added
+                as GOT_OFST.  */
+             BFD_ASSERT (addend == 0 || r_type == R_MIPS_GOT_PAGE);
+             g = mips_elf_global_got_index (dynobj, input_bfd,
+                                            &h->root, r_type, info);
+             if (h->tls_type == GOT_NORMAL
+                 && (! elf_hash_table(info)->dynamic_sections_created
+                     || (info->shared
+                         && (info->symbolic || h->root.forced_local)
+                         && h->root.def_regular)))
+               {
+                 /* This is a static link or a -Bsymbolic link.  The
+                    symbol is defined locally, or was forced to be local.
+                    We must initialize this entry in the GOT.  */
+                 asection *sgot = mips_elf_got_section (dynobj, FALSE);
+                 MIPS_ELF_PUT_WORD (dynobj, symbol, sgot->contents + g);
+               }
            }
        }
-      else if (r_type == R_MIPS_GOT16 || r_type == R_MIPS_CALL16)
-       /* There's no need to create a local GOT entry here; the
-          calculation for a local GOT16 entry does not involve G.  */
+      else if (!htab->is_vxworks
+              && (r_type == R_MIPS_CALL16 || (r_type == R_MIPS_GOT16)))
+       /* The calculation below does not involve "g".  */
        break;
       else
        {
-         g = mips_elf_local_got_index (abfd, input_bfd,
-                                       info, symbol + addend, r_symndx, h,
-                                       r_type);
+         g = mips_elf_local_got_index (abfd, input_bfd, info,
+                                       symbol + addend, r_symndx, h, r_type);
          if (g == MINUS_ONE)
            return bfd_reloc_outofrange;
        }
 
       /* Convert GOT indices to actual offsets.  */
-      g = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj,
-                                         abfd, input_bfd, g);
+      g = mips_elf_got_offset_from_index (dynobj, abfd, input_bfd, g);
       break;
 
     case R_MIPS_HI16:
@@ -3957,10 +4451,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
     case R_MIPS16_GPREL:
       gp0 = _bfd_get_gp_value (input_bfd);
       gp = _bfd_get_gp_value (abfd);
-      if (elf_hash_table (info)->dynobj)
-       gp += mips_elf_adjust_gp (abfd,
-                                 mips_elf_got_info
-                                 (elf_hash_table (info)->dynobj, NULL),
+      if (dynobj)
+       gp += mips_elf_adjust_gp (abfd, mips_elf_got_info (dynobj, NULL),
                                  input_bfd);
       break;
 
@@ -3970,7 +4462,35 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
 
   if (gnu_local_gp_p)
     symbol = gp;
-  
+
+  /* Relocations against the VxWorks __GOTT_BASE__ and __GOTT_INDEX__
+     symbols are resolved by the loader.  Add them to .rela.dyn.  */
+  if (h != NULL && is_gott_symbol (info, &h->root))
+    {
+      Elf_Internal_Rela outrel;
+      bfd_byte *loc;
+      asection *s;
+
+      s = mips_elf_rel_dyn_section (info, FALSE);
+      loc = s->contents + s->reloc_count++ * sizeof (Elf32_External_Rela);
+
+      outrel.r_offset = (input_section->output_section->vma
+                        + input_section->output_offset
+                        + relocation->r_offset);
+      outrel.r_info = ELF32_R_INFO (h->root.dynindx, r_type);
+      outrel.r_addend = addend;
+      bfd_elf32_swap_reloca_out (abfd, &outrel, loc);
+
+      /* If we've written this relocation for a readonly section,
+        we need to set DF_TEXTREL again, so that we do not delete the
+        DT_TEXTREL tag.  */
+      if (MIPS_ELF_READONLY_SECTION (input_section))
+       info->flags |= DF_TEXTREL;
+
+      *valuep = 0;
+      return bfd_reloc_ok;
+    }
+
   /* Figure out what kind of relocation is being performed.  */
   switch (r_type)
     {
@@ -3986,7 +4506,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
     case R_MIPS_REL32:
     case R_MIPS_64:
       if ((info->shared
-          || (elf_hash_table (info)->dynamic_sections_created
+          || (!htab->is_vxworks
+              && htab->root.dynamic_sections_created
               && h != NULL
               && h->root.def_dynamic
               && !h->root.def_regular))
@@ -3997,7 +4518,11 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
             against a symbol in a shared library, then we can't know
             where the symbol will end up.  So, we create a relocation
             record in the output, and leave the job up to the dynamic
-            linker.  */
+            linker.
+
+            In VxWorks executables, references to external symbols
+            are handled using copy relocs or PLT stubs, so there's
+            no need to add a dynamic relocation here.  */
          value = addend;
          if (!mips_elf_create_dynamic_relocation (abfd,
                                                   info,
@@ -4024,12 +4549,6 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
       value &= howto->dst_mask;
       break;
 
-    case R_MIPS_GNU_REL16_S2:
-      value = symbol + _bfd_mips_elf_sign_extend (addend, 18) - p;
-      overflowed_p = mips_elf_overflow_p (value, 18);
-      value = (value >> 2) & howto->dst_mask;
-      break;
-
     case R_MIPS16_26:
       /* The calculation for R_MIPS16_26 is just the same as for an
         R_MIPS_26.  It's only the storage of the relocated field into
@@ -4054,6 +4573,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
       break;
 
     case R_MIPS_TLS_DTPREL_LO16:
+    case R_MIPS_TLS_DTPREL32:
+    case R_MIPS_TLS_DTPREL64:
       value = (symbol + addend - dtprel_base (info)) & howto->dst_mask;
       break;
 
@@ -4155,13 +4676,12 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
 
     case R_MIPS_GOT16:
     case R_MIPS_CALL16:
-      if (local_p)
+      /* VxWorks does not have separate local and global semantics for
+        R_MIPS_GOT16; every relocation evaluates to "G".  */
+      if (!htab->is_vxworks && local_p)
        {
          bfd_boolean forced;
 
-         /* The special case is when the symbol is forced to be local.  We
-            need the full address in the GOT since no R_MIPS_LO16 relocation
-            follows.  */
          forced = ! mips_elf_local_relocation_p (input_bfd, relocation,
                                                  local_sections, FALSE);
          value = mips_elf_got16_entry (abfd, input_bfd, info,
@@ -4169,8 +4689,7 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
          if (value == MINUS_ONE)
            return bfd_reloc_outofrange;
          value
-           = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj,
-                                             abfd, input_bfd, value);
+           = mips_elf_got_offset_from_index (dynobj, abfd, input_bfd, value);
          overflowed_p = mips_elf_overflow_p (value, 16);
          break;
        }
@@ -4193,8 +4712,11 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
       break;
 
     case R_MIPS_PC16:
-      value = _bfd_mips_elf_sign_extend (addend, 16) + symbol - p;
-      overflowed_p = mips_elf_overflow_p (value, 16);
+    case R_MIPS_GNU_REL16_S2:
+      value = symbol + _bfd_mips_elf_sign_extend (addend, 18) - p;
+      overflowed_p = mips_elf_overflow_p (value, 18);
+      value >>= howto->rightshift;
+      value &= howto->dst_mask;
       break;
 
     case R_MIPS_GOT_HI16:
@@ -4221,8 +4743,7 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
       value = mips_elf_got_page (abfd, input_bfd, info, symbol + addend, NULL);
       if (value == MINUS_ONE)
        return bfd_reloc_outofrange;
-      value = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj,
-                                             abfd, input_bfd, value);
+      value = mips_elf_got_offset_from_index (dynobj, abfd, input_bfd, value);
       overflowed_p = mips_elf_overflow_p (value, 16);
       break;
 
@@ -4404,32 +4925,38 @@ mips_elf_perform_relocation (struct bfd_link_info *info,
 /* Returns TRUE if SECTION is a MIPS16 stub section.  */
 
 static bfd_boolean
-mips_elf_stub_section_p (bfd *abfd ATTRIBUTE_UNUSED, asection *section)
+mips16_stub_section_p (bfd *abfd ATTRIBUTE_UNUSED, asection *section)
 {
   const char *name = bfd_get_section_name (abfd, section);
 
-  return (strncmp (name, FN_STUB, sizeof FN_STUB - 1) == 0
-         || strncmp (name, CALL_STUB, sizeof CALL_STUB - 1) == 0
-         || strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0);
+  return FN_STUB_P (name) || CALL_STUB_P (name) || CALL_FP_STUB_P (name);
 }
 \f
-/* Add room for N relocations to the .rel.dyn section in ABFD.  */
+/* Add room for N relocations to the .rel(a).dyn section in ABFD.  */
 
 static void
-mips_elf_allocate_dynamic_relocations (bfd *abfd, unsigned int n)
+mips_elf_allocate_dynamic_relocations (bfd *abfd, struct bfd_link_info *info,
+                                      unsigned int n)
 {
   asection *s;
+  struct mips_elf_link_hash_table *htab;
 
-  s = mips_elf_rel_dyn_section (abfd, FALSE);
+  htab = mips_elf_hash_table (info);
+  s = mips_elf_rel_dyn_section (info, FALSE);
   BFD_ASSERT (s != NULL);
 
-  if (s->size == 0)
+  if (htab->is_vxworks)
+    s->size += n * MIPS_ELF_RELA_SIZE (abfd);
+  else
     {
-      /* Make room for a null element.  */
-      s->size += MIPS_ELF_REL_SIZE (abfd);
-      ++s->reloc_count;
+      if (s->size == 0)
+       {
+         /* Make room for a null element.  */
+         s->size += MIPS_ELF_REL_SIZE (abfd);
+         ++s->reloc_count;
+       }
+      s->size += n * MIPS_ELF_REL_SIZE (abfd);
     }
-  s->size += n * MIPS_ELF_REL_SIZE (abfd);
 }
 
 /* Create a rel.dyn relocation for the dynamic linker to resolve.  REL
@@ -4451,10 +4978,12 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd,
   int r_type;
   long indx;
   bfd_boolean defined_p;
+  struct mips_elf_link_hash_table *htab;
 
+  htab = mips_elf_hash_table (info);
   r_type = ELF_R_TYPE (output_bfd, rel->r_info);
   dynobj = elf_hash_table (info)->dynobj;
-  sreloc = mips_elf_rel_dyn_section (dynobj, FALSE);
+  sreloc = mips_elf_rel_dyn_section (info, FALSE);
   BFD_ASSERT (sreloc != NULL);
   BFD_ASSERT (sreloc->contents != NULL);
   BFD_ASSERT (sreloc->reloc_count * MIPS_ELF_REL_SIZE (output_bfd)
@@ -4462,10 +4991,13 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd,
 
   outrel[0].r_offset =
     _bfd_elf_section_offset (output_bfd, info, input_section, rel[0].r_offset);
-  outrel[1].r_offset =
-    _bfd_elf_section_offset (output_bfd, info, input_section, rel[1].r_offset);
-  outrel[2].r_offset =
-    _bfd_elf_section_offset (output_bfd, info, input_section, rel[2].r_offset);
+  if (ABI_64_P (output_bfd))
+    {
+      outrel[1].r_offset =
+       _bfd_elf_section_offset (output_bfd, info, input_section, rel[1].r_offset);
+      outrel[2].r_offset =
+       _bfd_elf_section_offset (output_bfd, info, input_section, rel[2].r_offset);
+    }
 
   if (outrel[0].r_offset == MINUS_ONE)
     /* The relocation field has been deleted.  */
@@ -4483,10 +5015,8 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd,
   /* We must now calculate the dynamic symbol table index to use
      in the relocation.  */
   if (h != NULL
-      && (! info->symbolic || !h->root.def_regular)
-      /* h->root.dynindx may be -1 if this symbol was marked to
-        become local.  */
-      && h->root.dynindx != -1)
+      && (!h->root.def_regular
+         || (info->shared && !info->symbolic && !h->root.forced_local)))
     {
       indx = h->root.dynindx;
       if (SGI_COMPAT (output_bfd))
@@ -4510,6 +5040,11 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd,
       else
        {
          indx = elf_section_data (sec->output_section)->dynindx;
+         if (indx == 0)
+           {
+             asection *osec = htab->root.text_index_section;
+             indx = elf_section_data (osec)->dynindx;
+           }
          if (indx == 0)
            abort ();
        }
@@ -4541,10 +5076,15 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd,
   if (defined_p && r_type != R_MIPS_REL32)
     *addendp += symbol;
 
-  /* The relocation is always an REL32 relocation because we don't
-     know where the shared library will wind up at load-time.  */
-  outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx,
-                                R_MIPS_REL32);
+  if (htab->is_vxworks)
+    /* VxWorks uses non-relative relocations for this.  */
+    outrel[0].r_info = ELF32_R_INFO (indx, R_MIPS_32);
+  else
+    /* The relocation is always an REL32 relocation because we don't
+       know where the shared library will wind up at load-time.  */
+    outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx,
+                                  R_MIPS_REL32);
+
   /* For strict adherence to the ABI specification, we should
      generate a R_MIPS_64 relocation record by itself before the
      _REL32/_64 record as well, such that the addend is read in as
@@ -4582,6 +5122,15 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd,
         (sreloc->contents
          + sreloc->reloc_count * sizeof (Elf64_Mips_External_Rel)));
     }
+  else if (htab->is_vxworks)
+    {
+      /* VxWorks uses RELA rather than REL dynamic relocations.  */
+      outrel[0].r_addend = *addendp;
+      bfd_elf32_swap_reloca_out
+       (output_bfd, &outrel[0],
+        (sreloc->contents
+         + sreloc->reloc_count * sizeof (Elf32_External_Rela)));
+    }
   else
     bfd_elf32_swap_reloc_out
       (output_bfd, &outrel[0],
@@ -4626,6 +5175,12 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd,
        }
     }
 
+  /* If we've written this relocation for a readonly section,
+     we need to set DF_TEXTREL again, so that we do not delete the
+     DT_TEXTREL tag.  */
+  if (MIPS_ELF_READONLY_SECTION (input_section))
+    info->flags |= DF_TEXTREL;
+
   return TRUE;
 }
 \f
@@ -4672,39 +5227,30 @@ _bfd_elf_mips_mach (flagword flags)
        default:
        case E_MIPS_ARCH_1:
          return bfd_mach_mips3000;
-         break;
 
        case E_MIPS_ARCH_2:
          return bfd_mach_mips6000;
-         break;
 
        case E_MIPS_ARCH_3:
          return bfd_mach_mips4000;
-         break;
 
        case E_MIPS_ARCH_4:
          return bfd_mach_mips8000;
-         break;
 
        case E_MIPS_ARCH_5:
          return bfd_mach_mips5;
-         break;
 
        case E_MIPS_ARCH_32:
          return bfd_mach_mipsisa32;
-         break;
 
        case E_MIPS_ARCH_64:
          return bfd_mach_mipsisa64;
-         break;
 
        case E_MIPS_ARCH_32R2:
          return bfd_mach_mipsisa32r2;
-         break;
 
        case E_MIPS_ARCH_64R2:
          return bfd_mach_mipsisa64r2;
-         break;
        }
     }
 
@@ -4794,6 +5340,7 @@ _bfd_mips_elf_symbol_processing (bfd *abfd, asymbol *asym)
       /* Common symbols less than the GP size are automatically
         treated as SHN_MIPS_SCOMMON symbols on IRIX5.  */
       if (asym->value > elf_gp_size (abfd)
+         || ELF_ST_TYPE (elfsym->internal_elf_sym.st_info) == STT_TLS
          || IRIX_COMPAT (abfd) == ict_irix6)
        break;
       /* Fall through.  */
@@ -5105,7 +5652,7 @@ _bfd_mips_elf_section_from_shdr (bfd *abfd,
        return FALSE;
       break;
     case SHT_MIPS_GPTAB:
-      if (strncmp (name, ".gptab.", sizeof ".gptab." - 1) != 0)
+      if (! CONST_STRNEQ (name, ".gptab."))
        return FALSE;
       break;
     case SHT_MIPS_UCODE:
@@ -5128,7 +5675,7 @@ _bfd_mips_elf_section_from_shdr (bfd *abfd,
        return FALSE;
       break;
     case SHT_MIPS_CONTENT:
-      if (strncmp (name, ".MIPS.content", sizeof ".MIPS.content" - 1) != 0)
+      if (! CONST_STRNEQ (name, ".MIPS.content"))
        return FALSE;
       break;
     case SHT_MIPS_OPTIONS:
@@ -5136,7 +5683,7 @@ _bfd_mips_elf_section_from_shdr (bfd *abfd,
        return FALSE;
       break;
     case SHT_MIPS_DWARF:
-      if (strncmp (name, ".debug_", sizeof ".debug_" - 1) != 0)
+      if (! CONST_STRNEQ (name, ".debug_"))
        return FALSE;
       break;
     case SHT_MIPS_SYMBOL_LIB:
@@ -5144,9 +5691,8 @@ _bfd_mips_elf_section_from_shdr (bfd *abfd,
        return FALSE;
       break;
     case SHT_MIPS_EVENTS:
-      if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) != 0
-         && strncmp (name, ".MIPS.post_rel",
-                     sizeof ".MIPS.post_rel" - 1) != 0)
+      if (! CONST_STRNEQ (name, ".MIPS.events")
+         && ! CONST_STRNEQ (name, ".MIPS.post_rel"))
        return FALSE;
       break;
     default:
@@ -5252,11 +5798,7 @@ _bfd_mips_elf_section_from_shdr (bfd *abfd,
 bfd_boolean
 _bfd_mips_elf_fake_sections (bfd *abfd, Elf_Internal_Shdr *hdr, asection *sec)
 {
-  register const char *name;
-  unsigned int sh_type;
-
-  name = bfd_get_section_name (abfd, sec);
-  sh_type = hdr->sh_type;
+  const char *name = bfd_get_section_name (abfd, sec);
 
   if (strcmp (name, ".liblist") == 0)
     {
@@ -5266,7 +5808,7 @@ _bfd_mips_elf_fake_sections (bfd *abfd, Elf_Internal_Shdr *hdr, asection *sec)
     }
   else if (strcmp (name, ".conflict") == 0)
     hdr->sh_type = SHT_MIPS_CONFLICT;
-  else if (strncmp (name, ".gptab.", sizeof ".gptab." - 1) == 0)
+  else if (CONST_STRNEQ (name, ".gptab."))
     {
       hdr->sh_type = SHT_MIPS_GPTAB;
       hdr->sh_entsize = sizeof (Elf32_External_gptab);
@@ -5323,7 +5865,7 @@ _bfd_mips_elf_fake_sections (bfd *abfd, Elf_Internal_Shdr *hdr, asection *sec)
       hdr->sh_type = SHT_MIPS_IFACE;
       hdr->sh_flags |= SHF_MIPS_NOSTRIP;
     }
-  else if (strncmp (name, ".MIPS.content", strlen (".MIPS.content")) == 0)
+  else if (CONST_STRNEQ (name, ".MIPS.content"))
     {
       hdr->sh_type = SHT_MIPS_CONTENT;
       hdr->sh_flags |= SHF_MIPS_NOSTRIP;
@@ -5335,17 +5877,24 @@ _bfd_mips_elf_fake_sections (bfd *abfd, Elf_Internal_Shdr *hdr, asection *sec)
       hdr->sh_entsize = 1;
       hdr->sh_flags |= SHF_MIPS_NOSTRIP;
     }
-  else if (strncmp (name, ".debug_", sizeof ".debug_" - 1) == 0)
-    hdr->sh_type = SHT_MIPS_DWARF;
+  else if (CONST_STRNEQ (name, ".debug_"))
+    {
+      hdr->sh_type = SHT_MIPS_DWARF;
+
+      /* Irix facilities such as libexc expect a single .debug_frame
+        per executable, the system ones have NOSTRIP set and the linker
+        doesn't merge sections with different flags so ...  */
+      if (SGI_COMPAT (abfd) && CONST_STRNEQ (name, ".debug_frame"))
+       hdr->sh_flags |= SHF_MIPS_NOSTRIP;
+    }
   else if (strcmp (name, ".MIPS.symlib") == 0)
     {
       hdr->sh_type = SHT_MIPS_SYMBOL_LIB;
       /* The sh_link and sh_info fields are set in
          final_write_processing.  */
     }
-  else if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) == 0
-          || strncmp (name, ".MIPS.post_rel",
-                      sizeof ".MIPS.post_rel" - 1) == 0)
+  else if (CONST_STRNEQ (name, ".MIPS.events")
+          || CONST_STRNEQ (name, ".MIPS.post_rel"))
     {
       hdr->sh_type = SHT_MIPS_EVENTS;
       hdr->sh_flags |= SHF_MIPS_NOSTRIP;
@@ -5358,12 +5907,6 @@ _bfd_mips_elf_fake_sections (bfd *abfd, Elf_Internal_Shdr *hdr, asection *sec)
       hdr->sh_entsize = 8;
     }
 
-  /* In the unlikely event a special section is empty it has to lose its
-     special meaning.  This may happen e.g. when using `strip' with the
-     "--only-keep-debug" option.  */
-  if (sec->size > 0 && !(sec->flags & SEC_HAS_CONTENTS))
-    hdr->sh_type = sh_type;
-
   /* The generic elf_fake_sections will set up REL_HDR using the default
    kind of relocations.  We used to set up a second header for the
    non-default kind of relocations here, but only NewABI would use
@@ -5414,12 +5957,27 @@ _bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info,
       return TRUE;
     }
 
+  /* Shared objects may have a dynamic symbol '_gp_disp' defined as
+     a SECTION *ABS*.  This causes ld to think it can resolve _gp_disp
+     by setting a DT_NEEDED for the shared object.  Since _gp_disp is
+     a magic symbol resolved by the linker, we ignore this bogus definition
+     of _gp_disp.  New ABI objects do not suffer from this problem so this
+     is not done for them. */
+  if (!NEWABI_P(abfd)
+      && (sym->st_shndx == SHN_ABS)
+      && (strcmp (*namep, "_gp_disp") == 0))
+    {
+      *namep = NULL;
+      return TRUE;
+    }
+
   switch (sym->st_shndx)
     {
     case SHN_COMMON:
       /* Common symbols less than the GP size are automatically
         treated as SHN_MIPS_SCOMMON symbols.  */
       if (sym->st_size > elf_gp_size (abfd)
+         || ELF_ST_TYPE (sym->st_info) == STT_TLS
          || IRIX_COMPAT (abfd) == ict_irix6)
        break;
       /* Fall through.  */
@@ -5584,23 +6142,29 @@ _bfd_mips_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
   flagword flags;
   register asection *s;
   const char * const *namep;
+  struct mips_elf_link_hash_table *htab;
 
+  htab = mips_elf_hash_table (info);
   flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
           | SEC_LINKER_CREATED | SEC_READONLY);
 
-  /* Mips ABI requests the .dynamic section to be read only.  */
-  s = bfd_get_section_by_name (abfd, ".dynamic");
-  if (s != NULL)
+  /* The psABI requires a read-only .dynamic section, but the VxWorks
+     EABI doesn't.  */
+  if (!htab->is_vxworks)
     {
-      if (! bfd_set_section_flags (abfd, s, flags))
-       return FALSE;
+      s = bfd_get_section_by_name (abfd, ".dynamic");
+      if (s != NULL)
+       {
+         if (! bfd_set_section_flags (abfd, s, flags))
+           return FALSE;
+       }
     }
 
   /* We need to create .got section.  */
   if (! mips_elf_create_got_section (abfd, info, FALSE))
     return FALSE;
 
-  if (! mips_elf_rel_dyn_section (elf_hash_table (info)->dynobj, TRUE))
+  if (! mips_elf_rel_dyn_section (info, TRUE))
     return FALSE;
 
   /* Create .stub section.  */
@@ -5721,9 +6285,169 @@ _bfd_mips_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
        }
     }
 
+  if (htab->is_vxworks)
+    {
+      /* Create the .plt, .rela.plt, .dynbss and .rela.bss sections.
+        Also create the _PROCEDURE_LINKAGE_TABLE symbol.  */
+      if (!_bfd_elf_create_dynamic_sections (abfd, info))
+       return FALSE;
+
+      /* Cache the sections created above.  */
+      htab->sdynbss = bfd_get_section_by_name (abfd, ".dynbss");
+      htab->srelbss = bfd_get_section_by_name (abfd, ".rela.bss");
+      htab->srelplt = bfd_get_section_by_name (abfd, ".rela.plt");
+      htab->splt = bfd_get_section_by_name (abfd, ".plt");
+      if (!htab->sdynbss
+         || (!htab->srelbss && !info->shared)
+         || !htab->srelplt
+         || !htab->splt)
+       abort ();
+
+      /* Do the usual VxWorks handling.  */
+      if (!elf_vxworks_create_dynamic_sections (abfd, info, &htab->srelplt2))
+       return FALSE;
+
+      /* Work out the PLT sizes.  */
+      if (info->shared)
+       {
+         htab->plt_header_size
+           = 4 * ARRAY_SIZE (mips_vxworks_shared_plt0_entry);
+         htab->plt_entry_size
+           = 4 * ARRAY_SIZE (mips_vxworks_shared_plt_entry);
+       }
+      else
+       {
+         htab->plt_header_size
+           = 4 * ARRAY_SIZE (mips_vxworks_exec_plt0_entry);
+         htab->plt_entry_size
+           = 4 * ARRAY_SIZE (mips_vxworks_exec_plt_entry);
+       }
+    }
+
   return TRUE;
 }
 \f
+/* Return true if relocation REL against section SEC is a REL rather than
+   RELA relocation.  RELOCS is the first relocation in the section and
+   ABFD is the bfd that contains SEC.  */
+
+static bfd_boolean
+mips_elf_rel_relocation_p (bfd *abfd, asection *sec,
+                          const Elf_Internal_Rela *relocs,
+                          const Elf_Internal_Rela *rel)
+{
+  Elf_Internal_Shdr *rel_hdr;
+  const struct elf_backend_data *bed;
+
+  /* To determine which flavor or relocation this is, we depend on the
+     fact that the INPUT_SECTION's REL_HDR is read before its REL_HDR2.  */
+  rel_hdr = &elf_section_data (sec)->rel_hdr;
+  bed = get_elf_backend_data (abfd);
+  if ((size_t) (rel - relocs)
+      >= (NUM_SHDR_ENTRIES (rel_hdr) * bed->s->int_rels_per_ext_rel))
+    rel_hdr = elf_section_data (sec)->rel_hdr2;
+  return rel_hdr->sh_entsize == MIPS_ELF_REL_SIZE (abfd);
+}
+
+/* Read the addend for REL relocation REL, which belongs to bfd ABFD.
+   HOWTO is the relocation's howto and CONTENTS points to the contents
+   of the section that REL is against.  */
+
+static bfd_vma
+mips_elf_read_rel_addend (bfd *abfd, const Elf_Internal_Rela *rel,
+                         reloc_howto_type *howto, bfd_byte *contents)
+{
+  bfd_byte *location;
+  unsigned int r_type;
+  bfd_vma addend;
+
+  r_type = ELF_R_TYPE (abfd, rel->r_info);
+  location = contents + rel->r_offset;
+
+  /* Get the addend, which is stored in the input file.  */
+  _bfd_mips16_elf_reloc_unshuffle (abfd, r_type, FALSE, location);
+  addend = mips_elf_obtain_contents (howto, rel, abfd, contents);
+  _bfd_mips16_elf_reloc_shuffle (abfd, r_type, FALSE, location);
+
+  return addend & howto->src_mask;
+}
+
+/* REL is a relocation in ABFD that needs a partnering LO16 relocation
+   and *ADDEND is the addend for REL itself.  Look for the LO16 relocation
+   and update *ADDEND with the final addend.  Return true on success
+   or false if the LO16 could not be found.  RELEND is the exclusive
+   upper bound on the relocations for REL's section.  */
+
+static bfd_boolean
+mips_elf_add_lo16_rel_addend (bfd *abfd,
+                             const Elf_Internal_Rela *rel,
+                             const Elf_Internal_Rela *relend,
+                             bfd_byte *contents, bfd_vma *addend)
+{
+  unsigned int r_type, lo16_type;
+  const Elf_Internal_Rela *lo16_relocation;
+  reloc_howto_type *lo16_howto;
+  bfd_vma l;
+
+  r_type = ELF_R_TYPE (abfd, rel->r_info);
+  if (r_type == R_MIPS16_HI16)
+    lo16_type = R_MIPS16_LO16;
+  else
+    lo16_type = R_MIPS_LO16;
+
+  /* The combined value is the sum of the HI16 addend, left-shifted by
+     sixteen bits, and the LO16 addend, sign extended.  (Usually, the
+     code does a `lui' of the HI16 value, and then an `addiu' of the
+     LO16 value.)
+
+     Scan ahead to find a matching LO16 relocation.
+
+     According to the MIPS ELF ABI, the R_MIPS_LO16 relocation must
+     be immediately following.  However, for the IRIX6 ABI, the next
+     relocation may be a composed relocation consisting of several
+     relocations for the same address.  In that case, the R_MIPS_LO16
+     relocation may occur as one of these.  We permit a similar
+     extension in general, as that is useful for GCC.
+
+     In some cases GCC dead code elimination removes the LO16 but keeps
+     the corresponding HI16.  This is strictly speaking a violation of
+     the ABI but not immediately harmful.  */
+  lo16_relocation = mips_elf_next_relocation (abfd, lo16_type, rel, relend);
+  if (lo16_relocation == NULL)
+    return FALSE;
+
+  /* Obtain the addend kept there.  */
+  lo16_howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, lo16_type, FALSE);
+  l = mips_elf_read_rel_addend (abfd, lo16_relocation, lo16_howto, contents);
+
+  l <<= lo16_howto->rightshift;
+  l = _bfd_mips_elf_sign_extend (l, 16);
+
+  *addend <<= 16;
+  *addend += l;
+  return TRUE;
+}
+
+/* Try to read the contents of section SEC in bfd ABFD.  Return true and
+   store the contents in *CONTENTS on success.  Assume that *CONTENTS
+   already holds the contents if it is nonull on entry.  */
+
+static bfd_boolean
+mips_elf_get_section_contents (bfd *abfd, asection *sec, bfd_byte **contents)
+{
+  if (*contents)
+    return TRUE;
+
+  /* Get cached copy if it exists.  */
+  if (elf_section_data (sec)->this_hdr.contents != NULL)
+    {
+      *contents = elf_section_data (sec)->this_hdr.contents;
+      return TRUE;
+    }
+
+  return bfd_malloc_and_get_section (abfd, sec, contents);
+}
+
 /* Look through the relocs for a section during the first phase, and
    allocate space in the global offset table.  */
 
@@ -5742,10 +6466,15 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   asection *sgot;
   asection *sreloc;
   const struct elf_backend_data *bed;
+  struct mips_elf_link_hash_table *htab;
+  bfd_byte *contents;
+  bfd_vma addend;
+  reloc_howto_type *howto;
 
   if (info->relocatable)
     return TRUE;
 
+  htab = mips_elf_hash_table (info);
   dynobj = elf_hash_table (info)->dynobj;
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
@@ -5754,7 +6483,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   /* Check for the mips16 stub sections.  */
 
   name = bfd_get_section_name (abfd, sec);
-  if (strncmp (name, FN_STUB, sizeof FN_STUB - 1) == 0)
+  if (FN_STUB_P (name))
     {
       unsigned long r_symndx;
 
@@ -5779,12 +6508,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              /* We can ignore stub sections when looking for relocs.  */
              if ((o->flags & SEC_RELOC) == 0
                  || o->reloc_count == 0
-                 || strncmp (bfd_get_section_name (abfd, o), FN_STUB,
-                             sizeof FN_STUB - 1) == 0
-                 || strncmp (bfd_get_section_name (abfd, o), CALL_STUB,
-                             sizeof CALL_STUB - 1) == 0
-                 || strncmp (bfd_get_section_name (abfd, o), CALL_FP_STUB,
-                             sizeof CALL_FP_STUB - 1) == 0)
+                 || mips16_stub_section_p (abfd, o))
                continue;
 
              sec_relocs
@@ -5836,6 +6560,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              elf_tdata (abfd)->local_stubs = n;
            }
 
+         sec->flags |= SEC_KEEP;
          elf_tdata (abfd)->local_stubs[r_symndx] = sec;
 
          /* We don't need to set mips16_stubs_seen in this case.
@@ -5856,12 +6581,23 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
          /* H is the symbol this stub is for.  */
 
+         /* If we already have an appropriate stub for this function, we
+            don't need another one, so we can discard this one.  Since
+            this function is called before the linker maps input sections
+            to output sections, we can easily discard it by setting the
+            SEC_EXCLUDE flag.  */
+         if (h->fn_stub != NULL)
+           {
+             sec->flags |= SEC_EXCLUDE;
+             return TRUE;
+           }
+
+         sec->flags |= SEC_KEEP;
          h->fn_stub = sec;
          mips_elf_hash_table (info)->mips16_stubs_seen = TRUE;
        }
     }
-  else if (strncmp (name, CALL_STUB, sizeof CALL_STUB - 1) == 0
-          || strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0)
+  else if (CALL_STUB_P (name) || CALL_FP_STUB_P (name))
     {
       unsigned long r_symndx;
       struct mips_elf_link_hash_entry *h;
@@ -5875,42 +6611,106 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       if (r_symndx < extsymoff
          || sym_hashes[r_symndx - extsymoff] == NULL)
        {
-         /* This stub was actually built for a static symbol defined
-            in the same file.  We assume that all static symbols in
-            mips16 code are themselves mips16, so we can simply
-            discard this stub.  Since this function is called before
-            the linker maps input sections to output sections, we can
-            easily discard it by setting the SEC_EXCLUDE flag.  */
-         sec->flags |= SEC_EXCLUDE;
-         return TRUE;
-       }
+         asection *o;
 
-      h = ((struct mips_elf_link_hash_entry *)
-          sym_hashes[r_symndx - extsymoff]);
+         /* This stub is for a local symbol.  This stub will only be
+             needed if there is some relocation (R_MIPS16_26) in this BFD
+             that refers to this symbol.  */
+         for (o = abfd->sections; o != NULL; o = o->next)
+           {
+             Elf_Internal_Rela *sec_relocs;
+             const Elf_Internal_Rela *r, *rend;
+
+             /* We can ignore stub sections when looking for relocs.  */
+             if ((o->flags & SEC_RELOC) == 0
+                 || o->reloc_count == 0
+                 || mips16_stub_section_p (abfd, o))
+               continue;
 
-      /* H is the symbol this stub is for.  */
+             sec_relocs
+               = _bfd_elf_link_read_relocs (abfd, o, NULL, NULL,
+                                            info->keep_memory);
+             if (sec_relocs == NULL)
+               return FALSE;
 
-      if (strncmp (name, CALL_FP_STUB, sizeof CALL_FP_STUB - 1) == 0)
-       loc = &h->call_fp_stub;
-      else
-       loc = &h->call_stub;
-
-      /* If we already have an appropriate stub for this function, we
-        don't need another one, so we can discard this one.  Since
-        this function is called before the linker maps input sections
-        to output sections, we can easily discard it by setting the
-        SEC_EXCLUDE flag.  We can also discard this section if we
-        happen to already know that this is a mips16 function; it is
-        not necessary to check this here, as it is checked later, but
-        it is slightly faster to check now.  */
-      if (*loc != NULL || h->root.other == STO_MIPS16)
-       {
-         sec->flags |= SEC_EXCLUDE;
-         return TRUE;
+             rend = sec_relocs + o->reloc_count;
+             for (r = sec_relocs; r < rend; r++)
+               if (ELF_R_SYM (abfd, r->r_info) == r_symndx
+                   && ELF_R_TYPE (abfd, r->r_info) == R_MIPS16_26)
+                   break;
+
+             if (elf_section_data (o)->relocs != sec_relocs)
+               free (sec_relocs);
+
+             if (r < rend)
+               break;
+           }
+
+         if (o == NULL)
+           {
+             /* There is no non-call reloc for this stub, so we do
+                 not need it.  Since this function is called before
+                 the linker maps input sections to output sections, we
+                 can easily discard it by setting the SEC_EXCLUDE
+                 flag.  */
+             sec->flags |= SEC_EXCLUDE;
+             return TRUE;
+           }
+
+         /* Record this stub in an array of local symbol call_stubs for
+             this BFD.  */
+         if (elf_tdata (abfd)->local_call_stubs == NULL)
+           {
+             unsigned long symcount;
+             asection **n;
+             bfd_size_type amt;
+
+             if (elf_bad_symtab (abfd))
+               symcount = NUM_SHDR_ENTRIES (symtab_hdr);
+             else
+               symcount = symtab_hdr->sh_info;
+             amt = symcount * sizeof (asection *);
+             n = bfd_zalloc (abfd, amt);
+             if (n == NULL)
+               return FALSE;
+             elf_tdata (abfd)->local_call_stubs = n;
+           }
+
+         sec->flags |= SEC_KEEP;
+         elf_tdata (abfd)->local_call_stubs[r_symndx] = sec;
+
+         /* We don't need to set mips16_stubs_seen in this case.
+             That flag is used to see whether we need to look through
+             the global symbol table for stubs.  We don't need to set
+             it here, because we just have a local stub.  */
        }
+      else
+       {
+         h = ((struct mips_elf_link_hash_entry *)
+              sym_hashes[r_symndx - extsymoff]);
+         
+         /* H is the symbol this stub is for.  */
+         
+         if (CALL_FP_STUB_P (name))
+           loc = &h->call_fp_stub;
+         else
+           loc = &h->call_stub;
+         
+         /* If we already have an appropriate stub for this function, we
+            don't need another one, so we can discard this one.  Since
+            this function is called before the linker maps input sections
+            to output sections, we can easily discard it by setting the
+            SEC_EXCLUDE flag.  */
+         if (*loc != NULL)
+           {
+             sec->flags |= SEC_EXCLUDE;
+             return TRUE;
+           }
 
-      *loc = sec;
-      mips_elf_hash_table (info)->mips16_stubs_seen = TRUE;
+         sec->flags |= SEC_KEEP;
+         *loc = sec;
+         mips_elf_hash_table (info)->mips16_stubs_seen = TRUE;
+       }
     }
 
   if (dynobj == NULL)
@@ -5934,6 +6734,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   sreloc = NULL;
   bed = get_elf_backend_data (abfd);
   rel_end = relocs + sec->reloc_count * bed->s->int_rels_per_ext_rel;
+  contents = NULL;
   for (rel = relocs; rel < rel_end; ++rel)
     {
       unsigned long r_symndx;
@@ -5979,6 +6780,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
            case R_MIPS_GOT_PAGE:
            case R_MIPS_GOT_OFST:
            case R_MIPS_GOT_DISP:
+           case R_MIPS_TLS_GOTTPREL:
            case R_MIPS_TLS_GD:
            case R_MIPS_TLS_LDM:
              if (dynobj == NULL)
@@ -5986,13 +6788,24 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              if (! mips_elf_create_got_section (dynobj, info, FALSE))
                return FALSE;
              g = mips_elf_got_info (dynobj, &sgot);
+             if (htab->is_vxworks && !info->shared)
+               {
+                 (*_bfd_error_handler)
+                   (_("%B: GOT reloc at 0x%lx not expected in executables"),
+                    abfd, (unsigned long) rel->r_offset);
+                 bfd_set_error (bfd_error_bad_value);
+                 return FALSE;
+               }
              break;
 
            case R_MIPS_32:
            case R_MIPS_REL32:
            case R_MIPS_64:
+             /* In VxWorks executables, references to external symbols
+                are handled using copy relocs or PLT stubs, so there's
+                no need to add a dynamic relocation here.  */
              if (dynobj == NULL
-                 && (info->shared || h != NULL)
+                 && (info->shared || (h != NULL && !htab->is_vxworks))
                  && (sec->flags & SEC_ALLOC) != 0)
                elf_hash_table (info)->dynobj = dynobj = abfd;
              break;
@@ -6002,15 +6815,39 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
            }
        }
 
-      if (!h && (r_type == R_MIPS_CALL_LO16
-                || r_type == R_MIPS_GOT_LO16
-                || r_type == R_MIPS_GOT_DISP))
+      if (h)
+       {
+         ((struct mips_elf_link_hash_entry *) h)->is_relocation_target = TRUE;
+
+         /* Relocations against the special VxWorks __GOTT_BASE__ and
+            __GOTT_INDEX__ symbols must be left to the loader.  Allocate
+            room for them in .rela.dyn.  */
+         if (is_gott_symbol (info, h))
+           {
+             if (sreloc == NULL)
+               {
+                 sreloc = mips_elf_rel_dyn_section (info, TRUE);
+                 if (sreloc == NULL)
+                   return FALSE;
+               }
+             mips_elf_allocate_dynamic_relocations (dynobj, info, 1);
+             if (MIPS_ELF_READONLY_SECTION (sec))
+               /* We tell the dynamic linker that there are
+                  relocations against the text segment.  */
+               info->flags |= DF_TEXTREL;
+           }
+       }
+      else if (r_type == R_MIPS_CALL_LO16
+              || r_type == R_MIPS_GOT_LO16
+              || r_type == R_MIPS_GOT_DISP
+              || (r_type == R_MIPS_GOT16 && htab->is_vxworks))
        {
          /* We may need a local GOT entry for this relocation.  We
             don't count R_MIPS_GOT_PAGE because we can estimate the
             maximum number of pages needed by looking at the size of
             the segment.  Similar comments apply to R_MIPS_GOT16 and
-            R_MIPS_CALL16.  We don't count R_MIPS_GOT_HI16, or
+            R_MIPS_CALL16, except on VxWorks, where GOT relocations
+            always evaluate to "G".  We don't count R_MIPS_GOT_HI16, or
             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,
@@ -6035,8 +6872,11 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_MIPS_CALL_LO16:
          if (h != NULL)
            {
-             /* This symbol requires a global offset table entry.  */
-             if (! mips_elf_record_global_got_symbol (h, abfd, info, g, 0))
+             /* VxWorks call relocations point the function's .got.plt
+                entry, which will be allocated by adjust_dynamic_symbol.
+                Otherwise, this symbol requires a global GOT entry.  */
+             if ((!htab->is_vxworks || h->forced_local)
+                 && !mips_elf_record_global_got_symbol (h, abfd, info, g, 0))
                return FALSE;
 
              /* We need a stub, not a plt entry for the undefined
@@ -6050,9 +6890,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_MIPS_GOT_PAGE:
          /* If this is a global, overridable symbol, GOT_PAGE will
             decay to GOT_DISP, so we'll need a GOT entry for it.  */
-         if (h == NULL)
-           break;
-         else
+         if (h)
            {
              struct mips_elf_link_hash_entry *hmips =
                (struct mips_elf_link_hash_entry *) h;
@@ -6065,13 +6903,37 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              if (hmips->root.def_regular
                  && ! (info->shared && ! info->symbolic
                        && ! hmips->root.forced_local))
-               break;
+               h = NULL;
            }
          /* Fall through.  */
 
        case R_MIPS_GOT16:
        case R_MIPS_GOT_HI16:
        case R_MIPS_GOT_LO16:
+         if (!h)
+           {
+             /* This relocation needs a page entry in the GOT.  */
+             if (mips_elf_rel_relocation_p (abfd, sec, relocs, rel))
+               {
+                 if (!mips_elf_get_section_contents (abfd, sec, &contents))
+                   return FALSE;
+                 howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, r_type, FALSE);
+                 addend = mips_elf_read_rel_addend (abfd, rel,
+                                                    howto, contents);
+                 if (r_type == R_MIPS_GOT16)
+                   mips_elf_add_lo16_rel_addend (abfd, rel, rel_end,
+                                                 contents, &addend);
+                 else
+                   addend <<= howto->rightshift;
+               }
+             else
+               addend = rel->r_addend;
+             if (!mips_elf_record_got_page_entry (abfd, r_symndx, addend, g))
+               return FALSE;
+             break;
+           }
+         /* Fall through.  */
+
        case R_MIPS_GOT_DISP:
          if (h && ! mips_elf_record_global_got_symbol (h, abfd, info, g, 0))
            return FALSE;
@@ -6122,25 +6984,25 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_MIPS_32:
        case R_MIPS_REL32:
        case R_MIPS_64:
-         if ((info->shared || h != NULL)
+         /* In VxWorks executables, references to external symbols
+            are handled using copy relocs or PLT stubs, so there's
+            no need to add a .rela.dyn entry for this relocation.  */
+         if ((info->shared || (h != NULL && !htab->is_vxworks))
              && (sec->flags & SEC_ALLOC) != 0)
            {
              if (sreloc == NULL)
                {
-                 sreloc = mips_elf_rel_dyn_section (dynobj, TRUE);
+                 sreloc = mips_elf_rel_dyn_section (info, TRUE);
                  if (sreloc == NULL)
                    return FALSE;
                }
-#define MIPS_READONLY_SECTION (SEC_ALLOC | SEC_LOAD | SEC_READONLY)
              if (info->shared)
                {
                  /* When creating a shared object, we must copy these
                     reloc types into the output file as R_MIPS_REL32
-                    relocs.  We make room for this reloc in the
-                    .rel.dyn reloc section.  */
-                 mips_elf_allocate_dynamic_relocations (dynobj, 1);
-                 if ((sec->flags & MIPS_READONLY_SECTION)
-                     == MIPS_READONLY_SECTION)
+                    relocs.  Make room for this reloc in .rel(a).dyn.  */
+                 mips_elf_allocate_dynamic_relocations (dynobj, info, 1);
+                 if (MIPS_ELF_READONLY_SECTION (sec))
                    /* We tell the dynamic linker that there are
                       relocations against the text segment.  */
                    info->flags |= DF_TEXTREL;
@@ -6153,8 +7015,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                      defined in a dynamic object.  */
                  hmips = (struct mips_elf_link_hash_entry *) h;
                  ++hmips->possibly_dynamic_relocs;
-                 if ((sec->flags & MIPS_READONLY_SECTION)
-                     == MIPS_READONLY_SECTION)
+                 if (MIPS_ELF_READONLY_SECTION (sec))
                    /* We need it to tell the dynamic linker if there
                       are relocations against the text segment.  */
                    hmips->readonly_reloc = TRUE;
@@ -6163,8 +7024,10 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              /* Even though we don't directly need a GOT entry for
                 this symbol, a symbol must have a dynamic symbol
                 table index greater that DT_MIPS_GOTSYM if there are
-                dynamic relocations against it.  */
-             if (h != NULL)
+                dynamic relocations against it.  This does not apply
+                to VxWorks, which does not have the usual coupling
+                between global GOT entries and .dynsym entries.  */
+             if (h != NULL && !htab->is_vxworks)
                {
                  if (dynobj == NULL)
                    elf_hash_table (info)->dynobj = dynobj = abfd;
@@ -6181,7 +7044,16 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              sizeof (Elf32_External_crinfo);
          break;
 
+       case R_MIPS_PC16:
+         if (h)
+           ((struct mips_elf_link_hash_entry *) h)->is_branch_target = TRUE;
+         break;
+
        case R_MIPS_26:
+         if (h)
+           ((struct mips_elf_link_hash_entry *) h)->is_branch_target = TRUE;
+         /* Fall through.  */
+
        case R_MIPS_GPREL16:
        case R_MIPS_LITERAL:
        case R_MIPS_GPREL32:
@@ -6200,7 +7072,9 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          /* This relocation describes which C++ vtable entries are actually
             used.  Record for later use during GC.  */
        case R_MIPS_GNU_VTENTRY:
-         if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_offset))
+         BFD_ASSERT (h != NULL);
+         if (h != NULL
+             && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_offset))
            return FALSE;
          break;
 
@@ -6209,36 +7083,28 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        }
 
       /* We must not create a stub for a symbol that has relocations
-         related to taking the function's address.  */
-      switch (r_type)
-       {
-       default:
-         if (h != NULL)
-           {
-             struct mips_elf_link_hash_entry *mh;
-
-             mh = (struct mips_elf_link_hash_entry *) h;
-             mh->no_fn_stub = TRUE;
-           }
-         break;
-       case R_MIPS_CALL16:
-       case R_MIPS_CALL_HI16:
-       case R_MIPS_CALL_LO16:
-       case R_MIPS_JALR:
-         break;
-       }
+        related to taking the function's address.  This doesn't apply to
+        VxWorks, where CALL relocs refer to a .got.plt entry instead of
+        a normal .got entry.  */
+      if (!htab->is_vxworks && h != NULL)
+       switch (r_type)
+         {
+         default:
+           ((struct mips_elf_link_hash_entry *) h)->no_fn_stub = TRUE;
+           break;
+         case R_MIPS_CALL16:
+         case R_MIPS_CALL_HI16:
+         case R_MIPS_CALL_LO16:
+         case R_MIPS_JALR:
+           break;
+         }
 
       /* If this reloc is not a 16 bit call, and it has a global
          symbol, then we will need the fn_stub if there is one.
          References from a stub section do not count.  */
       if (h != NULL
          && r_type != R_MIPS16_26
-         && strncmp (bfd_get_section_name (abfd, sec), FN_STUB,
-                     sizeof FN_STUB - 1) != 0
-         && strncmp (bfd_get_section_name (abfd, sec), CALL_STUB,
-                     sizeof CALL_STUB - 1) != 0
-         && strncmp (bfd_get_section_name (abfd, sec), CALL_FP_STUB,
-                     sizeof CALL_FP_STUB - 1) != 0)
+         && !mips16_stub_section_p (abfd, sec))
        {
          struct mips_elf_link_hash_entry *mh;
 
@@ -6372,17 +7238,8 @@ _bfd_mips_relax_section (bfd *abfd, asection *sec,
        continue;
 
       /* Get the section contents if we haven't done so already.  */
-      if (contents == NULL)
-       {
-         /* Get cached copy if it exists.  */
-         if (elf_section_data (sec)->this_hdr.contents != NULL)
-           contents = elf_section_data (sec)->this_hdr.contents;
-         else
-           {
-             if (!bfd_malloc_and_get_section (abfd, sec, &contents))
-               goto relax_return;
-           }
-       }
+      if (!mips_elf_get_section_contents (abfd, sec, &contents))
+       goto relax_return;
 
       instruction = bfd_get_32 (abfd, contents + irel->r_offset);
 
@@ -6433,7 +7290,9 @@ _bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   bfd *dynobj;
   struct mips_elf_link_hash_entry *hmips;
   asection *s;
+  struct mips_elf_link_hash_table *htab;
 
+  htab = mips_elf_hash_table (info);
   dynobj = elf_hash_table (info)->dynobj;
 
   /* Make sure we know what is going on here.  */
@@ -6453,8 +7312,8 @@ _bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       && (h->root.type == bfd_link_hash_defweak
          || !h->def_regular))
     {
-      mips_elf_allocate_dynamic_relocations (dynobj,
-                                            hmips->possibly_dynamic_relocs);
+      mips_elf_allocate_dynamic_relocations
+       (dynobj, info, hmips->possibly_dynamic_relocs);
       if (hmips->readonly_reloc)
        /* We tell the dynamic linker that there are relocations
           against the text segment.  */
@@ -6486,7 +7345,7 @@ _bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
          h->plt.offset = s->size;
 
          /* Make room for this stub code.  */
-         s->size += MIPS_FUNCTION_STUB_SIZE;
+         s->size += htab->function_stub_size;
 
          /* The last half word of the stub will be filled with the index
             of this symbol in .dynsym section.  */
@@ -6519,7 +7378,167 @@ _bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
 
   return TRUE;
 }
+
+/* Likewise, for VxWorks.  */
+
+bfd_boolean
+_bfd_mips_vxworks_adjust_dynamic_symbol (struct bfd_link_info *info,
+                                        struct elf_link_hash_entry *h)
+{
+  bfd *dynobj;
+  struct mips_elf_link_hash_entry *hmips;
+  struct mips_elf_link_hash_table *htab;
+
+  htab = mips_elf_hash_table (info);
+  dynobj = elf_hash_table (info)->dynobj;
+  hmips = (struct mips_elf_link_hash_entry *) h;
+
+  /* Make sure we know what is going on here.  */
+  BFD_ASSERT (dynobj != NULL
+             && (h->needs_plt
+                 || h->needs_copy
+                 || h->u.weakdef != NULL
+                 || (h->def_dynamic
+                     && h->ref_regular
+                     && !h->def_regular)));
+
+  /* If the symbol is defined by a dynamic object, we need a PLT stub if
+     either (a) we want to branch to the symbol or (b) we're linking an
+     executable that needs a canonical function address.  In the latter
+     case, the canonical address will be the address of the executable's
+     load stub.  */
+  if ((hmips->is_branch_target
+       || (!info->shared
+          && h->type == STT_FUNC
+          && hmips->is_relocation_target))
+      && h->def_dynamic
+      && h->ref_regular
+      && !h->def_regular
+      && !h->forced_local)
+    h->needs_plt = 1;
+
+  /* Locally-binding symbols do not need a PLT stub; we can refer to
+     the functions directly.  */
+  else if (h->needs_plt
+          && (SYMBOL_CALLS_LOCAL (info, h)
+              || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+                  && h->root.type == bfd_link_hash_undefweak)))
+    {
+      h->needs_plt = 0;
+      return TRUE;
+    }
+
+  if (h->needs_plt)
+    {
+      /* If this is the first symbol to need a PLT entry, allocate room
+        for the header, and for the header's .rela.plt.unloaded entries.  */
+      if (htab->splt->size == 0)
+       {
+         htab->splt->size += htab->plt_header_size;
+         if (!info->shared)
+           htab->srelplt2->size += 2 * sizeof (Elf32_External_Rela);
+       }
+
+      /* Assign the next .plt entry to this symbol.  */
+      h->plt.offset = htab->splt->size;
+      htab->splt->size += htab->plt_entry_size;
+
+      /* If the output file has no definition of the symbol, set the
+        symbol's value to the address of the stub.  For executables,
+        point at the PLT load stub rather than the lazy resolution stub;
+        this stub will become the canonical function address.  */
+      if (!h->def_regular)
+       {
+         h->root.u.def.section = htab->splt;
+         h->root.u.def.value = h->plt.offset;
+         if (!info->shared)
+           h->root.u.def.value += 8;
+       }
+
+      /* Make room for the .got.plt entry and the R_JUMP_SLOT relocation.  */
+      htab->sgotplt->size += 4;
+      htab->srelplt->size += sizeof (Elf32_External_Rela);
+
+      /* Make room for the .rela.plt.unloaded relocations.  */
+      if (!info->shared)
+       htab->srelplt2->size += 3 * sizeof (Elf32_External_Rela);
+
+      return TRUE;
+    }
+
+  /* If a function symbol is defined by a dynamic object, and we do not
+     need a PLT stub for it, the symbol's value should be zero.  */
+  if (h->type == STT_FUNC
+      && h->def_dynamic
+      && h->ref_regular
+      && !h->def_regular)
+    {
+      h->root.u.def.value = 0;
+      return TRUE;
+    }
+
+  /* If this is a weak symbol, and there is a real definition, the
+     processor independent code will have arranged for us to see the
+     real definition first, and we can just use the same value.  */
+  if (h->u.weakdef != NULL)
+    {
+      BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined
+                 || h->u.weakdef->root.type == bfd_link_hash_defweak);
+      h->root.u.def.section = h->u.weakdef->root.u.def.section;
+      h->root.u.def.value = h->u.weakdef->root.u.def.value;
+      return TRUE;
+    }
+
+  /* This is a reference to a symbol defined by a dynamic object which
+     is not a function.  */
+  if (info->shared)
+    return TRUE;
+
+  /* We must allocate the symbol in our .dynbss section, which will
+     become part of the .bss section of the executable.  There will be
+     an entry for this symbol in the .dynsym section.  The dynamic
+     object will contain position independent code, so all references
+     from the dynamic object to this symbol will go through the global
+     offset table.  The dynamic linker will use the .dynsym entry to
+     determine the address it must put in the global offset table, so
+     both the dynamic object and the regular object will refer to the
+     same memory location for the variable.  */
+
+  if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
+    {
+      htab->srelbss->size += sizeof (Elf32_External_Rela);
+      h->needs_copy = 1;
+    }
+
+  return _bfd_elf_adjust_dynamic_copy (h, htab->sdynbss);
+}
 \f
+/* Return the number of dynamic section symbols required by OUTPUT_BFD.
+   The number might be exact or a worst-case estimate, depending on how
+   much information is available to elf_backend_omit_section_dynsym at
+   the current linking stage.  */
+
+static bfd_size_type
+count_section_dynsyms (bfd *output_bfd, struct bfd_link_info *info)
+{
+  bfd_size_type count;
+
+  count = 0;
+  if (info->shared || elf_hash_table (info)->is_relocatable_executable)
+    {
+      asection *p;
+      const struct elf_backend_data *bed;
+
+      bed = get_elf_backend_data (output_bfd);
+      for (p = output_bfd->sections; p ; p = p->next)
+       if ((p->flags & SEC_EXCLUDE) == 0
+           && (p->flags & SEC_ALLOC) != 0
+           && !(*bed->elf_backend_omit_section_dynsym) (output_bfd, info, p))
+         ++count;
+    }
+  return count;
+}
+
 /* This function is called after all the input files have been read,
    and the input sections have been assigned to output sections.  We
    check for any mips16 stub sections that we can discard.  */
@@ -6535,9 +7554,13 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd,
   struct mips_got_info *g;
   int i;
   bfd_size_type loadable_size = 0;
-  bfd_size_type local_gotno;
+  bfd_size_type page_gotno;
+  bfd_size_type dynsymcount;
   bfd *sub;
   struct mips_elf_count_tls_arg count_tls_arg;
+  struct mips_elf_link_hash_table *htab;
+
+  htab = mips_elf_hash_table (info);
 
   /* The .reginfo section has a fixed size.  */
   ri = bfd_get_section_by_name (output_bfd, ".reginfo");
@@ -6591,16 +7614,39 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd,
        relocations, then GLOBAL_GOTSYM will be NULL.  */
     i = 0;
 
+  /* Get a worst-case estimate of the number of dynamic symbols needed.
+     At this point, dynsymcount does not account for section symbols
+     and count_section_dynsyms may overestimate the number that will
+     be needed.  */
+  dynsymcount = (elf_hash_table (info)->dynsymcount
+                + count_section_dynsyms (output_bfd, info));
+
+  /* Determine the size of one stub entry.  */
+  htab->function_stub_size = (dynsymcount > 0x10000
+                             ? MIPS_FUNCTION_STUB_BIG_SIZE
+                             : MIPS_FUNCTION_STUB_NORMAL_SIZE);
+
   /* In the worst case, we'll get one stub per dynamic symbol, plus
      one to account for the dummy entry at the end required by IRIX
      rld.  */
-  loadable_size += MIPS_FUNCTION_STUB_SIZE * (i + 1);
+  loadable_size += htab->function_stub_size * (i + 1);
+
+  if (htab->is_vxworks)
+    /* There's no need to allocate page entries for VxWorks; R_MIPS_GOT16
+       relocations against local symbols evaluate to "G", and the EABI does
+       not include R_MIPS_GOT_PAGE.  */
+    page_gotno = 0;
+  else
+    /* Assume there are two loadable segments consisting of contiguous
+       sections.  Is 5 enough?  */
+    page_gotno = (loadable_size >> 16) + 5;
 
-  /* Assume there are two loadable segments consisting of
-     contiguous sections.  Is 5 enough?  */
-  local_gotno = (loadable_size >> 16) + 5;
+  /* Choose the smaller of the two estimates; both are intended to be
+     conservative.  */
+  if (page_gotno > g->page_gotno)
+    page_gotno = g->page_gotno;
 
-  g->local_gotno += local_gotno;
+  g->local_gotno += page_gotno;
   s->size += g->local_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
 
   g->global_gotno = i;
@@ -6619,9 +7665,12 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd,
 
   mips_elf_resolve_final_got_entries (g);
 
-  if (s->size > MIPS_ELF_GOT_MAX_SIZE (output_bfd))
+  /* VxWorks does not support multiple GOTs.  It initializes $gp to
+     __GOTT_BASE__[__GOTT_INDEX__], the value of which is set by the
+     dynamic loader.  */
+  if (!htab->is_vxworks && s->size > MIPS_ELF_GOT_MAX_SIZE (info))
     {
-      if (! mips_elf_multi_got (output_bfd, info, g, s, local_gotno))
+      if (! mips_elf_multi_got (output_bfd, info, g, s, page_gotno))
        return FALSE;
     }
   else
@@ -6630,6 +7679,7 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd,
       g->tls_assigned_gotno = g->global_gotno + g->local_gotno;
       htab_traverse (g->got_entries, mips_elf_initialize_tls_index, g);
     }
+  htab->computed_got_sizes = TRUE;
 
   return TRUE;
 }
@@ -6641,9 +7691,11 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
                                     struct bfd_link_info *info)
 {
   bfd *dynobj;
-  asection *s;
+  asection *s, *sreldyn;
   bfd_boolean reltext;
+  struct mips_elf_link_hash_table *htab;
 
+  htab = mips_elf_hash_table (info);
   dynobj = elf_hash_table (info)->dynobj;
   BFD_ASSERT (dynobj != NULL);
 
@@ -6665,10 +7717,10 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
      determined the sizes of the various dynamic sections.  Allocate
      memory for them.  */
   reltext = FALSE;
+  sreldyn = NULL;
   for (s = dynobj->sections; s != NULL; s = s->next)
     {
       const char *name;
-      bfd_boolean strip;
 
       /* It's OK to base decisions on the section name, because none
         of the dynobj section names depend upon the input files.  */
@@ -6677,31 +7729,16 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
       if ((s->flags & SEC_LINKER_CREATED) == 0)
        continue;
 
-      strip = FALSE;
-
-      if (strncmp (name, ".rel", 4) == 0)
+      if (CONST_STRNEQ (name, ".rel"))
        {
-         if (s->size == 0)
-           {
-             /* We only strip the section if the output section name
-                 has the same name.  Otherwise, there might be several
-                 input sections for this output section.  FIXME: This
-                 code is probably not needed these days anyhow, since
-                 the linker now does not create empty output sections.  */
-             if (s->output_section != NULL
-                 && strcmp (name,
-                            bfd_get_section_name (s->output_section->owner,
-                                                  s->output_section)) == 0)
-               strip = TRUE;
-           }
-         else
+         if (s->size != 0)
            {
              const char *outname;
              asection *target;
 
              /* If this relocation section applies to a read only
                  section, then we probably need a DT_TEXTREL entry.
-                 If the relocation section is .rel.dyn, we always
+                 If the relocation section is .rel(a).dyn, we always
                  assert a DT_TEXTREL entry rather than testing whether
                  there exists a relocation to a read only section or
                  not.  */
@@ -6711,12 +7748,12 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
              if ((target != NULL
                   && (target->flags & SEC_READONLY) != 0
                   && (target->flags & SEC_ALLOC) != 0)
-                 || strcmp (outname, ".rel.dyn") == 0)
+                 || strcmp (outname, MIPS_ELF_REL_DYN_NAME (info)) == 0)
                reltext = TRUE;
 
              /* We use the reloc_count field as a counter if we need
                 to copy relocs into the output file.  */
-             if (strcmp (name, ".rel.dyn") != 0)
+             if (strcmp (name, MIPS_ELF_REL_DYN_NAME (info)) != 0)
                s->reloc_count = 0;
 
              /* If combreloc is enabled, elf_link_sort_relocs() will
@@ -6728,7 +7765,23 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
              info->combreloc = 0;
            }
        }
-      else if (strncmp (name, ".got", 4) == 0)
+      else if (htab->is_vxworks && strcmp (name, ".got") == 0)
+       {
+         /* Executables do not need a GOT.  */
+         if (info->shared)
+           {
+             /* Allocate relocations for all but the reserved entries.  */
+             struct mips_got_info *g;
+             unsigned int count;
+
+             g = mips_elf_got_info (dynobj, NULL);
+             count = (g->global_gotno
+                      + g->local_gotno
+                      - MIPS_RESERVED_GOTNO (info));
+             mips_elf_allocate_dynamic_relocations (dynobj, info, count);
+           }
+       }
+      else if (!htab->is_vxworks && CONST_STRNEQ (name, ".got"))
        {
          /* _bfd_mips_elf_always_size_sections() has already done
             most of the work, but some symbols may have been mapped
@@ -6773,7 +7826,7 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
                      BFD_ASSERT (g->assigned_gotno == g->next->local_gotno
                                  + g->next->global_gotno
                                  + g->next->tls_gotno
-                                 + MIPS_RESERVED_GOTNO);
+                                 + MIPS_RESERVED_GOTNO (info));
                    }
                }
            }
@@ -6793,40 +7846,65 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
            }
 
          if (needed_relocs)
-           mips_elf_allocate_dynamic_relocations (dynobj, needed_relocs);
+           mips_elf_allocate_dynamic_relocations (dynobj, info,
+                                                  needed_relocs);
        }
       else if (strcmp (name, MIPS_ELF_STUB_SECTION_NAME (output_bfd)) == 0)
        {
          /* IRIX rld assumes that the function stub isn't at the end
-            of .text section. So put a dummy. XXX  */
-         s->size += MIPS_FUNCTION_STUB_SIZE;
+            of .text section.  So put a dummy.  XXX  */
+         s->size += htab->function_stub_size;
        }
       else if (! info->shared
               && ! mips_elf_hash_table (info)->use_rld_obj_head
-              && strncmp (name, ".rld_map", 8) == 0)
+              && CONST_STRNEQ (name, ".rld_map"))
        {
-         /* We add a room for __rld_map. It will be filled in by the
+         /* We add a room for __rld_map.  It will be filled in by the
             rtld to contain a pointer to the _r_debug structure.  */
          s->size += 4;
        }
       else if (SGI_COMPAT (output_bfd)
-              && strncmp (name, ".compact_rel", 12) == 0)
+              && CONST_STRNEQ (name, ".compact_rel"))
        s->size += mips_elf_hash_table (info)->compact_rel_size;
-      else if (strncmp (name, ".init", 5) != 0)
+      else if (! CONST_STRNEQ (name, ".init")
+              && s != htab->sgotplt
+              && s != htab->splt)
        {
          /* It's not one of our sections, so don't allocate space.  */
          continue;
        }
 
-      if (strip)
+      if (s->size == 0)
        {
          s->flags |= SEC_EXCLUDE;
          continue;
        }
 
+      if ((s->flags & SEC_HAS_CONTENTS) == 0)
+       continue;
+
+      /* Allocate memory for this section last, since we may increase its
+        size above.  */
+      if (strcmp (name, MIPS_ELF_REL_DYN_NAME (info)) == 0)
+       {
+         sreldyn = s;
+         continue;
+       }
+
       /* Allocate memory for the section contents.  */
       s->contents = bfd_zalloc (dynobj, s->size);
-      if (s->contents == NULL && s->size != 0)
+      if (s->contents == NULL)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         return FALSE;
+       }
+    }
+
+  /* Allocate memory for the .rel(a).dyn section.  */
+  if (sreldyn != NULL)
+    {
+      sreldyn->contents = bfd_zalloc (dynobj, sreldyn->size);
+      if (sreldyn->contents == NULL)
        {
          bfd_set_error (bfd_error_no_memory);
          return FALSE;
@@ -6838,89 +7916,163 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
       /* Add some entries to the .dynamic section.  We fill in the
         values later, in _bfd_mips_elf_finish_dynamic_sections, but we
         must add the entries now so that we get the correct size for
-        the .dynamic section.  The DT_DEBUG entry is filled in by the
-        dynamic linker and used by the debugger.  */
-      if (! info->shared)
+        the .dynamic section.  */
+
+      /* SGI object has the equivalence of DT_DEBUG in the
+        DT_MIPS_RLD_MAP entry.  This must come first because glibc
+        only fills in DT_MIPS_RLD_MAP (not DT_DEBUG) and GDB only
+        looks at the first one it sees.  */
+      if (!info->shared
+         && !MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_RLD_MAP, 0))
+       return FALSE;
+
+      /* The DT_DEBUG entry may be filled in by the dynamic linker and
+        used by the debugger.  */
+      if (info->executable
+         && !SGI_COMPAT (output_bfd)
+         && !MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_DEBUG, 0))
+       return FALSE;
+
+      if (reltext && (SGI_COMPAT (output_bfd) || htab->is_vxworks))
+       info->flags |= DF_TEXTREL;
+
+      if ((info->flags & DF_TEXTREL) != 0)
        {
-         /* SGI object has the equivalence of DT_DEBUG in the
-            DT_MIPS_RLD_MAP entry.  */
-         if (!MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_RLD_MAP, 0))
+         if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_TEXTREL, 0))
            return FALSE;
-         if (!SGI_COMPAT (output_bfd))
+
+         /* Clear the DF_TEXTREL flag.  It will be set again if we
+            write out an actual text relocation; we may not, because
+            at this point we do not know whether e.g. any .eh_frame
+            absolute relocations have been converted to PC-relative.  */
+         info->flags &= ~DF_TEXTREL;
+       }
+
+      if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_PLTGOT, 0))
+       return FALSE;
+
+      if (htab->is_vxworks)
+       {
+         /* VxWorks uses .rela.dyn instead of .rel.dyn.  It does not
+            use any of the DT_MIPS_* tags.  */
+         if (mips_elf_rel_dyn_section (info, FALSE))
+           {
+             if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELA, 0))
+               return FALSE;
+
+             if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELASZ, 0))
+               return FALSE;
+
+             if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELAENT, 0))
+               return FALSE;
+           }
+         if (htab->splt->size > 0)
            {
-             if (!MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_DEBUG, 0))
+             if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_PLTREL, 0))
+               return FALSE;
+
+             if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_JMPREL, 0))
+               return FALSE;
+
+             if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_PLTRELSZ, 0))
                return FALSE;
            }
        }
       else
        {
-         /* Shared libraries on traditional mips have DT_DEBUG.  */
-         if (!SGI_COMPAT (output_bfd))
+         if (mips_elf_rel_dyn_section (info, FALSE))
            {
-             if (!MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_DEBUG, 0))
+             if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_REL, 0))
+               return FALSE;
+
+             if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELSZ, 0))
+               return FALSE;
+
+             if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELENT, 0))
                return FALSE;
            }
-       }
 
-      if (reltext && SGI_COMPAT (output_bfd))
-       info->flags |= DF_TEXTREL;
+         if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_RLD_VERSION, 0))
+           return FALSE;
 
-      if ((info->flags & DF_TEXTREL) != 0)
-       {
-         if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_TEXTREL, 0))
+         if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_FLAGS, 0))
            return FALSE;
-       }
 
-      if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_PLTGOT, 0))
-       return FALSE;
+         if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_BASE_ADDRESS, 0))
+           return FALSE;
 
-      if (mips_elf_rel_dyn_section (dynobj, FALSE))
-       {
-         if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_REL, 0))
+         if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_LOCAL_GOTNO, 0))
            return FALSE;
 
-         if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELSZ, 0))
+         if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_SYMTABNO, 0))
            return FALSE;
 
-         if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_RELENT, 0))
+         if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_UNREFEXTNO, 0))
            return FALSE;
-       }
 
-      if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_RLD_VERSION, 0))
-       return FALSE;
+         if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_GOTSYM, 0))
+           return FALSE;
 
-      if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_FLAGS, 0))
-       return FALSE;
+         if (IRIX_COMPAT (dynobj) == ict_irix5
+             && ! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_HIPAGENO, 0))
+           return FALSE;
 
-      if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_BASE_ADDRESS, 0))
+         if (IRIX_COMPAT (dynobj) == ict_irix6
+             && (bfd_get_section_by_name
+                 (dynobj, MIPS_ELF_OPTIONS_SECTION_NAME (dynobj)))
+             && !MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_OPTIONS, 0))
+           return FALSE;
+       }
+      if (htab->is_vxworks
+         && !elf_vxworks_add_dynamic_entries (output_bfd, info))
        return FALSE;
+    }
 
-      if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_LOCAL_GOTNO, 0))
-       return FALSE;
+  return TRUE;
+}
+\f
+/* REL is a relocation in INPUT_BFD that is being copied to OUTPUT_BFD.
+   Adjust its R_ADDEND field so that it is correct for the output file.
+   LOCAL_SYMS and LOCAL_SECTIONS are arrays of INPUT_BFD's local symbols
+   and sections respectively; both use symbol indexes.  */
 
-      if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_SYMTABNO, 0))
-       return FALSE;
+static void
+mips_elf_adjust_addend (bfd *output_bfd, struct bfd_link_info *info,
+                       bfd *input_bfd, Elf_Internal_Sym *local_syms,
+                       asection **local_sections, Elf_Internal_Rela *rel)
+{
+  unsigned int r_type, r_symndx;
+  Elf_Internal_Sym *sym;
+  asection *sec;
 
-      if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_UNREFEXTNO, 0))
-       return FALSE;
+  if (mips_elf_local_relocation_p (input_bfd, rel, local_sections, FALSE))
+    {
+      r_type = ELF_R_TYPE (output_bfd, rel->r_info);
+      if (r_type == R_MIPS16_GPREL
+         || r_type == R_MIPS_GPREL16
+         || r_type == R_MIPS_GPREL32
+         || r_type == R_MIPS_LITERAL)
+       {
+         rel->r_addend += _bfd_get_gp_value (input_bfd);
+         rel->r_addend -= _bfd_get_gp_value (output_bfd);
+       }
 
-      if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_GOTSYM, 0))
-       return FALSE;
+      r_symndx = ELF_R_SYM (output_bfd, rel->r_info);
+      sym = local_syms + r_symndx;
 
-      if (IRIX_COMPAT (dynobj) == ict_irix5
-         && ! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_HIPAGENO, 0))
-       return FALSE;
+      /* Adjust REL's addend to account for section merging.  */
+      if (!info->relocatable)
+       {
+         sec = local_sections[r_symndx];
+         _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+       }
 
-      if (IRIX_COMPAT (dynobj) == ict_irix6
-         && (bfd_get_section_by_name
-             (dynobj, MIPS_ELF_OPTIONS_SECTION_NAME (dynobj)))
-         && !MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_OPTIONS, 0))
-       return FALSE;
+      /* This would normally be done by the rela_normal code in elflink.c.  */
+      if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
+       rel->r_addend += local_sections[r_symndx]->output_offset;
     }
-
-  return TRUE;
 }
-\f
+
 /* Relocate a MIPS ELF section.  */
 
 bfd_boolean
@@ -6941,7 +8093,7 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
   for (rel = relocs; rel < relend; ++rel)
     {
       const char *name;
-      bfd_vma value;
+      bfd_vma value = 0;
       reloc_howto_type *howto;
       bfd_boolean require_jalx;
       /* TRUE if the relocation is a RELA relocation, rather than a
@@ -6949,8 +8101,54 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
       bfd_boolean rela_relocation_p = TRUE;
       unsigned int r_type = ELF_R_TYPE (output_bfd, rel->r_info);
       const char *msg;
+      unsigned long r_symndx;
+      asection *sec;
+      Elf_Internal_Shdr *symtab_hdr;
+      struct elf_link_hash_entry *h;
 
       /* Find the relocation howto for this relocation.  */
+      howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd, r_type,
+                                      NEWABI_P (input_bfd)
+                                      && (MIPS_RELOC_RELA_P
+                                          (input_bfd, input_section,
+                                           rel - relocs)));
+
+      r_symndx = ELF_R_SYM (input_bfd, rel->r_info);
+      symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+      if (mips_elf_local_relocation_p (input_bfd, rel, local_sections, FALSE))
+       {
+         sec = local_sections[r_symndx];
+         h = NULL;
+       }
+      else
+       {
+         unsigned long extsymoff;
+
+         extsymoff = 0;
+         if (!elf_bad_symtab (input_bfd))
+           extsymoff = symtab_hdr->sh_info;
+         h = elf_sym_hashes (input_bfd) [r_symndx - extsymoff];
+         while (h->root.type == bfd_link_hash_indirect
+                || h->root.type == bfd_link_hash_warning)
+           h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+         sec = NULL;
+         if (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+           sec = h->root.u.def.section;
+       }
+
+      if (sec != NULL && elf_discarded_section (sec))
+       {
+         /* For relocs against symbols from removed linkonce sections,
+            or sections discarded by a linker script, we just want the
+            section contents zeroed.  Avoid any special processing.  */
+         _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset);
+         rel->r_info = 0;
+         rel->r_addend = 0;
+         continue;
+       }
+
       if (r_type == R_MIPS_64 && ! NEWABI_P (input_bfd))
        {
          /* Some 32-bit code uses R_MIPS_64.  In particular, people use
@@ -6966,152 +8164,60 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
          if (bfd_big_endian (input_bfd))
            rel->r_offset += 4;
        }
-      else
-       /* NewABI defaults to RELA relocations.  */
-       howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd, r_type,
-                                        NEWABI_P (input_bfd)
-                                        && (MIPS_RELOC_RELA_P
-                                            (input_bfd, input_section,
-                                             rel - relocs)));
 
       if (!use_saved_addend_p)
        {
-         Elf_Internal_Shdr *rel_hdr;
-
          /* If these relocations were originally of the REL variety,
             we must pull the addend out of the field that will be
             relocated.  Otherwise, we simply use the contents of the
-            RELA relocation.  To determine which flavor or relocation
-            this is, we depend on the fact that the INPUT_SECTION's
-            REL_HDR is read before its REL_HDR2.  */
-         rel_hdr = &elf_section_data (input_section)->rel_hdr;
-         if ((size_t) (rel - relocs)
-             >= (NUM_SHDR_ENTRIES (rel_hdr) * bed->s->int_rels_per_ext_rel))
-           rel_hdr = elf_section_data (input_section)->rel_hdr2;
-         if (rel_hdr->sh_entsize == MIPS_ELF_REL_SIZE (input_bfd))
+            RELA relocation.  */
+         if (mips_elf_rel_relocation_p (input_bfd, input_section,
+                                        relocs, rel))
            {
-             bfd_byte *location = contents + rel->r_offset;
-
-             /* Note that this is a REL relocation.  */
              rela_relocation_p = FALSE;
-
-             /* Get the addend, which is stored in the input file.  */
-             _bfd_mips16_elf_reloc_unshuffle (input_bfd, r_type, FALSE,
-                                              location);
-             addend = mips_elf_obtain_contents (howto, rel, input_bfd,
-                                                contents);
-             _bfd_mips16_elf_reloc_shuffle(input_bfd, r_type, FALSE,
-                                           location);
-
-             addend &= howto->src_mask;
-
-             /* For some kinds of relocations, the ADDEND is a
-                combination of the addend stored in two different
-                relocations.   */
-             if (r_type == R_MIPS_HI16 || r_type == R_MIPS16_HI16
+             addend = mips_elf_read_rel_addend (input_bfd, rel,
+                                                howto, contents);
+             if (r_type == R_MIPS_HI16
+                 || r_type == R_MIPS16_HI16
                  || (r_type == R_MIPS_GOT16
                      && mips_elf_local_relocation_p (input_bfd, rel,
                                                      local_sections, FALSE)))
                {
-                 bfd_vma l;
-                 const Elf_Internal_Rela *lo16_relocation;
-                 reloc_howto_type *lo16_howto;
-                 bfd_byte *lo16_location;
-                 int lo16_type;
-
-                 if (r_type == R_MIPS16_HI16)
-                   lo16_type = R_MIPS16_LO16;
-                 else
-                   lo16_type = R_MIPS_LO16;
-
-                 /* The combined value is the sum of the HI16 addend,
-                    left-shifted by sixteen bits, and the LO16
-                    addend, sign extended.  (Usually, the code does
-                    a `lui' of the HI16 value, and then an `addiu' of
-                    the LO16 value.)
-
-                    Scan ahead to find a matching LO16 relocation.
-
-                    According to the MIPS ELF ABI, the R_MIPS_LO16
-                    relocation must be immediately following.
-                    However, for the IRIX6 ABI, the next relocation
-                    may be a composed relocation consisting of
-                    several relocations for the same address.  In
-                    that case, the R_MIPS_LO16 relocation may occur
-                    as one of these.  We permit a similar extension
-                    in general, as that is useful for GCC.  */
-                 lo16_relocation = mips_elf_next_relocation (input_bfd,
-                                                             lo16_type,
-                                                             rel, relend);
-                 if (lo16_relocation == NULL)
-                   return FALSE;
-
-                 lo16_location = contents + lo16_relocation->r_offset;
-
-                 /* Obtain the addend kept there.  */
-                 lo16_howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd,
-                                                       lo16_type, FALSE);
-                 _bfd_mips16_elf_reloc_unshuffle (input_bfd, lo16_type, FALSE,
-                                                  lo16_location);
-                 l = mips_elf_obtain_contents (lo16_howto, lo16_relocation,
-                                               input_bfd, contents);
-                 _bfd_mips16_elf_reloc_shuffle (input_bfd, lo16_type, FALSE,
-                                                lo16_location);
-                 l &= lo16_howto->src_mask;
-                 l <<= lo16_howto->rightshift;
-                 l = _bfd_mips_elf_sign_extend (l, 16);
-
-                 addend <<= 16;
-
-                 /* Compute the combined addend.  */
-                 addend += l;
+                 if (!mips_elf_add_lo16_rel_addend (input_bfd, rel, relend,
+                                                    contents, &addend))
+                   {
+                     const char *name;
+
+                     if (h)
+                       name = h->root.root.string;
+                     else
+                       name = bfd_elf_sym_name (input_bfd, symtab_hdr,
+                                                local_syms + r_symndx,
+                                                sec);
+                     (*_bfd_error_handler)
+                       (_("%B: Can't find matching LO16 reloc against `%s' for %s at 0x%lx in section `%A'"),
+                        input_bfd, input_section, name, howto->name,
+                        rel->r_offset);
+                   }
                }
              else
                addend <<= howto->rightshift;
            }
          else
            addend = rel->r_addend;
+         mips_elf_adjust_addend (output_bfd, info, input_bfd,
+                                 local_syms, local_sections, rel);
        }
 
       if (info->relocatable)
        {
-         Elf_Internal_Sym *sym;
-         unsigned long r_symndx;
-
          if (r_type == R_MIPS_64 && ! NEWABI_P (output_bfd)
              && bfd_big_endian (input_bfd))
            rel->r_offset -= 4;
 
-         /* Since we're just relocating, all we need to do is copy
-            the relocations back out to the object file, unless
-            they're against a section symbol, in which case we need
-            to adjust by the section offset, or unless they're GP
-            relative in which case we need to adjust by the amount
-            that we're adjusting GP in this relocatable object.  */
-
-         if (! mips_elf_local_relocation_p (input_bfd, rel, local_sections,
-                                            FALSE))
-           /* There's nothing to do for non-local relocations.  */
-           continue;
-
-         if (r_type == R_MIPS16_GPREL
-             || r_type == R_MIPS_GPREL16
-             || r_type == R_MIPS_GPREL32
-             || r_type == R_MIPS_LITERAL)
-           addend -= (_bfd_get_gp_value (output_bfd)
-                      - _bfd_get_gp_value (input_bfd));
-
-         r_symndx = ELF_R_SYM (output_bfd, rel->r_info);
-         sym = local_syms + r_symndx;
-         if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
-           /* Adjust the addend appropriately.  */
-           addend += local_sections[r_symndx]->output_offset;
-
-         if (rela_relocation_p)
-           /* If this is a RELA relocation, just update the addend.  */
-           rel->r_addend = addend;
-         else
+         if (!rela_relocation_p && rel->r_addend)
            {
+             addend += rel->r_addend;
              if (r_type == R_MIPS_HI16
                  || r_type == R_MIPS_GOT16)
                addend = mips_elf_high (addend);
@@ -7220,7 +8326,21 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
            ;
          else
            {
+             struct mips_elf_link_hash_table *htab;
+
+             htab = mips_elf_hash_table (info);
              BFD_ASSERT (name != NULL);
+             if (!htab->small_data_overflow_reported
+                 && (howto->type == R_MIPS_GPREL16
+                     || howto->type == R_MIPS_LITERAL))
+               {
+                 const char *msg =
+                   _("small-data section exceeds 64KB;"
+                     " lower small-data size limit (see option -G)");
+
+                 htab->small_data_overflow_reported = TRUE;
+                 (*info->callbacks->einfo) ("%P: %s\n", msg);
+               }
              if (! ((*info->callbacks->reloc_overflow)
                     (info, NULL, name, howto->name, (bfd_vma) 0,
                      input_bfd, input_section, rel->r_offset)))
@@ -7362,13 +8482,16 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
   asection *sgot;
   struct mips_got_info *g, *gg;
   const char *name;
+  int idx;
+  struct mips_elf_link_hash_table *htab;
 
+  htab = mips_elf_hash_table (info);
   dynobj = elf_hash_table (info)->dynobj;
 
   if (h->plt.offset != MINUS_ONE)
     {
       asection *s;
-      bfd_byte stub[MIPS_FUNCTION_STUB_SIZE];
+      bfd_byte stub[MIPS_FUNCTION_STUB_BIG_SIZE];
 
       /* This symbol has a stub.  Set it up.  */
 
@@ -7378,18 +8501,42 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
                                   MIPS_ELF_STUB_SECTION_NAME (dynobj));
       BFD_ASSERT (s != NULL);
 
-      /* FIXME: Can h->dynindex be more than 64K?  */
-      if (h->dynindx & 0xffff0000)
+      BFD_ASSERT ((htab->function_stub_size == MIPS_FUNCTION_STUB_BIG_SIZE)
+                  || (h->dynindx <= 0xffff));
+
+      /* Values up to 2^31 - 1 are allowed.  Larger values would cause
+        sign extension at runtime in the stub, resulting in a negative
+        index value.  */
+      if (h->dynindx & ~0x7fffffff)
        return FALSE;
 
       /* Fill the stub.  */
-      bfd_put_32 (output_bfd, STUB_LW (output_bfd), stub);
-      bfd_put_32 (output_bfd, STUB_MOVE (output_bfd), stub + 4);
-      bfd_put_32 (output_bfd, STUB_JALR, stub + 8);
-      bfd_put_32 (output_bfd, STUB_LI16 (output_bfd) + h->dynindx, stub + 12);
+      idx = 0;
+      bfd_put_32 (output_bfd, STUB_LW (output_bfd), stub + idx);
+      idx += 4;
+      bfd_put_32 (output_bfd, STUB_MOVE (output_bfd), stub + idx);
+      idx += 4;
+      if (htab->function_stub_size == MIPS_FUNCTION_STUB_BIG_SIZE)
+        {
+          bfd_put_32 (output_bfd, STUB_LUI ((h->dynindx >> 16) & 0x7fff),
+                      stub + idx);
+          idx += 4;
+        }
+      bfd_put_32 (output_bfd, STUB_JALR, stub + idx);
+      idx += 4;
+
+      /* If a large stub is not required and sign extension is not a
+         problem, then use legacy code in the stub.  */
+      if (htab->function_stub_size == MIPS_FUNCTION_STUB_BIG_SIZE)
+       bfd_put_32 (output_bfd, STUB_ORI (h->dynindx & 0xffff), stub + idx);
+      else if (h->dynindx & ~0x7fff)
+        bfd_put_32 (output_bfd, STUB_LI16U (h->dynindx & 0xffff), stub + idx);
+      else
+        bfd_put_32 (output_bfd, STUB_LI16S (output_bfd, h->dynindx),
+                   stub + idx);
 
       BFD_ASSERT (h->plt.offset <= s->size);
-      memcpy (s->contents + h->plt.offset, stub, MIPS_FUNCTION_STUB_SIZE);
+      memcpy (s->contents + h->plt.offset, stub, htab->function_stub_size);
 
       /* Mark the symbol as undefined.  plt.offset != -1 occurs
         only for the referenced symbol.  */
@@ -7480,7 +8627,7 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
   /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute.  */
   name = h->root.root.string;
   if (strcmp (name, "_DYNAMIC") == 0
-      || strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0)
+      || h == elf_hash_table (info)->hgot)
     sym->st_shndx = SHN_ABS;
   else if (strcmp (name, "_DYNAMIC_LINK") == 0
           || strcmp (name, "_DYNAMIC_LINKING") == 0)
@@ -7514,40 +8661,212 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
        }
       else if (sym->st_shndx != SHN_UNDEF && sym->st_shndx != SHN_ABS)
        {
-         if (h->type == STT_FUNC)
-           sym->st_shndx = SHN_MIPS_TEXT;
-         else if (h->type == STT_OBJECT)
-           sym->st_shndx = SHN_MIPS_DATA;
+         if (h->type == STT_FUNC)
+           sym->st_shndx = SHN_MIPS_TEXT;
+         else if (h->type == STT_OBJECT)
+           sym->st_shndx = SHN_MIPS_DATA;
+       }
+    }
+
+  /* Handle the IRIX6-specific symbols.  */
+  if (IRIX_COMPAT (output_bfd) == ict_irix6)
+    mips_elf_irix6_finish_dynamic_symbol (output_bfd, name, sym);
+
+  if (! info->shared)
+    {
+      if (! mips_elf_hash_table (info)->use_rld_obj_head
+         && (strcmp (name, "__rld_map") == 0
+             || strcmp (name, "__RLD_MAP") == 0))
+       {
+         asection *s = bfd_get_section_by_name (dynobj, ".rld_map");
+         BFD_ASSERT (s != NULL);
+         sym->st_value = s->output_section->vma + s->output_offset;
+         bfd_put_32 (output_bfd, 0, s->contents);
+         if (mips_elf_hash_table (info)->rld_value == 0)
+           mips_elf_hash_table (info)->rld_value = sym->st_value;
+       }
+      else if (mips_elf_hash_table (info)->use_rld_obj_head
+              && strcmp (name, "__rld_obj_head") == 0)
+       {
+         /* IRIX6 does not use a .rld_map section.  */
+         if (IRIX_COMPAT (output_bfd) == ict_irix5
+              || IRIX_COMPAT (output_bfd) == ict_none)
+           BFD_ASSERT (bfd_get_section_by_name (dynobj, ".rld_map")
+                       != NULL);
+         mips_elf_hash_table (info)->rld_value = sym->st_value;
+       }
+    }
+
+  /* If this is a mips16 symbol, force the value to be even.  */
+  if (sym->st_other == STO_MIPS16)
+    sym->st_value &= ~1;
+
+  return TRUE;
+}
+
+/* Likewise, for VxWorks.  */
+
+bfd_boolean
+_bfd_mips_vxworks_finish_dynamic_symbol (bfd *output_bfd,
+                                        struct bfd_link_info *info,
+                                        struct elf_link_hash_entry *h,
+                                        Elf_Internal_Sym *sym)
+{
+  bfd *dynobj;
+  asection *sgot;
+  struct mips_got_info *g;
+  struct mips_elf_link_hash_table *htab;
+
+  htab = mips_elf_hash_table (info);
+  dynobj = elf_hash_table (info)->dynobj;
+
+  if (h->plt.offset != (bfd_vma) -1)
+    {
+      bfd_byte *loc;
+      bfd_vma plt_address, plt_index, got_address, got_offset, branch_offset;
+      Elf_Internal_Rela rel;
+      static const bfd_vma *plt_entry;
+
+      BFD_ASSERT (h->dynindx != -1);
+      BFD_ASSERT (htab->splt != NULL);
+      BFD_ASSERT (h->plt.offset <= htab->splt->size);
+
+      /* Calculate the address of the .plt entry.  */
+      plt_address = (htab->splt->output_section->vma
+                    + htab->splt->output_offset
+                    + h->plt.offset);
+
+      /* Calculate the index of the entry.  */
+      plt_index = ((h->plt.offset - htab->plt_header_size)
+                  / htab->plt_entry_size);
+
+      /* Calculate the address of the .got.plt entry.  */
+      got_address = (htab->sgotplt->output_section->vma
+                    + htab->sgotplt->output_offset
+                    + plt_index * 4);
+
+      /* Calculate the offset of the .got.plt entry from
+        _GLOBAL_OFFSET_TABLE_.  */
+      got_offset = mips_elf_gotplt_index (info, h);
+
+      /* Calculate the offset for the branch at the start of the PLT
+        entry.  The branch jumps to the beginning of .plt.  */
+      branch_offset = -(h->plt.offset / 4 + 1) & 0xffff;
+
+      /* Fill in the initial value of the .got.plt entry.  */
+      bfd_put_32 (output_bfd, plt_address,
+                 htab->sgotplt->contents + plt_index * 4);
+
+      /* Find out where the .plt entry should go.  */
+      loc = htab->splt->contents + h->plt.offset;
+
+      if (info->shared)
+       {
+         plt_entry = mips_vxworks_shared_plt_entry;
+         bfd_put_32 (output_bfd, plt_entry[0] | branch_offset, loc);
+         bfd_put_32 (output_bfd, plt_entry[1] | plt_index, loc + 4);
        }
+      else
+       {
+         bfd_vma got_address_high, got_address_low;
+
+         plt_entry = mips_vxworks_exec_plt_entry;
+         got_address_high = ((got_address + 0x8000) >> 16) & 0xffff;
+         got_address_low = got_address & 0xffff;
+
+         bfd_put_32 (output_bfd, plt_entry[0] | branch_offset, loc);
+         bfd_put_32 (output_bfd, plt_entry[1] | plt_index, loc + 4);
+         bfd_put_32 (output_bfd, plt_entry[2] | got_address_high, loc + 8);
+         bfd_put_32 (output_bfd, plt_entry[3] | got_address_low, loc + 12);
+         bfd_put_32 (output_bfd, plt_entry[4], loc + 16);
+         bfd_put_32 (output_bfd, plt_entry[5], loc + 20);
+         bfd_put_32 (output_bfd, plt_entry[6], loc + 24);
+         bfd_put_32 (output_bfd, plt_entry[7], loc + 28);
+
+         loc = (htab->srelplt2->contents
+                + (plt_index * 3 + 2) * sizeof (Elf32_External_Rela));
+
+         /* Emit a relocation for the .got.plt entry.  */
+         rel.r_offset = got_address;
+         rel.r_info = ELF32_R_INFO (htab->root.hplt->indx, R_MIPS_32);
+         rel.r_addend = h->plt.offset;
+         bfd_elf32_swap_reloca_out (output_bfd, &rel, loc);
+
+         /* Emit a relocation for the lui of %hi(<.got.plt slot>).  */
+         loc += sizeof (Elf32_External_Rela);
+         rel.r_offset = plt_address + 8;
+         rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_MIPS_HI16);
+         rel.r_addend = got_offset;
+         bfd_elf32_swap_reloca_out (output_bfd, &rel, loc);
+
+         /* Emit a relocation for the addiu of %lo(<.got.plt slot>).  */
+         loc += sizeof (Elf32_External_Rela);
+         rel.r_offset += 4;
+         rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_MIPS_LO16);
+         bfd_elf32_swap_reloca_out (output_bfd, &rel, loc);
+       }
+
+      /* Emit an R_MIPS_JUMP_SLOT relocation against the .got.plt entry.  */
+      loc = htab->srelplt->contents + plt_index * sizeof (Elf32_External_Rela);
+      rel.r_offset = got_address;
+      rel.r_info = ELF32_R_INFO (h->dynindx, R_MIPS_JUMP_SLOT);
+      rel.r_addend = 0;
+      bfd_elf32_swap_reloca_out (output_bfd, &rel, loc);
+
+      if (!h->def_regular)
+       sym->st_shndx = SHN_UNDEF;
     }
 
-  /* Handle the IRIX6-specific symbols.  */
-  if (IRIX_COMPAT (output_bfd) == ict_irix6)
-    mips_elf_irix6_finish_dynamic_symbol (output_bfd, name, sym);
+  BFD_ASSERT (h->dynindx != -1 || h->forced_local);
 
-  if (! info->shared)
+  sgot = mips_elf_got_section (dynobj, FALSE);
+  BFD_ASSERT (sgot != NULL);
+  BFD_ASSERT (mips_elf_section_data (sgot) != NULL);
+  g = mips_elf_section_data (sgot)->u.got_info;
+  BFD_ASSERT (g != NULL);
+
+  /* See if this symbol has an entry in the GOT.  */
+  if (g->global_gotsym != NULL
+      && h->dynindx >= g->global_gotsym->dynindx)
     {
-      if (! mips_elf_hash_table (info)->use_rld_obj_head
-         && (strcmp (name, "__rld_map") == 0
-             || strcmp (name, "__RLD_MAP") == 0))
-       {
-         asection *s = bfd_get_section_by_name (dynobj, ".rld_map");
-         BFD_ASSERT (s != NULL);
-         sym->st_value = s->output_section->vma + s->output_offset;
-         bfd_put_32 (output_bfd, 0, s->contents);
-         if (mips_elf_hash_table (info)->rld_value == 0)
-           mips_elf_hash_table (info)->rld_value = sym->st_value;
-       }
-      else if (mips_elf_hash_table (info)->use_rld_obj_head
-              && strcmp (name, "__rld_obj_head") == 0)
-       {
-         /* IRIX6 does not use a .rld_map section.  */
-         if (IRIX_COMPAT (output_bfd) == ict_irix5
-              || IRIX_COMPAT (output_bfd) == ict_none)
-           BFD_ASSERT (bfd_get_section_by_name (dynobj, ".rld_map")
-                       != NULL);
-         mips_elf_hash_table (info)->rld_value = sym->st_value;
-       }
+      bfd_vma offset;
+      Elf_Internal_Rela outrel;
+      bfd_byte *loc;
+      asection *s;
+
+      /* Install the symbol value in the GOT.   */
+      offset = mips_elf_global_got_index (dynobj, output_bfd, h,
+                                         R_MIPS_GOT16, info);
+      MIPS_ELF_PUT_WORD (output_bfd, sym->st_value, sgot->contents + offset);
+
+      /* Add a dynamic relocation for it.  */
+      s = mips_elf_rel_dyn_section (info, FALSE);
+      loc = s->contents + (s->reloc_count++ * sizeof (Elf32_External_Rela));
+      outrel.r_offset = (sgot->output_section->vma
+                        + sgot->output_offset
+                        + offset);
+      outrel.r_info = ELF32_R_INFO (h->dynindx, R_MIPS_32);
+      outrel.r_addend = 0;
+      bfd_elf32_swap_reloca_out (dynobj, &outrel, loc);
+    }
+
+  /* Emit a copy reloc, if needed.  */
+  if (h->needs_copy)
+    {
+      Elf_Internal_Rela rel;
+
+      BFD_ASSERT (h->dynindx != -1);
+
+      rel.r_offset = (h->root.u.def.section->output_section->vma
+                     + h->root.u.def.section->output_offset
+                     + h->root.u.def.value);
+      rel.r_info = ELF32_R_INFO (h->dynindx, R_MIPS_COPY);
+      rel.r_addend = 0;
+      bfd_elf32_swap_reloca_out (output_bfd, &rel,
+                                htab->srelbss->contents
+                                + (htab->srelbss->reloc_count
+                                   * sizeof (Elf32_External_Rela)));
+      ++htab->srelbss->reloc_count;
     }
 
   /* If this is a mips16 symbol, force the value to be even.  */
@@ -7557,6 +8876,96 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
   return TRUE;
 }
 
+/* Install the PLT header for a VxWorks executable and finalize the
+   contents of .rela.plt.unloaded.  */
+
+static void
+mips_vxworks_finish_exec_plt (bfd *output_bfd, struct bfd_link_info *info)
+{
+  Elf_Internal_Rela rela;
+  bfd_byte *loc;
+  bfd_vma got_value, got_value_high, got_value_low, plt_address;
+  static const bfd_vma *plt_entry;
+  struct mips_elf_link_hash_table *htab;
+
+  htab = mips_elf_hash_table (info);
+  plt_entry = mips_vxworks_exec_plt0_entry;
+
+  /* Calculate the value of _GLOBAL_OFFSET_TABLE_.  */
+  got_value = (htab->root.hgot->root.u.def.section->output_section->vma
+              + htab->root.hgot->root.u.def.section->output_offset
+              + htab->root.hgot->root.u.def.value);
+
+  got_value_high = ((got_value + 0x8000) >> 16) & 0xffff;
+  got_value_low = got_value & 0xffff;
+
+  /* Calculate the address of the PLT header.  */
+  plt_address = htab->splt->output_section->vma + htab->splt->output_offset;
+
+  /* Install the PLT header.  */
+  loc = htab->splt->contents;
+  bfd_put_32 (output_bfd, plt_entry[0] | got_value_high, loc);
+  bfd_put_32 (output_bfd, plt_entry[1] | got_value_low, loc + 4);
+  bfd_put_32 (output_bfd, plt_entry[2], loc + 8);
+  bfd_put_32 (output_bfd, plt_entry[3], loc + 12);
+  bfd_put_32 (output_bfd, plt_entry[4], loc + 16);
+  bfd_put_32 (output_bfd, plt_entry[5], loc + 20);
+
+  /* Output the relocation for the lui of %hi(_GLOBAL_OFFSET_TABLE_).  */
+  loc = htab->srelplt2->contents;
+  rela.r_offset = plt_address;
+  rela.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_MIPS_HI16);
+  rela.r_addend = 0;
+  bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+  loc += sizeof (Elf32_External_Rela);
+
+  /* Output the relocation for the following addiu of
+     %lo(_GLOBAL_OFFSET_TABLE_).  */
+  rela.r_offset += 4;
+  rela.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_MIPS_LO16);
+  bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+  loc += sizeof (Elf32_External_Rela);
+
+  /* Fix up the remaining relocations.  They may have the wrong
+     symbol index for _G_O_T_ or _P_L_T_ depending on the order
+     in which symbols were output.  */
+  while (loc < htab->srelplt2->contents + htab->srelplt2->size)
+    {
+      Elf_Internal_Rela rel;
+
+      bfd_elf32_swap_reloca_in (output_bfd, loc, &rel);
+      rel.r_info = ELF32_R_INFO (htab->root.hplt->indx, R_MIPS_32);
+      bfd_elf32_swap_reloca_out (output_bfd, &rel, loc);
+      loc += sizeof (Elf32_External_Rela);
+
+      bfd_elf32_swap_reloca_in (output_bfd, loc, &rel);
+      rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_MIPS_HI16);
+      bfd_elf32_swap_reloca_out (output_bfd, &rel, loc);
+      loc += sizeof (Elf32_External_Rela);
+
+      bfd_elf32_swap_reloca_in (output_bfd, loc, &rel);
+      rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_MIPS_LO16);
+      bfd_elf32_swap_reloca_out (output_bfd, &rel, loc);
+      loc += sizeof (Elf32_External_Rela);
+    }
+}
+
+/* Install the PLT header for a VxWorks shared library.  */
+
+static void
+mips_vxworks_finish_shared_plt (bfd *output_bfd, struct bfd_link_info *info)
+{
+  unsigned int i;
+  struct mips_elf_link_hash_table *htab;
+
+  htab = mips_elf_hash_table (info);
+
+  /* We just need to copy the entry byte-by-byte.  */
+  for (i = 0; i < ARRAY_SIZE (mips_vxworks_shared_plt0_entry); i++)
+    bfd_put_32 (output_bfd, mips_vxworks_shared_plt0_entry[i],
+               htab->splt->contents + i * 4);
+}
+
 /* Finish up the dynamic sections.  */
 
 bfd_boolean
@@ -7567,7 +8976,9 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
   asection *sdyn;
   asection *sgot;
   struct mips_got_info *gg, *g;
+  struct mips_elf_link_hash_table *htab;
 
+  htab = mips_elf_hash_table (info);
   dynobj = elf_hash_table (info)->dynobj;
 
   sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
@@ -7587,6 +8998,7 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       bfd_byte *b;
+      int dyn_to_skip = 0, dyn_skipped = 0;
 
       BFD_ASSERT (sdyn != NULL);
       BFD_ASSERT (g != NULL);
@@ -7610,11 +9022,14 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
          switch (dyn.d_tag)
            {
            case DT_RELENT:
-             s = mips_elf_rel_dyn_section (dynobj, FALSE);
-             BFD_ASSERT (s != NULL);
              dyn.d_un.d_val = MIPS_ELF_REL_SIZE (dynobj);
              break;
 
+           case DT_RELAENT:
+             BFD_ASSERT (htab->is_vxworks);
+             dyn.d_un.d_val = MIPS_ELF_RELA_SIZE (dynobj);
+             break;
+
            case DT_STRSZ:
              /* Rewrite DT_STRSZ.  */
              dyn.d_un.d_val =
@@ -7623,9 +9038,20 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
 
            case DT_PLTGOT:
              name = ".got";
-             s = bfd_get_section_by_name (output_bfd, name);
-             BFD_ASSERT (s != NULL);
-             dyn.d_un.d_ptr = s->vma;
+             if (htab->is_vxworks)
+               {
+                 /* _GLOBAL_OFFSET_TABLE_ is defined to be the beginning
+                    of the ".got" section in DYNOBJ.  */
+                 s = bfd_get_section_by_name (dynobj, name);
+                 BFD_ASSERT (s != NULL);
+                 dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
+               }
+             else
+               {
+                 s = bfd_get_section_by_name (output_bfd, name);
+                 BFD_ASSERT (s != NULL);
+                 dyn.d_un.d_ptr = s->vma;
+               }
              break;
 
            case DT_MIPS_RLD_VERSION:
@@ -7637,7 +9063,11 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
              break;
 
            case DT_MIPS_TIME_STAMP:
-             time ((time_t *) &dyn.d_un.d_val);
+             {
+               time_t t;
+               time (&t);
+               dyn.d_un.d_val = t;
+             }
              break;
 
            case DT_MIPS_ICHECKSUM:
@@ -7687,7 +9117,7 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
              break;
 
            case DT_MIPS_HIPAGENO:
-             dyn.d_un.d_val = g->local_gotno - MIPS_RESERVED_GOTNO;
+             dyn.d_un.d_val = g->local_gotno - MIPS_RESERVED_GOTNO (info);
              break;
 
            case DT_MIPS_RLD_MAP:
@@ -7700,30 +9130,104 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
              dyn.d_un.d_ptr = s->vma;
              break;
 
+           case DT_RELASZ:
+             BFD_ASSERT (htab->is_vxworks);
+             /* The count does not include the JUMP_SLOT relocations.  */
+             if (htab->srelplt)
+               dyn.d_un.d_val -= htab->srelplt->size;
+             break;
+
+           case DT_PLTREL:
+             BFD_ASSERT (htab->is_vxworks);
+             dyn.d_un.d_val = DT_RELA;
+             break;
+
+           case DT_PLTRELSZ:
+             BFD_ASSERT (htab->is_vxworks);
+             dyn.d_un.d_val = htab->srelplt->size;
+             break;
+
+           case DT_JMPREL:
+             BFD_ASSERT (htab->is_vxworks);
+             dyn.d_un.d_val = (htab->srelplt->output_section->vma
+                               + htab->srelplt->output_offset);
+             break;
+
+           case DT_TEXTREL:
+             /* If we didn't need any text relocations after all, delete
+                the dynamic tag.  */
+             if (!(info->flags & DF_TEXTREL))
+               {
+                 dyn_to_skip = MIPS_ELF_DYN_SIZE (dynobj);
+                 swap_out_p = FALSE;
+               }
+             break;
+
+           case DT_FLAGS:
+             /* If we didn't need any text relocations after all, clear
+                DF_TEXTREL from DT_FLAGS.  */
+             if (!(info->flags & DF_TEXTREL))
+               dyn.d_un.d_val &= ~DF_TEXTREL;
+             else
+               swap_out_p = FALSE;
+             break;
+
            default:
              swap_out_p = FALSE;
+             if (htab->is_vxworks
+                 && elf_vxworks_finish_dynamic_entry (output_bfd, &dyn))
+               swap_out_p = TRUE;
              break;
            }
 
-         if (swap_out_p)
+         if (swap_out_p || dyn_skipped)
            (*get_elf_backend_data (dynobj)->s->swap_dyn_out)
-             (dynobj, &dyn, b);
+             (dynobj, &dyn, b - dyn_skipped);
+
+         if (dyn_to_skip)
+           {
+             dyn_skipped += dyn_to_skip;
+             dyn_to_skip = 0;
+           }
        }
+
+      /* Wipe out any trailing entries if we shifted down a dynamic tag.  */
+      if (dyn_skipped > 0)
+       memset (b - dyn_skipped, 0, dyn_skipped);
     }
 
-  /* The first entry of the global offset table will be filled at
-     runtime. The second entry will be used by some runtime loaders.
-     This isn't the case of IRIX rld.  */
-  if (sgot != NULL && sgot->size > 0)
+  if (sgot != NULL && sgot->size > 0
+      && !bfd_is_abs_section (sgot->output_section))
     {
-      MIPS_ELF_PUT_WORD (output_bfd, 0, sgot->contents);
-      MIPS_ELF_PUT_WORD (output_bfd, 0x80000000,
-                        sgot->contents + MIPS_ELF_GOT_SIZE (output_bfd));
-    }
+      if (htab->is_vxworks)
+       {
+         /* The first entry of the global offset table points to the
+            ".dynamic" section.  The second is initialized by the
+            loader and contains the shared library identifier.
+            The third is also initialized by the loader and points
+            to the lazy resolution stub.  */
+         MIPS_ELF_PUT_WORD (output_bfd,
+                            sdyn->output_offset + sdyn->output_section->vma,
+                            sgot->contents);
+         MIPS_ELF_PUT_WORD (output_bfd, 0,
+                            sgot->contents + MIPS_ELF_GOT_SIZE (output_bfd));
+         MIPS_ELF_PUT_WORD (output_bfd, 0,
+                            sgot->contents
+                            + 2 * MIPS_ELF_GOT_SIZE (output_bfd));
+       }
+      else
+       {
+         /* The first entry of the global offset table will be filled at
+            runtime. The second entry will be used by some runtime loaders.
+            This isn't the case of IRIX rld.  */
+         MIPS_ELF_PUT_WORD (output_bfd, (bfd_vma) 0, sgot->contents);
+         MIPS_ELF_PUT_WORD (output_bfd, (bfd_vma) 0x80000000,
+                            sgot->contents + MIPS_ELF_GOT_SIZE (output_bfd));
+       }
 
-  if (sgot != NULL)
-    elf_section_data (sgot->output_section)->this_hdr.sh_entsize
-      = MIPS_ELF_GOT_SIZE (output_bfd);
+      elf_section_data (sgot->output_section)->this_hdr.sh_entsize
+        = MIPS_ELF_GOT_SIZE (output_bfd);
+    }
 
   /* Generate dynamic relocations for the non-primary gots.  */
   if (gg != NULL && gg->next)
@@ -7792,11 +9296,15 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
                 decided not to make.  This is for the n64 irix rld,
                 which doesn't seem to apply any relocations if there
                 are trailing null entries.  */
-             s = mips_elf_rel_dyn_section (dynobj, FALSE);
+             s = mips_elf_rel_dyn_section (info, FALSE);
              dyn.d_un.d_val = (s->reloc_count
                                * (ABI_64_P (output_bfd)
                                   ? sizeof (Elf64_Mips_External_Rel)
                                   : sizeof (Elf32_External_Rel)));
+             /* Adjust the section size too.  Tools like the prelinker
+                can reasonably expect the values to the same.  */
+             elf_section_data (s->output_section)->this_hdr.sh_size
+               = dyn.d_un.d_val;
              break;
 
            default:
@@ -7838,32 +9346,45 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
              {
                file_ptr dummy_offset;
 
-               BFD_ASSERT (s->size >= MIPS_FUNCTION_STUB_SIZE);
-               dummy_offset = s->size - MIPS_FUNCTION_STUB_SIZE;
+               BFD_ASSERT (s->size >= htab->function_stub_size);
+               dummy_offset = s->size - htab->function_stub_size;
                memset (s->contents + dummy_offset, 0,
-                       MIPS_FUNCTION_STUB_SIZE);
+                       htab->function_stub_size);
              }
          }
       }
 
-    /* We need to sort the entries of the dynamic relocation section.  */
-
-    s = mips_elf_rel_dyn_section (dynobj, FALSE);
-
-    if (s != NULL
-       && s->size > (bfd_vma)2 * MIPS_ELF_REL_SIZE (output_bfd))
+    /* The psABI says that the dynamic relocations must be sorted in
+       increasing order of r_symndx.  The VxWorks EABI doesn't require
+       this, and because the code below handles REL rather than RELA
+       relocations, using it for VxWorks would be outright harmful.  */
+    if (!htab->is_vxworks)
       {
-       reldyn_sorting_bfd = output_bfd;
+       s = mips_elf_rel_dyn_section (info, FALSE);
+       if (s != NULL
+           && s->size > (bfd_vma)2 * MIPS_ELF_REL_SIZE (output_bfd))
+         {
+           reldyn_sorting_bfd = output_bfd;
 
-       if (ABI_64_P (output_bfd))
-         qsort ((Elf64_External_Rel *) s->contents + 1, s->reloc_count - 1,
-                sizeof (Elf64_Mips_External_Rel), sort_dynamic_relocs_64);
-       else
-         qsort ((Elf32_External_Rel *) s->contents + 1, s->reloc_count - 1,
-                sizeof (Elf32_External_Rel), sort_dynamic_relocs);
+           if (ABI_64_P (output_bfd))
+             qsort ((Elf64_External_Rel *) s->contents + 1,
+                    s->reloc_count - 1, sizeof (Elf64_Mips_External_Rel),
+                    sort_dynamic_relocs_64);
+           else
+             qsort ((Elf32_External_Rel *) s->contents + 1,
+                    s->reloc_count - 1, sizeof (Elf32_External_Rel),
+                    sort_dynamic_relocs);
+         }
       }
   }
 
+  if (htab->is_vxworks && htab->splt->size > 0)
+    {
+      if (info->shared)
+       mips_vxworks_finish_shared_plt (output_bfd, info);
+      else
+       mips_vxworks_finish_exec_plt (output_bfd, info);
+    }
   return TRUE;
 }
 
@@ -8005,7 +9526,7 @@ _bfd_mips_elf_final_write_processing (bfd *abfd,
          BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
          name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
          BFD_ASSERT (name != NULL
-                     && strncmp (name, ".gptab.", sizeof ".gptab." - 1) == 0);
+                     && CONST_STRNEQ (name, ".gptab."));
          sec = bfd_get_section_by_name (abfd, name + sizeof ".gptab" - 1);
          BFD_ASSERT (sec != NULL);
          (*hdrpp)->sh_info = elf_section_data (sec)->this_idx;
@@ -8015,8 +9536,7 @@ _bfd_mips_elf_final_write_processing (bfd *abfd,
          BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
          name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
          BFD_ASSERT (name != NULL
-                     && strncmp (name, ".MIPS.content",
-                                 sizeof ".MIPS.content" - 1) == 0);
+                     && CONST_STRNEQ (name, ".MIPS.content"));
          sec = bfd_get_section_by_name (abfd,
                                         name + sizeof ".MIPS.content" - 1);
          BFD_ASSERT (sec != NULL);
@@ -8036,13 +9556,12 @@ _bfd_mips_elf_final_write_processing (bfd *abfd,
          BFD_ASSERT ((*hdrpp)->bfd_section != NULL);
          name = bfd_get_section_name (abfd, (*hdrpp)->bfd_section);
          BFD_ASSERT (name != NULL);
-         if (strncmp (name, ".MIPS.events", sizeof ".MIPS.events" - 1) == 0)
+         if (CONST_STRNEQ (name, ".MIPS.events"))
            sec = bfd_get_section_by_name (abfd,
                                           name + sizeof ".MIPS.events" - 1);
          else
            {
-             BFD_ASSERT (strncmp (name, ".MIPS.post_rel",
-                                  sizeof ".MIPS.post_rel" - 1) == 0);
+             BFD_ASSERT (CONST_STRNEQ (name, ".MIPS.post_rel"));
              sec = bfd_get_section_by_name (abfd,
                                             (name
                                              + sizeof ".MIPS.post_rel" - 1));
@@ -8059,7 +9578,8 @@ _bfd_mips_elf_final_write_processing (bfd *abfd,
    segments.  */
 
 int
-_bfd_mips_elf_additional_program_headers (bfd *abfd)
+_bfd_mips_elf_additional_program_headers (bfd *abfd,
+                                         struct bfd_link_info *info ATTRIBUTE_UNUSED)
 {
   asection *s;
   int ret = 0;
@@ -8081,6 +9601,12 @@ _bfd_mips_elf_additional_program_headers (bfd *abfd)
       && bfd_get_section_by_name (abfd, ".mdebug"))
     ++ret;
 
+  /* Allocate a PT_NULL header in dynamic objects.  See
+     _bfd_mips_elf_modify_segment_map for details.  */
+  if (!SGI_COMPAT (abfd)
+      && bfd_get_section_by_name (abfd, ".dynamic"))
+    ++ret;
+
   return ret;
 }
 
@@ -8088,7 +9614,7 @@ _bfd_mips_elf_additional_program_headers (bfd *abfd)
 
 bfd_boolean
 _bfd_mips_elf_modify_segment_map (bfd *abfd,
-                                 struct bfd_link_info *info ATTRIBUTE_UNUSED)
+                                 struct bfd_link_info *info)
 {
   asection *s;
   struct elf_segment_map *m, **pm;
@@ -8150,15 +9676,18 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd,
                     || (*pm)->p_type == PT_INTERP))
            pm = &(*pm)->next;
 
-         amt = sizeof (struct elf_segment_map);
-         options_segment = bfd_zalloc (abfd, amt);
-         options_segment->next = *pm;
-         options_segment->p_type = PT_MIPS_OPTIONS;
-         options_segment->p_flags = PF_R;
-         options_segment->p_flags_valid = TRUE;
-         options_segment->count = 1;
-         options_segment->sections[0] = s;
-         *pm = options_segment;
+         if (*pm == NULL || (*pm)->p_type != PT_MIPS_OPTIONS)
+           {
+             amt = sizeof (struct elf_segment_map);
+             options_segment = bfd_zalloc (abfd, amt);
+             options_segment->next = *pm;
+             options_segment->p_type = PT_MIPS_OPTIONS;
+             options_segment->p_flags = PF_R;
+             options_segment->p_flags_valid = TRUE;
+             options_segment->count = 1;
+             options_segment->sections[0] = s;
+             *pm = options_segment;
+           }
        }
     }
   else
@@ -8228,8 +9757,18 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd,
              m->p_flags_valid = 1;
            }
        }
-      if (m != NULL
-         && m->count == 1 && strcmp (m->sections[0]->name, ".dynamic") == 0)
+      /* GNU/Linux binaries do not need the extended PT_DYNAMIC section.
+        glibc's dynamic linker has traditionally derived the number of
+        tags from the p_filesz field, and sometimes allocates stack
+        arrays of that size.  An overly-big PT_DYNAMIC segment can
+        be actively harmful in such cases.  Making PT_DYNAMIC contain
+        other sections can also make life hard for the prelinker,
+        which might move one of the other sections to a different
+        PT_LOAD segment.  */
+      if (SGI_COMPAT (abfd)
+         && m != NULL
+         && m->count == 1
+         && strcmp (m->sections[0]->name, ".dynamic") == 0)
        {
          static const char *sec_names[] =
          {
@@ -8286,6 +9825,42 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd,
        }
     }
 
+  /* Allocate a spare program header in dynamic objects so that tools
+     like the prelinker can add an extra PT_LOAD entry.
+
+     If the prelinker needs to make room for a new PT_LOAD entry, its
+     standard procedure is to move the first (read-only) sections into
+     the new (writable) segment.  However, the MIPS ABI requires
+     .dynamic to be in a read-only segment, and the section will often
+     start within sizeof (ElfNN_Phdr) bytes of the last program header.
+
+     Although the prelinker could in principle move .dynamic to a
+     writable segment, it seems better to allocate a spare program
+     header instead, and avoid the need to move any sections.
+     There is a long tradition of allocating spare dynamic tags,
+     so allocating a spare program header seems like a natural
+     extension.
+
+     If INFO is NULL, we may be copying an already prelinked binary
+     with objcopy or strip, so do not add this header.  */
+  if (info != NULL
+      && !SGI_COMPAT (abfd)
+      && bfd_get_section_by_name (abfd, ".dynamic"))
+    {
+      for (pm = &elf_tdata (abfd)->segment_map; *pm != NULL; pm = &(*pm)->next)
+       if ((*pm)->p_type == PT_NULL)
+         break;
+      if (*pm == NULL)
+       {
+         m = bfd_zalloc (abfd, sizeof (*m));
+         if (m == NULL)
+           return FALSE;
+
+         m->p_type = PT_NULL;
+         *pm = m;
+       }
+    }
+
   return TRUE;
 }
 \f
@@ -8294,7 +9869,7 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd,
 
 asection *
 _bfd_mips_elf_gc_mark_hook (asection *sec,
-                           struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                           struct bfd_link_info *info,
                            Elf_Internal_Rela *rel,
                            struct elf_link_hash_entry *h,
                            Elf_Internal_Sym *sym)
@@ -8302,32 +9877,14 @@ _bfd_mips_elf_gc_mark_hook (asection *sec,
   /* ??? Do mips16 stub sections need to be handled special?  */
 
   if (h != NULL)
-    {
-      switch (ELF_R_TYPE (sec->owner, rel->r_info))
-       {
-       case R_MIPS_GNU_VTINHERIT:
-       case R_MIPS_GNU_VTENTRY:
-         break;
-
-       default:
-         switch (h->root.type)
-           {
-           case bfd_link_hash_defined:
-           case bfd_link_hash_defweak:
-             return h->root.u.def.section;
-
-           case bfd_link_hash_common:
-             return h->root.u.c.p->section;
-
-           default:
-             break;
-           }
-       }
-    }
-  else
-    return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
+    switch (ELF_R_TYPE (sec->owner, rel->r_info))
+      {
+      case R_MIPS_GNU_VTINHERIT:
+      case R_MIPS_GNU_VTENTRY:
+       return NULL;
+      }
 
-  return NULL;
+  return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
 }
 
 /* Update the got entry reference counts for the section being removed.  */
@@ -8382,13 +9939,13 @@ _bfd_mips_elf_gc_sweep_hook (bfd *abfd ATTRIBUTE_UNUSED,
    _bfd_elf_link_hash_copy_indirect copy the flags for us.  */
 
 void
-_bfd_mips_elf_copy_indirect_symbol (const struct elf_backend_data *bed,
+_bfd_mips_elf_copy_indirect_symbol (struct bfd_link_info *info,
                                    struct elf_link_hash_entry *dir,
                                    struct elf_link_hash_entry *ind)
 {
   struct mips_elf_link_hash_entry *dirmips, *indmips;
 
-  _bfd_elf_link_hash_copy_indirect (bed, dir, ind);
+  _bfd_elf_link_hash_copy_indirect (info, dir, ind);
 
   if (ind->root.type != bfd_link_hash_indirect)
     return;
@@ -8403,8 +9960,6 @@ _bfd_mips_elf_copy_indirect_symbol (const struct elf_backend_data *bed,
 
   if (dirmips->tls_type == 0)
     dirmips->tls_type = indmips->tls_type;
-  else
-    BFD_ASSERT (indmips->tls_type == 0);
 }
 
 void
@@ -8416,6 +9971,7 @@ _bfd_mips_elf_hide_symbol (struct bfd_link_info *info,
   asection *got;
   struct mips_got_info *g;
   struct mips_elf_link_hash_entry *h;
+  struct mips_elf_link_hash_table *htab;
 
   h = (struct mips_elf_link_hash_entry *) entry;
   if (h->forced_local)
@@ -8423,11 +9979,11 @@ _bfd_mips_elf_hide_symbol (struct bfd_link_info *info,
   h->forced_local = force_local;
 
   dynobj = elf_hash_table (info)->dynobj;
-  if (dynobj != NULL && force_local && h->root.type != STT_TLS)
+  htab = mips_elf_hash_table (info);
+  if (dynobj != NULL && force_local && h->root.type != STT_TLS
+      && (got = mips_elf_got_section (dynobj, TRUE)) != NULL
+      && (g = mips_elf_section_data (got)->u.got_info) != NULL)
     {
-      got = mips_elf_got_section (dynobj, FALSE);
-      g = mips_elf_section_data (got)->u.got_info;
-
       if (g->next)
        {
          struct mips_got_entry e;
@@ -8461,20 +10017,30 @@ _bfd_mips_elf_hide_symbol (struct bfd_link_info *info,
              gg->assigned_gotno--;
            }
        }
-      else if (g->global_gotno == 0 && g->global_gotsym == NULL)
-       /* If we haven't got through GOT allocation yet, just bump up the
-          number of local entries, as this symbol won't be counted as
-          global.  */
-       g->local_gotno++;
       else if (h->root.got.offset == 1)
        {
-         /* If we're past non-multi-GOT allocation and this symbol had
-            been marked for a global got entry, give it a local entry
-            instead.  */
-         BFD_ASSERT (g->global_gotno > 0);
+         /* check_relocs didn't know that this symbol would be
+            forced-local, so add an extra local got entry.  */
          g->local_gotno++;
-         g->global_gotno--;
+         if (htab->computed_got_sizes)
+           {
+             /* We'll have treated this symbol as global rather
+                than local.  */
+             BFD_ASSERT (g->global_gotno > 0);
+             g->global_gotno--;
+           }
        }
+      else if (htab->is_vxworks && h->root.needs_plt)
+       {
+         /* check_relocs didn't know that this symbol would be
+            forced-local, so add an extra local got entry.  */
+         g->local_gotno++;
+         if (htab->computed_got_sizes)
+           /* The symbol is only used in call relocations, so we'll
+              have assumed it only needs a .got.plt entry.  Increase
+              the size of .got accordingly.  */
+           got->size += MIPS_ELF_GOT_SIZE (dynobj);
+        }
     }
 
   _bfd_elf_link_hash_hide_symbol (info, &h->root, force_local);
@@ -8550,8 +10116,9 @@ _bfd_mips_elf_ignore_discarded_relocs (asection *sec)
 }
 
 bfd_boolean
-_bfd_mips_elf_write_section (bfd *output_bfd, asection *sec,
-                            bfd_byte *contents)
+_bfd_mips_elf_write_section (bfd *output_bfd,
+                            struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
+                             asection *sec, bfd_byte *contents)
 {
   bfd_byte *to, *from, *end;
   int i;
@@ -8846,7 +10413,7 @@ _bfd_elf_mips_get_relocated_section_contents
                                               input_section, relocatable,
                                               data, gp);
          else
-           r = bfd_perform_relocation (input_bfd, *parent, data, 
+           r = bfd_perform_relocation (input_bfd, *parent, data,
                                        input_section,
                                        relocatable ? abfd : NULL,
                                        &error_message);
@@ -8867,8 +10434,7 @@ _bfd_elf_mips_get_relocated_section_contents
                case bfd_reloc_undefined:
                  if (!((*link_info->callbacks->undefined_symbol)
                        (link_info, bfd_asymbol_name (*(*parent)->sym_ptr_ptr),
-                        input_bfd, input_section, (*parent)->address,
-                        TRUE)))
+                        input_bfd, input_section, (*parent)->address, TRUE)))
                    goto error_return;
                  break;
                case bfd_reloc_dangerous:
@@ -8917,8 +10483,9 @@ _bfd_mips_elf_link_hash_table_create (bfd *abfd)
   if (ret == NULL)
     return NULL;
 
-  if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
-                                      mips_elf_link_hash_newfunc))
+  if (!_bfd_elf_link_hash_table_init (&ret->root, abfd,
+                                     mips_elf_link_hash_newfunc,
+                                     sizeof (struct mips_elf_link_hash_entry)))
     {
       free (ret);
       return NULL;
@@ -8934,9 +10501,39 @@ _bfd_mips_elf_link_hash_table_create (bfd *abfd)
   ret->use_rld_obj_head = FALSE;
   ret->rld_value = 0;
   ret->mips16_stubs_seen = FALSE;
+  ret->computed_got_sizes = FALSE;
+  ret->is_vxworks = FALSE;
+  ret->small_data_overflow_reported = FALSE;
+  ret->srelbss = NULL;
+  ret->sdynbss = NULL;
+  ret->srelplt = NULL;
+  ret->srelplt2 = NULL;
+  ret->sgotplt = NULL;
+  ret->splt = NULL;
+  ret->plt_header_size = 0;
+  ret->plt_entry_size = 0;
+  ret->function_stub_size = 0;
 
   return &ret->root.root;
 }
+
+/* Likewise, but indicate that the target is VxWorks.  */
+
+struct bfd_link_hash_table *
+_bfd_mips_vxworks_link_hash_table_create (bfd *abfd)
+{
+  struct bfd_link_hash_table *ret;
+
+  ret = _bfd_mips_elf_link_hash_table_create (abfd);
+  if (ret)
+    {
+      struct mips_elf_link_hash_table *htab;
+
+      htab = (struct mips_elf_link_hash_table *) ret;
+      htab->is_vxworks = 1;
+    }
+  return ret;
+}
 \f
 /* We need to use a special link routine to handle the .reginfo and
    the .mdebug sections.  We need to merge all instances of these
@@ -8959,6 +10556,7 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   EXTR esym;
   unsigned int i;
   bfd_size_type amt;
+  struct mips_elf_link_hash_table *htab;
 
   static const char * const secname[] =
   {
@@ -8975,6 +10573,7 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info)
      generic size_dynamic_sections renumbered them out from under us.
      Rather than trying somehow to prevent the renumbering, just do
      the sort again.  */
+  htab = mips_elf_hash_table (info);
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       bfd *dynobj;
@@ -8989,18 +10588,7 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info)
         we count the sections after (possibly) removing the .options
         section above.  */
 
-      dynsecsymcount = 0;
-      if (info->shared)
-       {
-         asection * p;
-
-         for (p = abfd->sections; p ; p = p->next)
-           if ((p->flags & SEC_EXCLUDE) == 0
-               && (p->flags & SEC_ALLOC) != 0
-               && !(*bed->elf_backend_omit_section_dynsym) (abfd, info, p))
-             ++ dynsecsymcount;
-       }
-      
+      dynsecsymcount = count_section_dynsyms (abfd, info);
       if (! mips_elf_sort_hash_table (info, dynsecsymcount + 1))
        return FALSE;
 
@@ -9025,6 +10613,14 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info)
        elf_gp (abfd) = (h->u.def.value
                         + h->u.def.section->output_section->vma
                         + h->u.def.section->output_offset);
+      else if (htab->is_vxworks
+              && (h = bfd_link_hash_lookup (info->hash,
+                                            "_GLOBAL_OFFSET_TABLE_",
+                                            FALSE, FALSE, TRUE))
+              && h->type == bfd_link_hash_defined)
+       elf_gp (abfd) = (h->u.def.section->output_section->vma
+                        + h->u.def.section->output_offset
+                        + h->u.def.value);
       else if (info->relocatable)
        {
          bfd_vma lo = MINUS_ONE;
@@ -9036,7 +10632,7 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info)
              lo = o->vma;
 
          /* And calculate GP relative to that.  */
-         elf_gp (abfd) = lo + ELF_MIPS_GP_OFFSET (abfd);
+         elf_gp (abfd) = lo + ELF_MIPS_GP_OFFSET (info);
        }
       else
        {
@@ -9323,7 +10919,7 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info)
          mdebug_sec = o;
        }
 
-      if (strncmp (o->name, ".gptab.", sizeof ".gptab." - 1) == 0)
+      if (CONST_STRNEQ (o->name, ".gptab."))
        {
          const char *subname;
          unsigned int c;
@@ -9660,11 +11256,26 @@ mips_mach_extends_p (unsigned long base, unsigned long extension)
 {
   size_t i;
 
-  for (i = 0; extension != base && i < ARRAY_SIZE (mips_mach_extensions); i++)
+  if (extension == base)
+    return TRUE;
+
+  if (base == bfd_mach_mipsisa32
+      && mips_mach_extends_p (bfd_mach_mipsisa64, extension))
+    return TRUE;
+
+  if (base == bfd_mach_mipsisa32r2
+      && mips_mach_extends_p (bfd_mach_mipsisa64r2, extension))
+    return TRUE;
+
+  for (i = 0; i < ARRAY_SIZE (mips_mach_extensions); i++)
     if (extension == mips_mach_extensions[i].extension)
-      extension = mips_mach_extensions[i].base;
+      {
+       extension = mips_mach_extensions[i].base;
+       if (extension == base)
+         return TRUE;
+      }
 
-  return extension == base;
+  return FALSE;
 }
 
 
@@ -9683,6 +11294,114 @@ mips_32bit_flags_p (flagword flags)
 }
 
 
+/* Merge object attributes from IBFD into OBFD.  Raise an error if
+   there are conflicting attributes.  */
+static bfd_boolean
+mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
+{
+  obj_attribute *in_attr;
+  obj_attribute *out_attr;
+
+  if (!elf_known_obj_attributes_proc (obfd)[0].i)
+    {
+      /* This is the first object.  Copy the attributes.  */
+      _bfd_elf_copy_obj_attributes (ibfd, obfd);
+
+      /* Use the Tag_null value to indicate the attributes have been
+        initialized.  */
+      elf_known_obj_attributes_proc (obfd)[0].i = 1;
+
+      return TRUE;
+    }
+
+  /* Check for conflicting Tag_GNU_MIPS_ABI_FP attributes and merge
+     non-conflicting ones.  */
+  in_attr = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
+  out_attr = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
+  if (in_attr[Tag_GNU_MIPS_ABI_FP].i != out_attr[Tag_GNU_MIPS_ABI_FP].i)
+    {
+      out_attr[Tag_GNU_MIPS_ABI_FP].type = 1;
+      if (out_attr[Tag_GNU_MIPS_ABI_FP].i == 0)
+       out_attr[Tag_GNU_MIPS_ABI_FP].i = in_attr[Tag_GNU_MIPS_ABI_FP].i;
+      else if (in_attr[Tag_GNU_MIPS_ABI_FP].i == 0)
+       ;
+      else if (in_attr[Tag_GNU_MIPS_ABI_FP].i > 3)
+       _bfd_error_handler
+         (_("Warning: %B uses unknown floating point ABI %d"), ibfd,
+          in_attr[Tag_GNU_MIPS_ABI_FP].i);
+      else if (out_attr[Tag_GNU_MIPS_ABI_FP].i > 3)
+       _bfd_error_handler
+         (_("Warning: %B uses unknown floating point ABI %d"), obfd,
+          out_attr[Tag_GNU_MIPS_ABI_FP].i);
+      else
+       switch (out_attr[Tag_GNU_MIPS_ABI_FP].i)
+         {
+         case 1:
+           switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
+             {
+             case 2:
+               _bfd_error_handler
+                 (_("Warning: %B uses -msingle-float, %B uses -mdouble-float"),
+                  obfd, ibfd);
+               break;
+
+             case 3:
+               _bfd_error_handler
+                 (_("Warning: %B uses hard float, %B uses soft float"),
+                  obfd, ibfd);
+               break;
+
+             default:
+               abort ();
+             }
+           break;
+
+         case 2:
+           switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
+             {
+             case 1:
+               _bfd_error_handler
+                 (_("Warning: %B uses -msingle-float, %B uses -mdouble-float"),
+                  ibfd, obfd);
+               break;
+
+             case 3:
+               _bfd_error_handler
+                 (_("Warning: %B uses hard float, %B uses soft float"),
+                  obfd, ibfd);
+               break;
+
+             default:
+               abort ();
+             }
+           break;
+
+         case 3:
+           switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
+             {
+             case 1:
+             case 2:
+               _bfd_error_handler
+                 (_("Warning: %B uses hard float, %B uses soft float"),
+                  ibfd, obfd);
+               break;
+
+             default:
+               abort ();
+             }
+           break;
+
+         default:
+           abort ();
+         }
+    }
+
+  /* Merge Tag_compatibility attributes and any common GNU ones.  */
+  _bfd_elf_merge_object_attributes (ibfd, obfd);
+
+  return TRUE;
+}
+
 /* Merge backend specific data from an object file to the output
    object file when linking.  */
 
@@ -9716,6 +11435,9 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
       return FALSE;
     }
 
+  if (!mips_elf_merge_obj_attributes (ibfd, obfd))
+    return FALSE;
+
   new_flags = elf_elfheader (ibfd)->e_flags;
   elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_NOREORDER;
   old_flags = elf_elfheader (obfd)->e_flags;
@@ -9728,7 +11450,9 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
        = elf_elfheader (ibfd)->e_ident[EI_CLASS];
 
       if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
-         && bfd_get_arch_info (obfd)->the_default)
+         && (bfd_get_arch_info (obfd)->the_default
+             || mips_mach_extends_p (bfd_get_mach (obfd), 
+                                     bfd_get_mach (ibfd))))
        {
          if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
                                   bfd_get_mach (ibfd)))
@@ -9753,6 +11477,12 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
   new_flags &= ~EF_MIPS_UCODE;
   old_flags &= ~EF_MIPS_UCODE;
 
+  /* Don't care about the PIC flags from dynamic objects; they are
+     PIC by design.  */
+  if ((new_flags & (EF_MIPS_PIC | EF_MIPS_CPIC)) != 0
+      && (ibfd->flags & DYNAMIC) != 0)
+    new_flags &= ~ (EF_MIPS_PIC | EF_MIPS_CPIC);
+
   if (new_flags == old_flags)
     return TRUE;
 
@@ -9933,37 +11663,52 @@ _bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr)
     fprintf (file, _(" [no abi set]"));
 
   if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_1)
-    fprintf (file, _(" [mips1]"));
+    fprintf (file, " [mips1]");
   else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_2)
-    fprintf (file, _(" [mips2]"));
+    fprintf (file, " [mips2]");
   else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_3)
-    fprintf (file, _(" [mips3]"));
+    fprintf (file, " [mips3]");
   else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_4)
-    fprintf (file, _(" [mips4]"));
+    fprintf (file, " [mips4]");
   else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_5)
-    fprintf (file, _(" [mips5]"));
+    fprintf (file, " [mips5]");
   else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32)
-    fprintf (file, _(" [mips32]"));
+    fprintf (file, " [mips32]");
   else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_64)
-    fprintf (file, _(" [mips64]"));
+    fprintf (file, " [mips64]");
   else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R2)
-    fprintf (file, _(" [mips32r2]"));
+    fprintf (file, " [mips32r2]");
   else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_64R2)
-    fprintf (file, _(" [mips64r2]"));
+    fprintf (file, " [mips64r2]");
   else
     fprintf (file, _(" [unknown ISA]"));
 
   if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MDMX)
-    fprintf (file, _(" [mdmx]"));
+    fprintf (file, " [mdmx]");
 
   if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_M16)
-    fprintf (file, _(" [mips16]"));
+    fprintf (file, " [mips16]");
 
   if (elf_elfheader (abfd)->e_flags & EF_MIPS_32BITMODE)
-    fprintf (file, _(" [32bitmode]"));
+    fprintf (file, " [32bitmode]");
   else
     fprintf (file, _(" [not 32bitmode]"));
 
+  if (elf_elfheader (abfd)->e_flags & EF_MIPS_NOREORDER)
+    fprintf (file, " [noreorder]");
+
+  if (elf_elfheader (abfd)->e_flags & EF_MIPS_PIC)
+    fprintf (file, " [PIC]");
+
+  if (elf_elfheader (abfd)->e_flags & EF_MIPS_CPIC)
+    fprintf (file, " [CPIC]");
+
+  if (elf_elfheader (abfd)->e_flags & EF_MIPS_XGOT)
+    fprintf (file, " [XGOT]");
+
+  if (elf_elfheader (abfd)->e_flags & EF_MIPS_UCODE)
+    fprintf (file, " [UCODE]");
+
   fputc ('\n', file);
 
   return TRUE;
@@ -9971,29 +11716,50 @@ _bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr)
 
 const struct bfd_elf_special_section _bfd_mips_elf_special_sections[] =
 {
-  { ".lit4",   5,  0, SHT_PROGBITS,   SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
-  { ".lit8",   5,  0, SHT_PROGBITS,   SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
-  { ".mdebug", 7,  0, SHT_MIPS_DEBUG, 0 },
-  { ".sbss",   5, -2, SHT_NOBITS,     SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
-  { ".sdata",  6, -2, SHT_PROGBITS,   SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
-  { ".ucode",  6,  0, SHT_MIPS_UCODE, 0 },
-  { NULL,      0,  0, 0,              0 }
+  { STRING_COMMA_LEN (".lit4"),   0, SHT_PROGBITS,   SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
+  { STRING_COMMA_LEN (".lit8"),   0, SHT_PROGBITS,   SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
+  { STRING_COMMA_LEN (".mdebug"), 0, SHT_MIPS_DEBUG, 0 },
+  { STRING_COMMA_LEN (".sbss"),  -2, SHT_NOBITS,     SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
+  { STRING_COMMA_LEN (".sdata"), -2, SHT_PROGBITS,   SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
+  { STRING_COMMA_LEN (".ucode"),  0, SHT_MIPS_UCODE, 0 },
+  { NULL,                     0,  0, 0,              0 }
 };
 
-const struct bfd_elf_special_section *
-_bfd_mips_elf_get_sec_type_attr (bfd *abfd, asection *sec)
+/* Merge non visibility st_other attributes.  Ensure that the
+   STO_OPTIONAL flag is copied into h->other, even if this is not a
+   definiton of the symbol.  */
+void
+_bfd_mips_elf_merge_symbol_attribute (struct elf_link_hash_entry *h,
+                                     const Elf_Internal_Sym *isym,
+                                     bfd_boolean definition,
+                                     bfd_boolean dynamic ATTRIBUTE_UNUSED)
 {
-  const struct bfd_elf_special_section *ssect;
+  if ((isym->st_other & ~ELF_ST_VISIBILITY (-1)) != 0)
+    {
+      unsigned char other;
 
-  /* See if this is one of the special sections.  */
-  if (sec->name == NULL)
-    return NULL;
+      other = (definition ? isym->st_other : h->other);
+      other &= ~ELF_ST_VISIBILITY (-1);
+      h->other = other | ELF_ST_VISIBILITY (h->other);
+    }
+
+  if (!definition
+      && ELF_MIPS_IS_OPTIONAL (isym->st_other))
+    h->other |= STO_OPTIONAL;
+}
 
-  ssect = _bfd_elf_get_special_section (sec->name,
-                                       _bfd_mips_elf_special_sections,
-                                       sec->use_rela_p);
-  if (ssect != NULL)
-    return ssect;
+/* Decide whether an undefined symbol is special and can be ignored.
+   This is the case for OPTIONAL symbols on IRIX.  */
+bfd_boolean
+_bfd_mips_elf_ignore_undef_symbol (struct elf_link_hash_entry *h)
+{
+  return ELF_MIPS_IS_OPTIONAL (h->other) ? TRUE : FALSE;
+}
 
-  return _bfd_elf_get_sec_type_attr (abfd, sec);
+bfd_boolean
+_bfd_mips_elf_common_definition (Elf_Internal_Sym *sym)
+{
+  return (sym->st_shndx == SHN_COMMON
+         || sym->st_shndx == SHN_MIPS_ACOMMON
+         || sym->st_shndx == SHN_MIPS_SCOMMON);
 }
This page took 0.086699 seconds and 4 git commands to generate.