daily update
[deliverable/binutils-gdb.git] / bfd / elfxx-mips.c
index fedd667c0a0ffe4a1883f65b6c8be66ec81059dc..f51845b7b5e5dbea21cc55496eef509e36cf06f3 100644 (file)
@@ -1,7 +1,5 @@
 /* MIPS-specific support for ELF
-   Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
-   Free Software Foundation, Inc.
+   Copyright 1993-2013 Free Software Foundation, Inc.
 
    Most of the information added by Ian Lance Taylor, Cygnus Support,
    <ian@cygnus.com>.
 
 #include "hashtab.h"
 
+/* Types of TLS GOT entry.  */
+enum mips_got_tls_type {
+  GOT_TLS_NONE,
+  GOT_TLS_GD,
+  GOT_TLS_LDM,
+  GOT_TLS_IE
+};
+
 /* This structure is used to hold information about one GOT entry.
    There are four types of entry:
 
@@ -86,17 +92,41 @@ struct mips_got_entry
     struct mips_elf_link_hash_entry *h;
   } d;
 
-  /* The TLS type of this GOT entry: GOT_NORMAL, GOT_TLS_IE, GOT_TLS_GD
-     or GOT_TLS_LDM.  An LDM GOT entry will be a local symbol entry with
-     r_symndx == 0.  */
+  /* The TLS type of this GOT entry.  An LDM GOT entry will be a local
+     symbol entry with r_symndx == 0.  */
   unsigned char tls_type;
 
+  /* True if we have filled in the GOT contents for a TLS entry,
+     and created the associated relocations.  */
+  unsigned char tls_initialized;
+
   /* The offset from the beginning of the .got section to the entry
      corresponding to this symbol+addend.  If it's a global symbol
      whose offset is yet to be decided, it's going to be -1.  */
   long gotidx;
 };
 
+/* This structure represents a GOT page reference from an input bfd.
+   Each instance represents a symbol + ADDEND, where the representation
+   of the symbol depends on whether it is local to the input bfd.
+   If it is, then SYMNDX >= 0, and the symbol has index SYMNDX in U.ABFD.
+   Otherwise, SYMNDX < 0 and U.H points to the symbol's hash table entry.
+
+   Page references with SYMNDX >= 0 always become page references
+   in the output.  Page references with SYMNDX < 0 only become page
+   references if the symbol binds locally; in other cases, the page
+   reference decays to a global GOT reference.  */
+struct mips_got_page_ref
+{
+  long symndx;
+  union
+  {
+    struct mips_elf_link_hash_entry *h;
+    bfd *abfd;
+  } u;
+  bfd_vma addend;
+};
+
 /* 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.  */
@@ -108,13 +138,11 @@ struct mips_got_page_range
 };
 
 /* This structure describes the range of addends that are applied to page
-   relocations against a given symbol.  */
+   relocations against a given section.  */
 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 section that these entries are based on.  */
+  asection *sec;
   /* The ranges for this page entry.  */
   struct mips_got_page_range *ranges;
   /* The maximum number of page entries needed for RANGES.  */
@@ -144,16 +172,13 @@ struct mips_got_info
   unsigned int assigned_gotno;
   /* A hash table holding members of the got.  */
   struct htab *got_entries;
+  /* A hash table holding mips_got_page_ref structures.  */
+  struct htab *got_page_refs;
   /* A hash table of mips_got_page_entry structures.  */
   struct htab *got_page_entries;
   /* In multi-got links, a pointer to the next got (err, rather, most
      of the time, it points to the previous got).  */
   struct mips_got_info *next;
-  /* This is the GOT index of the TLS LDM entry for the GOT, MINUS_ONE
-     for none, or MINUS_TWO for not yet assigned.  This is needed
-     because a single-GOT link may have multiple hash table entries
-     for the LDM.  It does not get initialized in multi-GOT mode.  */
-  bfd_vma tls_ldm_offset;
 };
 
 /* Structure passed when merging bfds' gots.  */
@@ -292,6 +317,32 @@ struct mips_elf_hash_sort_data
   long max_non_got_dynindx;
 };
 
+/* We make up to two PLT entries if needed, one for standard MIPS code
+   and one for compressed code, either a MIPS16 or microMIPS one.  We
+   keep a separate record of traditional lazy-binding stubs, for easier
+   processing.  */
+
+struct plt_entry
+{
+  /* Traditional SVR4 stub offset, or -1 if none.  */
+  bfd_vma stub_offset;
+
+  /* Standard PLT entry offset, or -1 if none.  */
+  bfd_vma mips_offset;
+
+  /* Compressed PLT entry offset, or -1 if none.  */
+  bfd_vma comp_offset;
+
+  /* The corresponding .got.plt index, or -1 if none.  */
+  bfd_vma gotplt_index;
+
+  /* Whether we need a standard PLT entry.  */
+  unsigned int need_mips : 1;
+
+  /* Whether we need a compressed PLT entry.  */
+  unsigned int need_comp : 1;
+};
+
 /* The MIPS ELF linker needs additional information for each symbol in
    the global hash table.  */
 
@@ -321,23 +372,6 @@ struct mips_elf_link_hash_entry
      being called returns a floating point value.  */
   asection *call_fp_stub;
 
-#define GOT_NORMAL     0
-#define GOT_TLS_GD     1
-#define GOT_TLS_LDM    2
-#define GOT_TLS_IE     4
-#define GOT_TLS_TYPE   7
-#define GOT_TLS_OFFSET_DONE    0x40
-#define GOT_TLS_DONE    0x80
-  unsigned char tls_ie_type;
-  unsigned char tls_gd_type;
-
-  /* These fields are only used in single-GOT mode; in multi-GOT mode there
-     is one mips_got_entry per GOT entry, so the offset is stored
-     there.  In single-GOT mode there may be many mips_got_entry
-     structures all referring to the same GOT slot.  */
-  bfd_vma tls_ie_got_offset;
-  bfd_vma tls_gd_got_offset;
-
   /* The highest GGA_* value that satisfies all references to this symbol.  */
   unsigned int global_got_area : 2;
 
@@ -373,6 +407,9 @@ struct mips_elf_link_hash_entry
   /* Does this symbol need a traditional MIPS lazy-binding stub
      (as opposed to a PLT entry)?  */
   unsigned int needs_lazy_stub : 1;
+
+  /* Does this symbol resolve to a PLT entry?  */
+  unsigned int use_plt_entry : 1;
 };
 
 /* MIPS ELF linker hash table.  */
@@ -400,6 +437,9 @@ struct mips_elf_link_hash_table
   /* True if we can generate copy relocs and PLTs.  */
   bfd_boolean use_plts_and_copy_relocs;
 
+  /* True if we can only use 32-bit microMIPS instructions.  */
+  bfd_boolean insn32;
+
   /* True if we're generating code for VxWorks.  */
   bfd_boolean is_vxworks;
 
@@ -427,8 +467,20 @@ struct mips_elf_link_hash_table
   /* The size of the PLT header in bytes.  */
   bfd_vma plt_header_size;
 
-  /* The size of a PLT entry in bytes.  */
-  bfd_vma plt_entry_size;
+  /* The size of a standard PLT entry in bytes.  */
+  bfd_vma plt_mips_entry_size;
+
+  /* The size of a compressed PLT entry in bytes.  */
+  bfd_vma plt_comp_entry_size;
+
+  /* The offset of the next standard PLT entry to create.  */
+  bfd_vma plt_mips_offset;
+
+  /* The offset of the next compressed PLT entry to create.  */
+  bfd_vma plt_comp_offset;
+
+  /* The index of the next .got.plt entry to create.  */
+  bfd_vma plt_got_index;
 
   /* The number of functions that need a lazy-binding stub.  */
   bfd_vma lazy_stub_count;
@@ -455,6 +507,12 @@ struct mips_elf_link_hash_table
      The function returns the new section on success, otherwise it
      returns null.  */
   asection *(*add_stub_section) (const char *, asection *, asection *);
+
+  /* Small local sym cache.  */
+  struct sym_cache sym_cache;
+
+  /* Is the PLT header compressed?  */
+  unsigned int plt_header_is_comp : 1;
 };
 
 /* Get the MIPS ELF linker hash table from a link_info structure.  */
@@ -484,8 +542,27 @@ struct mips_elf_obj_tdata
   /* Input BFD providing Tag_GNU_MIPS_ABI_FP attribute for output.  */
   bfd *abi_fp_bfd;
 
+  /* Input BFD providing Tag_GNU_MIPS_ABI_MSA attribute for output.  */
+  bfd *abi_msa_bfd;
+
   /* The GOT requirements of input bfds.  */
   struct mips_got_info *got;
+
+  /* Used by _bfd_mips_elf_find_nearest_line.  The structure could be
+     included directly in this one, but there's no point to wasting
+     the memory just for the infrequently called find_nearest_line.  */
+  struct mips_elf_find_line *find_line_info;
+
+  /* An array of stub sections indexed by symbol number.  */
+  asection **local_stubs;
+  asection **local_call_stubs;
+
+  /* The Irix 5 support uses two virtual sections, which represent
+     text/data symbols defined in dynamic objects.  */
+  asymbol *elf_data_symbol;
+  asymbol *elf_text_symbol;
+  asection *elf_data_section;
+  asection *elf_text_section;
 };
 
 /* Get MIPS ELF private object data from BFD's tdata.  */
@@ -708,6 +785,10 @@ static bfd *reldyn_sorting_bfd;
 /* Nonzero if ABFD is using NewABI conventions.  */
 #define NEWABI_P(abfd) (ABI_N32_P (abfd) || ABI_64_P (abfd))
 
+/* Nonzero if ABFD has microMIPS code.  */
+#define MICROMIPS_P(abfd) \
+  ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS) != 0)
+
 /* The IRIX compatibility level we are striving for.  */
 #define IRIX_COMPAT(abfd) \
   (get_elf_backend_data (abfd)->elf_backend_mips_irix_compat (abfd))
@@ -823,8 +904,35 @@ static bfd *reldyn_sorting_bfd;
     ? (0x64180000 + (VAL))     /* daddiu t8,zero,VAL sign extended */  \
     : (0x24180000 + (VAL))))   /* addiu t8,zero,VAL sign extended */
 
+/* Likewise for the microMIPS ASE.  */
+#define STUB_LW_MICROMIPS(abfd)                                                \
+  (ABI_64_P (abfd)                                                     \
+   ? 0xdf3c8010                                        /* ld t9,0x8010(gp) */  \
+   : 0xff3c8010)                               /* lw t9,0x8010(gp) */
+#define STUB_MOVE_MICROMIPS 0x0dff             /* move t7,ra */
+#define STUB_MOVE32_MICROMIPS(abfd)                                    \
+   (ABI_64_P (abfd)                                                    \
+    ? 0x581f7950                               /* daddu t7,ra,zero */  \
+    : 0x001f7950)                              /* addu t7,ra,zero */
+#define STUB_LUI_MICROMIPS(VAL)                                                \
+   (0x41b80000 + (VAL))                                /* lui t8,VAL */
+#define STUB_JALR_MICROMIPS 0x45d9             /* jalr t9 */
+#define STUB_JALR32_MICROMIPS 0x03f90f3c       /* jalr ra,t9 */
+#define STUB_ORI_MICROMIPS(VAL)                                                \
+  (0x53180000 + (VAL))                         /* ori t8,t8,VAL */
+#define STUB_LI16U_MICROMIPS(VAL)                                      \
+  (0x53000000 + (VAL))                         /* ori t8,zero,VAL unsigned */
+#define STUB_LI16S_MICROMIPS(abfd, VAL)                                        \
+   (ABI_64_P (abfd)                                                    \
+    ? 0x5f000000 + (VAL)       /* daddiu t8,zero,VAL sign extended */  \
+    : 0x33000000 + (VAL))      /* addiu t8,zero,VAL sign extended */
+
 #define MIPS_FUNCTION_STUB_NORMAL_SIZE 16
 #define MIPS_FUNCTION_STUB_BIG_SIZE 20
+#define MICROMIPS_FUNCTION_STUB_NORMAL_SIZE 12
+#define MICROMIPS_FUNCTION_STUB_BIG_SIZE 16
+#define MICROMIPS_INSN32_FUNCTION_STUB_NORMAL_SIZE 16
+#define MICROMIPS_INSN32_FUNCTION_STUB_BIG_SIZE 20
 
 /* The name of the dynamic interpreter.  This is put in the .interp
    section.  */
@@ -936,7 +1044,40 @@ static const bfd_vma mips_n64_exec_plt0_entry[] =
   0x2718fffe   /* subu $24, $24, 2                                     */
 };
 
-/* The format of subsequent PLT entries.  */
+/* The format of the microMIPS first PLT entry in an O32 executable.
+   We rely on v0 ($2) rather than t8 ($24) to contain the address
+   of the GOTPLT entry handled, so this stub may only be used when
+   all the subsequent PLT entries are microMIPS code too.
+
+   The trailing NOP is for alignment and correct disassembly only.  */
+static const bfd_vma micromips_o32_exec_plt0_entry[] =
+{
+  0x7980, 0x0000,      /* addiupc $3, (&GOTPLT[0]) - .                 */
+  0xff23, 0x0000,      /* lw $25, 0($3)                                */
+  0x0535,              /* subu $2, $2, $3                              */
+  0x2525,              /* srl $2, $2, 2                                */
+  0x3302, 0xfffe,      /* subu $24, $2, 2                              */
+  0x0dff,              /* move $15, $31                                */
+  0x45f9,              /* jalrs $25                                    */
+  0x0f83,              /* move $28, $3                                 */
+  0x0c00               /* nop                                          */
+};
+
+/* The format of the microMIPS first PLT entry in an O32 executable
+   in the insn32 mode.  */
+static const bfd_vma micromips_insn32_o32_exec_plt0_entry[] =
+{
+  0x41bc, 0x0000,      /* lui $28, %hi(&GOTPLT[0])                     */
+  0xff3c, 0x0000,      /* lw $25, %lo(&GOTPLT[0])($28)                 */
+  0x339c, 0x0000,      /* addiu $28, $28, %lo(&GOTPLT[0])              */
+  0x0398, 0xc1d0,      /* subu $24, $24, $28                           */
+  0x001f, 0x7950,      /* move $15, $31                                */
+  0x0318, 0x1040,      /* srl $24, $24, 2                              */
+  0x03f9, 0x0f3c,      /* jalr $25                                     */
+  0x3318, 0xfffe       /* subu $24, $24, 2                             */
+};
+
+/* The format of subsequent standard PLT entries.  */
 static const bfd_vma mips_exec_plt_entry[] =
 {
   0x3c0f0000,  /* lui $15, %hi(.got.plt entry)                 */
@@ -945,6 +1086,39 @@ static const bfd_vma mips_exec_plt_entry[] =
   0x03200008   /* jr $25                                       */
 };
 
+/* The format of subsequent MIPS16 o32 PLT entries.  We use v0 ($2)
+   and v1 ($3) as temporaries because t8 ($24) and t9 ($25) are not
+   directly addressable.  */
+static const bfd_vma mips16_o32_exec_plt_entry[] =
+{
+  0xb203,              /* lw $2, 12($pc)                       */
+  0x9a60,              /* lw $3, 0($2)                         */
+  0x651a,              /* move $24, $2                         */
+  0xeb00,              /* jr $3                                */
+  0x653b,              /* move $25, $3                         */
+  0x6500,              /* nop                                  */
+  0x0000, 0x0000       /* .word (.got.plt entry)               */
+};
+
+/* The format of subsequent microMIPS o32 PLT entries.  We use v0 ($2)
+   as a temporary because t8 ($24) is not addressable with ADDIUPC.  */
+static const bfd_vma micromips_o32_exec_plt_entry[] =
+{
+  0x7900, 0x0000,      /* addiupc $2, (.got.plt entry) - .     */
+  0xff22, 0x0000,      /* lw $25, 0($2)                        */
+  0x4599,              /* jr $25                               */
+  0x0f02               /* move $24, $2                         */
+};
+
+/* The format of subsequent microMIPS o32 PLT entries in the insn32 mode.  */
+static const bfd_vma micromips_insn32_o32_exec_plt_entry[] =
+{
+  0x41af, 0x0000,      /* lui $15, %hi(.got.plt entry)         */
+  0xff2f, 0x0000,      /* lw $25, %lo(.got.plt entry)($15)     */
+  0x0019, 0x0f3c,      /* jr $25                               */
+  0x330f, 0x0000       /* addiu $24, $15, %lo(.got.plt entry)  */
+};
+
 /* The format of the first PLT entry in a VxWorks executable.  */
 static const bfd_vma mips_vxworks_exec_plt0_entry[] =
 {
@@ -1075,8 +1249,6 @@ mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
       ret->fn_stub = NULL;
       ret->call_stub = NULL;
       ret->call_fp_stub = NULL;
-      ret->tls_ie_type = GOT_NORMAL;
-      ret->tls_gd_type = GOT_NORMAL;
       ret->global_got_area = GGA_NONE;
       ret->got_only_for_calls = TRUE;
       ret->readonly_reloc = FALSE;
@@ -1085,6 +1257,7 @@ mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
       ret->need_fn_stub = FALSE;
       ret->has_nonpic_branches = FALSE;
       ret->needs_lazy_stub = FALSE;
+      ret->use_plt_entry = FALSE;
     }
 
   return (struct bfd_hash_entry *) ret;
@@ -2699,6 +2872,8 @@ mips_elf_output_extsym (struct mips_elf_link_hash_entry *h, void *data)
 
       if (hd->needs_lazy_stub)
        {
+         BFD_ASSERT (hd->root.plt.plist != NULL);
+         BFD_ASSERT (hd->root.plt.plist->stub_offset != MINUS_ONE);
          /* Set type and value for a symbol with a function stub.  */
          h->esym.asym.st = stProc;
          sec = hd->root.root.u.def.section;
@@ -2708,7 +2883,7 @@ mips_elf_output_extsym (struct mips_elf_link_hash_entry *h, void *data)
            {
              output_section = sec->output_section;
              if (output_section != NULL)
-               h->esym.asym.value = (hd->root.plt.offset
+               h->esym.asym.value = (hd->root.plt.plist->stub_offset
                                      + sec->output_offset
                                      + output_section->vma);
              else
@@ -2760,8 +2935,8 @@ mips_elf_got_entry_hash (const void *entry_)
   const struct mips_got_entry *entry = (struct mips_got_entry *)entry_;
 
   return (entry->symndx
-         + (((entry->tls_type & GOT_TLS_TYPE) == GOT_TLS_LDM) << 18)
-         + ((entry->tls_type & GOT_TLS_TYPE) == GOT_TLS_LDM ? 0
+         + ((entry->tls_type == GOT_TLS_LDM) << 18)
+         + (entry->tls_type == GOT_TLS_LDM ? 0
             : !entry->abfd ? mips_elf_hash_bfd_vma (entry->d.address)
             : entry->symndx >= 0 ? (entry->abfd->id
                                     + mips_elf_hash_bfd_vma (entry->d.addend))
@@ -2775,21 +2950,47 @@ mips_elf_got_entry_eq (const void *entry1, const void *entry2)
   const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2;
 
   return (e1->symndx == e2->symndx
-         && (e1->tls_type & GOT_TLS_TYPE) == (e2->tls_type & GOT_TLS_TYPE)
-         && ((e1->tls_type & GOT_TLS_TYPE) == GOT_TLS_LDM ? TRUE
+         && e1->tls_type == e2->tls_type
+         && (e1->tls_type == GOT_TLS_LDM ? TRUE
              : !e1->abfd ? !e2->abfd && e1->d.address == e2->d.address
              : e1->symndx >= 0 ? (e1->abfd == e2->abfd
                                   && e1->d.addend == e2->d.addend)
              : e2->abfd && e1->d.h == e2->d.h));
 }
 
+static hashval_t
+mips_got_page_ref_hash (const void *ref_)
+{
+  const struct mips_got_page_ref *ref;
+
+  ref = (const struct mips_got_page_ref *) ref_;
+  return ((ref->symndx >= 0
+          ? (hashval_t) (ref->u.abfd->id + ref->symndx)
+          : ref->u.h->root.root.root.hash)
+         + mips_elf_hash_bfd_vma (ref->addend));
+}
+
+static int
+mips_got_page_ref_eq (const void *ref1_, const void *ref2_)
+{
+  const struct mips_got_page_ref *ref1, *ref2;
+
+  ref1 = (const struct mips_got_page_ref *) ref1_;
+  ref2 = (const struct mips_got_page_ref *) ref2_;
+  return (ref1->symndx == ref2->symndx
+         && (ref1->symndx < 0
+             ? ref1->u.h == ref2->u.h
+             : ref1->u.abfd == ref2->u.abfd)
+         && ref1->addend == ref2->addend);
+}
+
 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;
+  return entry->sec->id;
 }
 
 static int
@@ -2799,7 +3000,7 @@ mips_got_page_entry_eq (const void *entry1_, const void *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;
+  return entry1->sec == entry2->sec;
 }
 \f
 /* Create and return a new mips_got_info structure.  */
@@ -2813,15 +3014,14 @@ mips_elf_create_got_info (bfd *abfd)
   if (g == NULL)
     return NULL;
 
-  g->tls_ldm_offset = MINUS_ONE;
   g->got_entries = htab_try_create (1, mips_elf_got_entry_hash,
                                    mips_elf_got_entry_eq, NULL);
   if (g->got_entries == NULL)
     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)
+  g->got_page_refs = htab_try_create (1, mips_got_page_ref_hash,
+                                     mips_got_page_ref_eq, NULL);
+  if (g->got_page_refs == NULL)
     return NULL;
 
   return g;
@@ -2858,7 +3058,9 @@ mips_elf_replace_bfd_got (bfd *abfd, struct mips_got_info *g)
       /* The GOT structure itself and the hash table entries are
         allocated to a bfd, but the hash tables aren't.  */
       htab_delete (tdata->got->got_entries);
-      htab_delete (tdata->got->got_page_entries);
+      htab_delete (tdata->got->got_page_refs);
+      if (tdata->got->got_page_entries)
+       htab_delete (tdata->got->got_page_entries);
     }
   tdata->got = g;
 }
@@ -2908,7 +3110,7 @@ mips_elf_reloc_tls_type (unsigned int r_type)
   if (tls_gottprel_reloc_p (r_type))
     return GOT_TLS_IE;
 
-  return GOT_NORMAL;
+  return GOT_TLS_NONE;
 }
 
 /* Return the number of GOT slots needed for GOT TLS type TYPE.  */
@@ -2925,7 +3127,7 @@ mips_tls_got_entries (unsigned int type)
     case GOT_TLS_IE:
       return 1;
 
-    case GOT_NORMAL:
+    case GOT_TLS_NONE:
       return 0;
     }
   abort ();
@@ -2956,7 +3158,7 @@ mips_tls_got_relocs (struct bfd_link_info *info, unsigned char tls_type,
   if (!need_relocs)
     return 0;
 
-  switch (tls_type & GOT_TLS_TYPE)
+  switch (tls_type)
     {
     case GOT_TLS_GD:
       return indx != 0 ? 2 : 1;
@@ -2980,13 +3182,10 @@ mips_elf_count_got_entry (struct bfd_link_info *info,
                          struct mips_got_info *g,
                          struct mips_got_entry *entry)
 {
-  unsigned char tls_type;
-
-  tls_type = entry->tls_type & GOT_TLS_TYPE;
-  if (tls_type)
+  if (entry->tls_type)
     {
-      g->tls_gotno += mips_tls_got_entries (tls_type);
-      g->relocs += mips_tls_got_relocs (info, tls_type,
+      g->tls_gotno += mips_tls_got_entries (entry->tls_type);
+      g->relocs += mips_tls_got_relocs (info, entry->tls_type,
                                        entry->symndx < 0
                                        ? &entry->d.h->root : NULL);
     }
@@ -2996,80 +3195,6 @@ mips_elf_count_got_entry (struct bfd_link_info *info,
     g->global_gotno += 1;
 }
 
-/* A htab_traverse callback.  Count the number of GOT entries and
-   TLS relocations required for the GOT entry in *ENTRYP.  DATA points
-   to a mips_elf_traverse_got_arg structure.  */
-
-static int
-mips_elf_count_got_entries (void **entryp, void *data)
-{
-  struct mips_got_entry *entry;
-  struct mips_elf_traverse_got_arg *arg;
-
-  entry = (struct mips_got_entry *) *entryp;
-  arg = (struct mips_elf_traverse_got_arg *) data;
-  mips_elf_count_got_entry (arg->info, arg->g, entry);
-
-  return 1;
-}
-
-/* A htab_traverse callback.  If *SLOT describes a GOT entry for a local
-   symbol, count the number of GOT entries and TLS relocations that it
-   requires.  DATA points to a mips_elf_traverse_got_arg structure.  */
-
-static int
-mips_elf_count_local_got_entries (void **entryp, void *data)
-{
-  struct mips_got_entry *entry;
-  struct mips_elf_traverse_got_arg *arg;
-
-  entry = (struct mips_got_entry *) *entryp;
-  arg = (struct mips_elf_traverse_got_arg *) data;
-  if (entry->abfd != NULL && entry->symndx != -1)
-    {
-      if ((entry->tls_type & GOT_TLS_TYPE) == GOT_TLS_LDM)
-       {
-         if (arg->g->tls_ldm_offset == MINUS_TWO)
-           return 1;
-         arg->g->tls_ldm_offset = MINUS_TWO;
-       }
-      mips_elf_count_got_entry (arg->info, arg->g, entry);
-    }
-
-  return 1;
-}
-
-/* Count the number of TLS GOT entries and relocationss required for the
-   global (or forced-local) symbol in ARG1.  */
-
-static int
-mips_elf_count_global_tls_entries (void *entry, void *data)
-{
-  struct mips_elf_link_hash_entry *hm;
-  struct mips_elf_traverse_got_arg *arg;
-
-  hm = (struct mips_elf_link_hash_entry *) entry;
-  if (hm->root.root.type == bfd_link_hash_indirect
-      || hm->root.root.type == bfd_link_hash_warning)
-    return 1;
-
-  arg = (struct mips_elf_traverse_got_arg *) data;
-  if (hm->tls_gd_type)
-    {
-      arg->g->tls_gotno += 2;
-      arg->g->relocs += mips_tls_got_relocs (arg->info, hm->tls_gd_type,
-                                            &hm->root);
-    }
-  if (hm->tls_ie_type)
-    {
-      arg->g->tls_gotno += 1;
-      arg->g->relocs += mips_tls_got_relocs (arg->info, hm->tls_ie_type,
-                                            &hm->root);
-    }
-
-  return 1;
-}
-
 /* Output a simple dynamic relocation into SRELOC.  */
 
 static void
@@ -3104,16 +3229,15 @@ mips_elf_output_dynamic_relocation (bfd *output_bfd,
 /* Initialize a set of TLS GOT entries for one symbol.  */
 
 static void
-mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset,
-                              unsigned char *tls_type_p,
-                              struct bfd_link_info *info,
+mips_elf_initialize_tls_slots (bfd *abfd, struct bfd_link_info *info,
+                              struct mips_got_entry *entry,
                               struct mips_elf_link_hash_entry *h,
                               bfd_vma value)
 {
   struct mips_elf_link_hash_table *htab;
   int indx;
   asection *sreloc, *sgot;
-  bfd_vma got_offset2;
+  bfd_vma got_offset, got_offset2;
   bfd_boolean need_relocs = FALSE;
 
   htab = mips_elf_hash_table (info);
@@ -3132,7 +3256,7 @@ mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset,
        indx = h->root.dynindx;
     }
 
-  if (*tls_type_p & GOT_TLS_DONE)
+  if (entry->tls_initialized)
     return;
 
   if ((info->shared || indx != 0)
@@ -3149,8 +3273,9 @@ mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset,
 
   /* Emit necessary relocations.  */
   sreloc = mips_elf_rel_dyn_section (info, FALSE);
+  got_offset = entry->gotidx;
 
-  switch (*tls_type_p & GOT_TLS_TYPE)
+  switch (entry->tls_type)
     {
     case GOT_TLS_GD:
       /* General Dynamic.  */
@@ -3223,37 +3348,7 @@ mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset,
       abort ();
     }
 
-  *tls_type_p |= GOT_TLS_DONE;
-}
-
-/* Return the GOT index to use for a relocation against H using the
-   TLS model in *TLS_TYPE.  The GOT entries for this symbol/model
-   combination start at GOT_INDEX into ABFD's GOT.  This function
-   initializes the GOT entries and corresponding relocations.  */
-
-static bfd_vma
-mips_tls_got_index (bfd *abfd, bfd_vma got_index, unsigned char *tls_type,
-                   struct bfd_link_info *info,
-                   struct mips_elf_link_hash_entry *h, bfd_vma symbol)
-{
-  mips_elf_initialize_tls_slots (abfd, got_index, tls_type, info, h, symbol);
-  return got_index;
-}
-
-/* Return the GOT index to use for a relocation of type R_TYPE against H
-   in ABFD.  */
-
-static bfd_vma
-mips_tls_single_got_index (bfd *abfd, int r_type, struct bfd_link_info *info,
-                          struct mips_elf_link_hash_entry *h, bfd_vma symbol)
-{
-  if (tls_gottprel_reloc_p (r_type))
-    return mips_tls_got_index (abfd, h->tls_ie_got_offset, &h->tls_ie_type,
-                              info, h, symbol);
-  if (tls_gd_reloc_p (r_type))
-    return mips_tls_got_index (abfd, h->tls_gd_got_offset, &h->tls_gd_type,
-                              info, h, symbol);
-  abort ();
+  entry->tls_initialized = TRUE;
 }
 
 /* Return the offset from _GLOBAL_OFFSET_TABLE_ of the .got.plt entry
@@ -3264,25 +3359,20 @@ 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;
+  bfd_vma got_address, got_value;
   struct mips_elf_link_hash_table *htab;
 
   htab = mips_elf_hash_table (info);
   BFD_ASSERT (htab != NULL);
 
-  BFD_ASSERT (h->plt.offset != (bfd_vma) -1);
-
-  /* This function only works for VxWorks, because a non-VxWorks .got.plt
-     section starts with reserved entries.  */
-  BFD_ASSERT (htab->is_vxworks);
-
-  /* Calculate the index of the symbol's PLT entry.  */
-  plt_index = (h->plt.offset - htab->plt_header_size) / htab->plt_entry_size;
+  BFD_ASSERT (h->plt.plist != NULL);
+  BFD_ASSERT (h->plt.plist->gotplt_index != MINUS_ONE);
 
   /* Calculate the address of the associated .got.plt entry.  */
   got_address = (htab->sgotplt->output_section->vma
                 + htab->sgotplt->output_offset
-                + plt_index * 4);
+                + (h->plt.plist->gotplt_index
+                   * MIPS_ELF_GOT_SIZE (info->output_bfd)));
 
   /* Calculate the value of _GLOBAL_OFFSET_TABLE_.  */
   got_value = (htab->root.hgot->root.u.def.section->output_section->vma
@@ -3314,17 +3404,8 @@ mips_elf_local_got_index (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
     return MINUS_ONE;
 
   if (entry->tls_type)
-    {
-      if (entry->symndx == -1 && htab->got_info->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_single_got_index (abfd, r_type, info, h, value);
-      else
-       return mips_tls_got_index (abfd, entry->gotidx, &entry->tls_type,
-                                  info, h, value);
-    }
-  else
-    return entry->gotidx;
+    mips_elf_initialize_tls_slots (abfd, info, entry, h, value);
+  return entry->gotidx;
 }
 
 /* Return the GOT index of global symbol H in the primary GOT.  */
@@ -3366,54 +3447,31 @@ mips_elf_global_got_index (bfd *obfd, struct bfd_link_info *info, bfd *ibfd,
                           struct elf_link_hash_entry *h, int r_type)
 {
   struct mips_elf_link_hash_table *htab;
-  bfd_vma got_index;
-  struct mips_got_info *g, *gg;
+  struct mips_got_info *g;
+  struct mips_got_entry lookup, *entry;
+  bfd_vma gotidx;
 
   htab = mips_elf_hash_table (info);
   BFD_ASSERT (htab != NULL);
 
-  gg = g = htab->got_info;
-  if (g->next && ibfd)
-    {
-      struct mips_got_entry e, *p;
-
-      BFD_ASSERT (h->dynindx >= 0);
-
-      g = mips_elf_bfd_got (ibfd, FALSE);
-      BFD_ASSERT (g);
-      if (g->next != gg || TLS_RELOC_P (r_type))
-       {
-         e.abfd = ibfd;
-         e.symndx = -1;
-         e.d.h = (struct mips_elf_link_hash_entry *)h;
-         e.tls_type = mips_elf_reloc_tls_type (r_type);
+  g = mips_elf_bfd_got (ibfd, FALSE);
+  BFD_ASSERT (g);
 
-         p = htab_find (g->got_entries, &e);
+  lookup.tls_type = mips_elf_reloc_tls_type (r_type);
+  if (!lookup.tls_type && g == mips_elf_bfd_got (obfd, FALSE))
+    return mips_elf_primary_global_got_index (obfd, info, h);
 
-         BFD_ASSERT (p && p->gotidx > 0);
+  lookup.abfd = ibfd;
+  lookup.symndx = -1;
+  lookup.d.h = (struct mips_elf_link_hash_entry *) h;
+  entry = htab_find (g->got_entries, &lookup);
+  BFD_ASSERT (entry);
 
-         if (p->tls_type)
-           {
-             bfd_vma value = MINUS_ONE;
-             if ((h->root.type == bfd_link_hash_defined
-                  || h->root.type == bfd_link_hash_defweak)
-                 && h->root.u.def.section->output_section)
-               value = (h->root.u.def.value
-                        + h->root.u.def.section->output_offset
-                        + h->root.u.def.section->output_section->vma);
-
-             return mips_tls_got_index (obfd, p->gotidx, &p->tls_type,
-                                        info, e.d.h, value);
-           }
-         else
-           return p->gotidx;
-       }
-    }
+  gotidx = entry->gotidx;
+  BFD_ASSERT (gotidx > 0 && gotidx < htab->sgot->size);
 
-  if (TLS_RELOC_P (r_type))
+  if (lookup.tls_type)
     {
-      struct mips_elf_link_hash_entry *hm
-       = (struct mips_elf_link_hash_entry *) h;
       bfd_vma value = MINUS_ONE;
 
       if ((h->root.type == bfd_link_hash_defined
@@ -3423,13 +3481,9 @@ mips_elf_global_got_index (bfd *obfd, struct bfd_link_info *info, bfd *ibfd,
                 + h->root.u.def.section->output_offset
                 + h->root.u.def.section->output_section->vma);
 
-      got_index = mips_tls_single_got_index (obfd, r_type, info, hm, value);
+      mips_elf_initialize_tls_slots (obfd, info, entry, lookup.d.h, value);
     }
-  else
-    got_index = mips_elf_primary_global_got_index (obfd, info, h);
-  BFD_ASSERT (got_index < htab->sgot->size);
-
-  return got_index;
+  return gotidx;
 }
 
 /* Find a GOT page entry that points to within 32KB of VALUE.  These
@@ -3521,18 +3575,15 @@ mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info,
                                 struct mips_elf_link_hash_entry *h,
                                 int r_type)
 {
-  struct mips_got_entry entry, **loc;
+  struct mips_got_entry lookup, *entry;
+  void **loc;
   struct mips_got_info *g;
   struct mips_elf_link_hash_table *htab;
+  bfd_vma gotidx;
 
   htab = mips_elf_hash_table (info);
   BFD_ASSERT (htab != NULL);
 
-  entry.abfd = NULL;
-  entry.symndx = -1;
-  entry.d.address = value;
-  entry.tls_type = mips_elf_reloc_tls_type (r_type);
-
   g = mips_elf_bfd_got (ibfd, FALSE);
   if (g == NULL)
     {
@@ -3543,48 +3594,49 @@ mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info,
   /* This function shouldn't be called for symbols that live in the global
      area of the GOT.  */
   BFD_ASSERT (h == NULL || h->global_got_area == GGA_NONE);
-  if (entry.tls_type)
-    {
-      struct mips_got_entry *p;
 
-      entry.abfd = ibfd;
+  lookup.tls_type = mips_elf_reloc_tls_type (r_type);
+  if (lookup.tls_type)
+    {
+      lookup.abfd = ibfd;
       if (tls_ldm_reloc_p (r_type))
        {
-         entry.symndx = 0;
-         entry.d.addend = 0;
+         lookup.symndx = 0;
+         lookup.d.addend = 0;
        }
       else if (h == NULL)
        {
-         entry.symndx = r_symndx;
-         entry.d.addend = 0;
+         lookup.symndx = r_symndx;
+         lookup.d.addend = 0;
        }
       else
-       entry.d.h = h;
-
-      p = (struct mips_got_entry *)
-       htab_find (g->got_entries, &entry);
-
-      BFD_ASSERT (p);
-      return p;
-    }
+       {
+         lookup.symndx = -1;
+         lookup.d.h = h;
+       }
 
-  loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
-                                                  INSERT);
-  if (*loc)
-    return *loc;
+      entry = (struct mips_got_entry *) htab_find (g->got_entries, &lookup);
+      BFD_ASSERT (entry);
 
-  entry.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno++;
+      gotidx = entry->gotidx;
+      BFD_ASSERT (gotidx > 0 && gotidx < htab->sgot->size);
 
-  *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
+      return entry;
+    }
 
-  if (! *loc)
+  lookup.abfd = NULL;
+  lookup.symndx = -1;
+  lookup.d.address = value;
+  loc = htab_find_slot (g->got_entries, &lookup, INSERT);
+  if (!loc)
     return NULL;
 
-  memcpy (*loc, &entry, sizeof entry);
+  entry = (struct mips_got_entry *) *loc;
+  if (entry)
+    return 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.  */
       (*_bfd_error_handler)
        (_("not enough GOT space for local GOT entries"));
@@ -3592,8 +3644,15 @@ mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info,
       return NULL;
     }
 
-  MIPS_ELF_PUT_WORD (abfd, value,
-                    (htab->sgot->contents + entry.gotidx));
+  entry = (struct mips_got_entry *) bfd_alloc (abfd, sizeof (*entry));
+  if (!entry)
+    return NULL;
+
+  lookup.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno++;
+  *entry = lookup;
+  *loc = entry;
+
+  MIPS_ELF_PUT_WORD (abfd, value, htab->sgot->contents + entry->gotidx);
 
   /* These GOT entries need a dynamic relocation on VxWorks.  */
   if (htab->is_vxworks)
@@ -3606,7 +3665,7 @@ mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info,
       s = mips_elf_rel_dyn_section (info, FALSE);
       got_address = (htab->sgot->output_section->vma
                     + htab->sgot->output_offset
-                    + entry.gotidx);
+                    + entry->gotidx);
 
       rloc = s->contents + (s->reloc_count++ * sizeof (Elf32_External_Rela));
       outrel.r_offset = got_address;
@@ -3615,7 +3674,7 @@ mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info,
       bfd_elf32_swap_reloca_out (abfd, &outrel, rloc);
     }
 
-  return *loc;
+  return entry;
 }
 
 /* Return the number of dynamic section symbols required by OUTPUT_BFD.
@@ -3752,6 +3811,7 @@ mips_elf_record_got_entry (struct bfd_link_info *info, bfd *abfd,
       if (!entry)
        return FALSE;
 
+      lookup->tls_initialized = FALSE;
       lookup->gotidx = -1;
       *entry = *lookup;
       *loc = entry;
@@ -3808,12 +3868,8 @@ mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
     }
 
   tls_type = mips_elf_reloc_tls_type (r_type);
-  if (tls_type == GOT_NORMAL && hmips->global_got_area > GGA_NORMAL)
+  if (tls_type == GOT_TLS_NONE && hmips->global_got_area > GGA_NORMAL)
     hmips->global_got_area = GGA_NORMAL;
-  else if (tls_type == GOT_TLS_IE && hmips->tls_ie_type == 0)
-    hmips->tls_ie_type = tls_type;
-  else if (tls_type == GOT_TLS_GD && hmips->tls_gd_type == 0)
-    hmips->tls_gd_type = tls_type;
 
   entry.abfd = abfd;
   entry.symndx = -1;
@@ -3846,30 +3902,18 @@ mips_elf_record_local_got_symbol (bfd *abfd, long symndx, bfd_vma addend,
   return mips_elf_record_got_entry (info, abfd, &entry);
 }
 
-/* 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.
-
-   This function creates an upper bound on the number of GOT slots
-   required; no attempt is made to combine references to non-overridable
-   global symbols across multiple input files.  */
+/* Record that ABFD has a page relocation against SYMNDX + ADDEND.
+   H is the symbol's hash table entry, or null if SYMNDX is local
+   to ABFD.  */
 
 static bfd_boolean
-mips_elf_record_got_page_entry (struct bfd_link_info *info, bfd *abfd,
-                               long symndx, bfd_signed_vma addend)
+mips_elf_record_got_page_ref (struct bfd_link_info *info, bfd *abfd,
+                             long symndx, struct elf_link_hash_entry *h,
+                             bfd_signed_vma addend)
 {
   struct mips_elf_link_hash_table *htab;
   struct mips_got_info *g1, *g2;
-  struct mips_got_page_entry lookup, *entry;
-  struct mips_got_page_range **range_ptr, *range;
-  bfd_vma old_pages, new_pages;
+  struct mips_got_page_ref lookup, *entry;
   void **loc, **bfd_loc;
 
   htab = mips_elf_hash_table (info);
@@ -3878,26 +3922,29 @@ mips_elf_record_got_page_entry (struct bfd_link_info *info, bfd *abfd,
   g1 = htab->got_info;
   BFD_ASSERT (g1 != NULL);
 
-  /* Find the mips_got_page_entry hash table entry for this symbol.  */
-  lookup.abfd = abfd;
-  lookup.symndx = symndx;
-  loc = htab_find_slot (g1->got_page_entries, &lookup, INSERT);
+  if (h)
+    {
+      lookup.symndx = -1;
+      lookup.u.h = (struct mips_elf_link_hash_entry *) h;
+    }
+  else
+    {
+      lookup.symndx = symndx;
+      lookup.u.abfd = abfd;
+    }
+  lookup.addend = addend;
+  loc = htab_find_slot (g1->got_page_refs, &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;
+  entry = (struct mips_got_page_ref *) *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;
+      *entry = lookup;
       *loc = entry;
     }
 
@@ -3906,13 +3953,170 @@ mips_elf_record_got_page_entry (struct bfd_link_info *info, bfd *abfd,
   if (!g2)
     return FALSE;
 
-  bfd_loc = htab_find_slot (g2->got_page_entries, &lookup, INSERT);
+  bfd_loc = htab_find_slot (g2->got_page_refs, &lookup, INSERT);
   if (!bfd_loc)
     return FALSE;
 
   if (!*bfd_loc)
     *bfd_loc = entry;
 
+  return TRUE;
+}
+
+/* Add room for N relocations to the .rel(a).dyn section in ABFD.  */
+
+static void
+mips_elf_allocate_dynamic_relocations (bfd *abfd, struct bfd_link_info *info,
+                                      unsigned int n)
+{
+  asection *s;
+  struct mips_elf_link_hash_table *htab;
+
+  htab = mips_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
+
+  s = mips_elf_rel_dyn_section (info, FALSE);
+  BFD_ASSERT (s != NULL);
+
+  if (htab->is_vxworks)
+    s->size += n * MIPS_ELF_RELA_SIZE (abfd);
+  else
+    {
+      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);
+    }
+}
+\f
+/* A htab_traverse callback for GOT entries, with DATA pointing to a
+   mips_elf_traverse_got_arg structure.  Count the number of GOT
+   entries and TLS relocs.  Set DATA->value to true if we need
+   to resolve indirect or warning symbols and then recreate the GOT.  */
+
+static int
+mips_elf_check_recreate_got (void **entryp, void *data)
+{
+  struct mips_got_entry *entry;
+  struct mips_elf_traverse_got_arg *arg;
+
+  entry = (struct mips_got_entry *) *entryp;
+  arg = (struct mips_elf_traverse_got_arg *) data;
+  if (entry->abfd != NULL && entry->symndx == -1)
+    {
+      struct mips_elf_link_hash_entry *h;
+
+      h = entry->d.h;
+      if (h->root.root.type == bfd_link_hash_indirect
+         || h->root.root.type == bfd_link_hash_warning)
+       {
+         arg->value = TRUE;
+         return 0;
+       }
+    }
+  mips_elf_count_got_entry (arg->info, arg->g, entry);
+  return 1;
+}
+
+/* A htab_traverse callback for GOT entries, with DATA pointing to a
+   mips_elf_traverse_got_arg structure.  Add all entries to DATA->g,
+   converting entries for indirect and warning symbols into entries
+   for the target symbol.  Set DATA->g to null on error.  */
+
+static int
+mips_elf_recreate_got (void **entryp, void *data)
+{
+  struct mips_got_entry new_entry, *entry;
+  struct mips_elf_traverse_got_arg *arg;
+  void **slot;
+
+  entry = (struct mips_got_entry *) *entryp;
+  arg = (struct mips_elf_traverse_got_arg *) data;
+  if (entry->abfd != NULL
+      && entry->symndx == -1
+      && (entry->d.h->root.root.type == bfd_link_hash_indirect
+         || entry->d.h->root.root.type == bfd_link_hash_warning))
+    {
+      struct mips_elf_link_hash_entry *h;
+
+      new_entry = *entry;
+      entry = &new_entry;
+      h = entry->d.h;
+      do
+       {
+         BFD_ASSERT (h->global_got_area == GGA_NONE);
+         h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+       }
+      while (h->root.root.type == bfd_link_hash_indirect
+            || h->root.root.type == bfd_link_hash_warning);
+      entry->d.h = h;
+    }
+  slot = htab_find_slot (arg->g->got_entries, entry, INSERT);
+  if (slot == NULL)
+    {
+      arg->g = NULL;
+      return 0;
+    }
+  if (*slot == NULL)
+    {
+      if (entry == &new_entry)
+       {
+         entry = bfd_alloc (entry->abfd, sizeof (*entry));
+         if (!entry)
+           {
+             arg->g = NULL;
+             return 0;
+           }
+         *entry = new_entry;
+       }
+      *slot = entry;
+      mips_elf_count_got_entry (arg->info, arg->g, entry);
+    }
+  return 1;
+}
+
+/* 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 G requires a page entry that can reach SEC + ADDEND.  */
+
+static bfd_boolean
+mips_elf_record_got_page_entry (struct mips_elf_traverse_got_arg *arg,
+                               asection *sec, bfd_signed_vma addend)
+{
+  struct mips_got_info *g = arg->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 section.  */
+  lookup.sec = sec;
+  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 section.  */
+  entry = (struct mips_got_page_entry *) *loc;
+  if (!entry)
+    {
+      entry = bfd_zalloc (arg->info->output_bfd, sizeof (*entry));
+      if (!entry)
+       return FALSE;
+
+      entry->sec = sec;
+      *loc = entry;
+    }
+
   /* Skip over ranges whose maximum extent cannot share a page entry
      with ADDEND.  */
   range_ptr = &entry->ranges;
@@ -3925,7 +4129,7 @@ mips_elf_record_got_page_entry (struct bfd_link_info *info, bfd *abfd,
   range = *range_ptr;
   if (!range || addend < range->min_addend - 0xffff)
     {
-      range = bfd_alloc (abfd, sizeof (*range));
+      range = bfd_zalloc (arg->info->output_bfd, sizeof (*range));
       if (!range)
        return FALSE;
 
@@ -3935,8 +4139,7 @@ mips_elf_record_got_page_entry (struct bfd_link_info *info, bfd *abfd,
 
       *range_ptr = range;
       entry->num_pages++;
-      g1->page_gotno++;
-      g2->page_gotno++;
+      g->page_gotno++;
       return TRUE;
     }
 
@@ -3963,153 +4166,178 @@ mips_elf_record_got_page_entry (struct bfd_link_info *info, bfd *abfd,
   if (old_pages != new_pages)
     {
       entry->num_pages += new_pages - old_pages;
-      g1->page_gotno += new_pages - old_pages;
-      g2->page_gotno += new_pages - old_pages;
+      g->page_gotno += new_pages - old_pages;
     }
 
   return TRUE;
 }
 
-/* Add room for N relocations to the .rel(a).dyn section in ABFD.  */
+/* A htab_traverse callback for which *REFP points to a mips_got_page_ref
+   and for which DATA points to a mips_elf_traverse_got_arg.  Work out
+   whether the page reference described by *REFP needs a GOT page entry,
+   and record that entry in DATA->g if so.  Set DATA->g to null on failure.  */
 
-static void
-mips_elf_allocate_dynamic_relocations (bfd *abfd, struct bfd_link_info *info,
-                                      unsigned int n)
+static bfd_boolean
+mips_elf_resolve_got_page_ref (void **refp, void *data)
 {
-  asection *s;
+  struct mips_got_page_ref *ref;
+  struct mips_elf_traverse_got_arg *arg;
   struct mips_elf_link_hash_table *htab;
+  asection *sec;
+  bfd_vma addend;
 
-  htab = mips_elf_hash_table (info);
-  BFD_ASSERT (htab != NULL);
-
-  s = mips_elf_rel_dyn_section (info, FALSE);
-  BFD_ASSERT (s != NULL);
+  ref = (struct mips_got_page_ref *) *refp;
+  arg = (struct mips_elf_traverse_got_arg *) data;
+  htab = mips_elf_hash_table (arg->info);
 
-  if (htab->is_vxworks)
-    s->size += n * MIPS_ELF_RELA_SIZE (abfd);
-  else
+  if (ref->symndx < 0)
     {
-      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);
-    }
-}
-\f
-/* A htab_traverse callback for GOT entries.  Set boolean *DATA to true
-   if the GOT entry is for an indirect or warning symbol.  */
+      struct mips_elf_link_hash_entry *h;
 
-static int
-mips_elf_check_recreate_got (void **entryp, void *data)
-{
-  struct mips_got_entry *entry;
-  bfd_boolean *must_recreate;
+      /* Global GOT_PAGEs decay to GOT_DISP and so don't need page entries.  */
+      h = ref->u.h;
+      if (!SYMBOL_REFERENCES_LOCAL (arg->info, &h->root))
+       return 1;
 
-  entry = (struct mips_got_entry *) *entryp;
-  must_recreate = (bfd_boolean *) data;
-  if (entry->abfd != NULL && entry->symndx == -1)
+      /* Ignore undefined symbols; we'll issue an error later if
+        appropriate.  */
+      if (!((h->root.root.type == bfd_link_hash_defined
+            || h->root.root.type == bfd_link_hash_defweak)
+           && h->root.root.u.def.section))
+       return 1;
+
+      sec = h->root.root.u.def.section;
+      addend = h->root.root.u.def.value + ref->addend;
+    }
+  else
     {
-      struct mips_elf_link_hash_entry *h;
+      Elf_Internal_Sym *isym;
 
-      h = entry->d.h;
-      if (h->root.root.type == bfd_link_hash_indirect
-         || h->root.root.type == bfd_link_hash_warning)
+      /* Read in the symbol.  */
+      isym = bfd_sym_from_r_symndx (&htab->sym_cache, ref->u.abfd,
+                                   ref->symndx);
+      if (isym == NULL)
        {
-         *must_recreate = TRUE;
+         arg->g = NULL;
          return 0;
        }
-    }
-  return 1;
-}
-
-/* A htab_traverse callback for GOT entries.  Add all entries to
-   hash table *DATA, converting entries for indirect and warning
-   symbols into entries for the target symbol.  Set *DATA to null
-   on error.  */
-
-static int
-mips_elf_recreate_got (void **entryp, void *data)
-{
-  htab_t *new_got;
-  struct mips_got_entry new_entry, *entry;
-  void **slot;
 
-  new_got = (htab_t *) data;
-  entry = (struct mips_got_entry *) *entryp;
-  if (entry->abfd != NULL
-      && entry->symndx == -1
-      && (entry->d.h->root.root.type == bfd_link_hash_indirect
-         || entry->d.h->root.root.type == bfd_link_hash_warning))
-    {
-      struct mips_elf_link_hash_entry *h;
+      /* Get the associated input section.  */
+      sec = bfd_section_from_elf_index (ref->u.abfd, isym->st_shndx);
+      if (sec == NULL)
+       {
+         arg->g = NULL;
+         return 0;
+       }
 
-      new_entry = *entry;
-      entry = &new_entry;
-      h = entry->d.h;
-      do
+      /* If this is a mergable section, work out the section and offset
+        of the merged data.  For section symbols, the addend specifies
+        of the offset _of_ the first byte in the data, otherwise it
+        specifies the offset _from_ the first byte.  */
+      if (sec->flags & SEC_MERGE)
        {
-         BFD_ASSERT (h->global_got_area == GGA_NONE);
-         h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+         void *secinfo;
+
+         secinfo = elf_section_data (sec)->sec_info;
+         if (ELF_ST_TYPE (isym->st_info) == STT_SECTION)
+           addend = _bfd_merged_section_offset (ref->u.abfd, &sec, secinfo,
+                                                isym->st_value + ref->addend);
+         else
+           addend = _bfd_merged_section_offset (ref->u.abfd, &sec, secinfo,
+                                                isym->st_value) + ref->addend;
        }
-      while (h->root.root.type == bfd_link_hash_indirect
-            || h->root.root.type == bfd_link_hash_warning);
-      entry->d.h = h;
+      else
+       addend = isym->st_value + ref->addend;
     }
-  slot = htab_find_slot (*new_got, entry, INSERT);
-  if (slot == NULL)
+  if (!mips_elf_record_got_page_entry (arg, sec, addend))
     {
-      *new_got = NULL;
+      arg->g = NULL;
       return 0;
     }
-  if (*slot == NULL)
-    {
-      if (entry == &new_entry)
-       {
-         entry = bfd_alloc (entry->abfd, sizeof (*entry));
-         if (!entry)
-           {
-             *new_got = NULL;
-             return 0;
-           }
-         *entry = new_entry;
-       }
-      *slot = entry;
-    }
   return 1;
 }
 
 /* If any entries in G->got_entries are for indirect or warning symbols,
-   replace them with entries for the target symbol.  */
+   replace them with entries for the target symbol.  Convert g->got_page_refs
+   into got_page_entry structures and estimate the number of page entries
+   that they require.  */
 
 static bfd_boolean
-mips_elf_resolve_final_got_entries (struct mips_got_info *g)
+mips_elf_resolve_final_got_entries (struct bfd_link_info *info,
+                                   struct mips_got_info *g)
 {
-  bfd_boolean must_recreate;
-  htab_t new_got;
+  struct mips_elf_traverse_got_arg tga;
+  struct mips_got_info oldg;
+
+  oldg = *g;
 
-  must_recreate = FALSE;
-  htab_traverse (g->got_entries, mips_elf_check_recreate_got, &must_recreate);
-  if (must_recreate)
+  tga.info = info;
+  tga.g = g;
+  tga.value = FALSE;
+  htab_traverse (g->got_entries, mips_elf_check_recreate_got, &tga);
+  if (tga.value)
     {
-      new_got = htab_create (htab_size (g->got_entries),
-                            mips_elf_got_entry_hash,
-                            mips_elf_got_entry_eq, NULL);
-      htab_traverse (g->got_entries, mips_elf_recreate_got, &new_got);
-      if (new_got == NULL)
+      *g = oldg;
+      g->got_entries = htab_create (htab_size (oldg.got_entries),
+                                   mips_elf_got_entry_hash,
+                                   mips_elf_got_entry_eq, NULL);
+      if (!g->got_entries)
+       return FALSE;
+
+      htab_traverse (oldg.got_entries, mips_elf_recreate_got, &tga);
+      if (!tga.g)
        return FALSE;
 
-      htab_delete (g->got_entries);
-      g->got_entries = new_got;
+      htab_delete (oldg.got_entries);
     }
+
+  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;
+
+  tga.info = info;
+  tga.g = g;
+  htab_traverse (g->got_page_refs, mips_elf_resolve_got_page_ref, &tga);
+
   return TRUE;
 }
 
-/* A mips_elf_link_hash_traverse callback for which DATA points
-   to the link_info structure.  Count the number of type (3) entries
-   in the master GOT.  */
+/* Return true if a GOT entry for H should live in the local rather than
+   global GOT area.  */
+
+static bfd_boolean
+mips_use_local_got_p (struct bfd_link_info *info,
+                     struct mips_elf_link_hash_entry *h)
+{
+  /* Symbols that aren't in the dynamic symbol table must live in the
+     local GOT.  This includes symbols that are completely undefined
+     and which therefore don't bind locally.  We'll report undefined
+     symbols later if appropriate.  */
+  if (h->root.dynindx == -1)
+    return TRUE;
+
+  /* Symbols that bind locally can (and in the case of forced-local
+     symbols, must) live in the local GOT.  */
+  if (h->got_only_for_calls
+      ? SYMBOL_CALLS_LOCAL (info, &h->root)
+      : SYMBOL_REFERENCES_LOCAL (info, &h->root))
+    return TRUE;
+
+  /* If this is an executable that must provide a definition of the symbol,
+     either though PLTs or copy relocations, then that address should go in
+     the local rather than global GOT.  */
+  if (info->executable && h->has_static_relocs)
+    return TRUE;
+
+  return FALSE;
+}
+
+/* A mips_elf_link_hash_traverse callback for which DATA points to the
+   link_info structure.  Decide whether the hash entry needs an entry in
+   the global part of the primary GOT, setting global_got_area accordingly.
+   Count the number of global symbols that are in the primary GOT only
+   because they have relocations against them (reloc_only_gotno).  */
 
 static int
 mips_elf_count_got_symbols (struct mips_elf_link_hash_entry *h, void *data)
@@ -4124,38 +4352,23 @@ mips_elf_count_got_symbols (struct mips_elf_link_hash_entry *h, void *data)
   if (h->global_got_area != GGA_NONE)
     {
       /* Make a final decision about whether the symbol belongs in the
-        local or global GOT.  Symbols that bind locally can (and in the
-        case of forced-local symbols, must) live in the local GOT.
-        Those that are aren't in the dynamic symbol table must also
-        live in the local GOT.
-
-        Note that the former condition does not always imply the
-        latter: symbols do not bind locally if they are completely
-        undefined.  We'll report undefined symbols later if appropriate.  */
-      if (h->root.dynindx == -1
-         || (h->got_only_for_calls
-             ? SYMBOL_CALLS_LOCAL (info, &h->root)
-             : SYMBOL_REFERENCES_LOCAL (info, &h->root)))
-       {
-         /* The symbol belongs in the local GOT.  We no longer need this
-            entry if it was only used for relocations; those relocations
-            will be against the null or section symbol instead of H.  */
-         if (h->global_got_area != GGA_RELOC_ONLY)
-           g->local_gotno++;
-         h->global_got_area = GGA_NONE;
-       }
+        local or global GOT.  */
+      if (mips_use_local_got_p (info, h))
+       /* The symbol belongs in the local GOT.  We no longer need this
+          entry if it was only used for relocations; those relocations
+          will be against the null or section symbol instead of H.  */
+       h->global_got_area = GGA_NONE;
       else if (htab->is_vxworks
               && h->got_only_for_calls
-              && h->root.plt.offset != MINUS_ONE)
+              && h->root.plt.plist->mips_offset != MINUS_ONE)
        /* On VxWorks, calls can refer directly to the .got.plt entry;
           they don't need entries in the regular GOT.  .got.plt entries
           will be allocated by _bfd_mips_elf_adjust_dynamic_symbol.  */
        h->global_got_area = GGA_NONE;
-      else
+      else if (h->global_got_area == GGA_RELOC_ONLY)
        {
+         g->reloc_only_gotno++;
          g->global_gotno++;
-         if (h->global_got_area == GGA_RELOC_ONLY)
-           g->reloc_only_gotno++;
        }
     }
   return 1;
@@ -4275,17 +4488,12 @@ static bfd_boolean
 mips_elf_merge_got (bfd *abfd, struct mips_got_info *g,
                    struct mips_elf_got_per_bfd_arg *arg)
 {
-  struct mips_elf_traverse_got_arg tga;
   unsigned int estimate;
   int result;
 
-  if (!mips_elf_resolve_final_got_entries (g))
+  if (!mips_elf_resolve_final_got_entries (arg->info, g))
     return FALSE;
 
-  tga.info = arg->info;
-  tga.g = g;
-  htab_traverse (g->got_entries, mips_elf_count_got_entries, &tga);
-
   /* Work out the number of page, local and TLS entries.  */
   estimate = arg->max_pages;
   if (estimate > g->page_gotno)
@@ -4366,63 +4574,21 @@ mips_elf_initialize_tls_index (void **entryp, void *data)
 {
   struct mips_got_entry *entry;
   struct mips_elf_traverse_got_arg *arg;
-  struct mips_got_info *g;
-  bfd_vma next_index;
-  unsigned char tls_type;
 
   /* We're only interested in TLS symbols.  */
   entry = (struct mips_got_entry *) *entryp;
-  tls_type = (entry->tls_type & GOT_TLS_TYPE);
-  if (tls_type == 0)
+  if (entry->tls_type == GOT_TLS_NONE)
     return 1;
 
   arg = (struct mips_elf_traverse_got_arg *) data;
-  g = arg->g;
-  next_index = arg->value * g->tls_assigned_gotno;
-
-  if (entry->symndx == -1 && g->next == NULL)
-    {
-      /* A type (3) got entry in the single-GOT case.  We use the symbol's
-        hash table entry to track its index.  */
-      if (tls_type == GOT_TLS_IE)
-       {
-         if (entry->d.h->tls_ie_type & GOT_TLS_OFFSET_DONE)
-           return 1;
-         entry->d.h->tls_ie_type |= GOT_TLS_OFFSET_DONE;
-         entry->d.h->tls_ie_got_offset = next_index;
-       }
-      else
-       {
-         BFD_ASSERT (tls_type == GOT_TLS_GD);
-         if (entry->d.h->tls_gd_type & GOT_TLS_OFFSET_DONE)
-           return 1;
-         entry->d.h->tls_gd_type |= GOT_TLS_OFFSET_DONE;
-         entry->d.h->tls_gd_got_offset = next_index;
-       }
-    }
-  else
+  if (!mips_elf_set_gotidx (entryp, arg->value * arg->g->tls_assigned_gotno))
     {
-      if (tls_type == GOT_TLS_LDM)
-       {
-         /* 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;
-       }
-      if (!mips_elf_set_gotidx (entryp, next_index))
-       {
-         arg->g = NULL;
-         return 0;
-       }
+      arg->g = NULL;
+      return 0;
     }
 
   /* Account for the entries we've just allocated.  */
-  g->tls_assigned_gotno += mips_tls_got_entries (tls_type);
+  arg->g->tls_assigned_gotno += mips_tls_got_entries (entry->tls_type);
   return 1;
 }
 
@@ -4875,6 +5041,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;
+  h->other = (h->other & ~ELF_ST_VISIBILITY (-1)) | STV_HIDDEN;
   elf_hash_table (info)->hgot = h;
 
   if (info->shared
@@ -5173,10 +5340,7 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
        }
 
       target_is_16_bit_code_p = ELF_ST_IS_MIPS16 (h->root.other);
-      /* If the output section is the PLT section,
-         then the target is not microMIPS.  */
-      target_is_micromips_code_p = (htab->splt != sec
-                                   && ELF_ST_IS_MICROMIPS (h->root.other));
+      target_is_micromips_code_p = ELF_ST_IS_MICROMIPS (h->root.other);
     }
 
   /* If this is a reference to a 16-bit function with a stub, we need
@@ -5194,8 +5358,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
           && h->fn_stub != NULL
           && (r_type != R_MIPS16_CALL16 || h->need_fn_stub))
          || (local_p
-             && elf_tdata (input_bfd)->local_stubs != NULL
-             && elf_tdata (input_bfd)->local_stubs[r_symndx] != NULL))
+             && mips_elf_tdata (input_bfd)->local_stubs != NULL
+             && mips_elf_tdata (input_bfd)->local_stubs[r_symndx] != NULL))
       && !section_allows_mips16_refs_p (input_section))
     {
       /* This is a 32- or 64-bit call to a 16-bit function.  We should
@@ -5203,7 +5367,7 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
         stub.  */
       if (local_p)
        {
-         sec = elf_tdata (input_bfd)->local_stubs[r_symndx];
+         sec = mips_elf_tdata (input_bfd)->local_stubs[r_symndx];
          value = 0;
        }
       else
@@ -5227,19 +5391,19 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
       /* 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.  Note that we specifically
-     exclude R_MIPS16_CALL16 from this behavior; indirect calls should
-     use an indirect stub instead.  */
+  /* If this is a MIPS16 call with a stub, that is made through the PLT or
+     to a standard MIPS function, we need to redirect the call to the stub.
+     Note that we specifically exclude R_MIPS16_CALL16 from this behavior;
+     indirect calls should use an indirect stub instead.  */
   else if (r_type == R_MIPS16_26 && !info->relocatable
           && ((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)
+                  && mips_elf_tdata (input_bfd)->local_call_stubs != NULL
+                  && mips_elf_tdata (input_bfd)->local_call_stubs[r_symndx] != NULL))
+          && ((h != NULL && h->use_plt_entry) || !target_is_16_bit_code_p))
     {
       if (local_p)
-       sec = elf_tdata (input_bfd)->local_call_stubs[r_symndx];
+       sec = mips_elf_tdata (input_bfd)->local_call_stubs[r_symndx];
       else
        {
          /* If both call_stub and call_fp_stub are defined, we can figure
@@ -5278,6 +5442,31 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
     symbol = (h->la25_stub->stub_section->output_section->vma
              + h->la25_stub->stub_section->output_offset
              + h->la25_stub->offset);
+  /* For direct MIPS16 and microMIPS calls make sure the compressed PLT
+     entry is used if a standard PLT entry has also been made.  In this
+     case the symbol will have been set by mips_elf_set_plt_sym_value
+     to point to the standard PLT entry, so redirect to the compressed
+     one.  */
+  else if ((r_type == R_MIPS16_26 || r_type == R_MICROMIPS_26_S1)
+          && !info->relocatable
+          && h != NULL
+          && h->use_plt_entry
+          && h->root.plt.plist->comp_offset != MINUS_ONE
+          && h->root.plt.plist->mips_offset != MINUS_ONE)
+    {
+      bfd_boolean micromips_p = MICROMIPS_P (abfd);
+
+      sec = htab->splt;
+      symbol = (sec->output_section->vma
+               + sec->output_offset
+               + htab->plt_header_size
+               + htab->plt_mips_offset
+               + h->root.plt.plist->comp_offset
+               + 1);
+
+      target_is_16_bit_code_p = !micromips_p;
+      target_is_micromips_code_p = micromips_p;
+    }
 
   /* Make sure MIPS16 and microMIPS are not used together.  */
   if ((r_type == R_MIPS16_26 && target_is_micromips_code_p)
@@ -5303,10 +5492,7 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
                                && (target_is_16_bit_code_p
                                    || target_is_micromips_code_p))));
 
-  local_p = (h == NULL
-            || (h->got_only_for_calls
-                ? SYMBOL_CALLS_LOCAL (info, &h->root)
-                : SYMBOL_REFERENCES_LOCAL (info, &h->root)));
+  local_p = (h == NULL || mips_use_local_got_p (info, h));
 
   gp0 = _bfd_get_gp_value (input_bfd);
   gp = _bfd_get_gp_value (abfd);
@@ -6421,7 +6607,7 @@ _bfd_mips_elf_symbol_processing (bfd *abfd, asymbol *asym)
       && (asym->value & 1) != 0)
     {
       asym->value--;
-      if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS)
+      if (MICROMIPS_P (abfd))
        elfsym->internal_elf_sym.st_other
          = ELF_ST_SET_MICROMIPS (elfsym->internal_elf_sym.st_other);
       else
@@ -7023,7 +7209,7 @@ _bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info,
 
     case SHN_MIPS_TEXT:
       /* This section is used in a shared object.  */
-      if (elf_tdata (abfd)->elf_text_section == NULL)
+      if (mips_elf_tdata (abfd)->elf_text_section == NULL)
        {
          asymbol *elf_text_symbol;
          asection *elf_text_section;
@@ -7040,11 +7226,11 @@ _bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info,
 
          /* Initialize the section.  */
 
-         elf_tdata (abfd)->elf_text_section = elf_text_section;
-         elf_tdata (abfd)->elf_text_symbol = elf_text_symbol;
+         mips_elf_tdata (abfd)->elf_text_section = elf_text_section;
+         mips_elf_tdata (abfd)->elf_text_symbol = elf_text_symbol;
 
          elf_text_section->symbol = elf_text_symbol;
-         elf_text_section->symbol_ptr_ptr = &elf_tdata (abfd)->elf_text_symbol;
+         elf_text_section->symbol_ptr_ptr = &mips_elf_tdata (abfd)->elf_text_symbol;
 
          elf_text_section->name = ".text";
          elf_text_section->flags = SEC_NO_FLAGS;
@@ -7057,14 +7243,14 @@ _bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info,
       /* This code used to do *secp = bfd_und_section_ptr if
          info->shared.  I don't know why, and that doesn't make sense,
          so I took it out.  */
-      *secp = elf_tdata (abfd)->elf_text_section;
+      *secp = mips_elf_tdata (abfd)->elf_text_section;
       break;
 
     case SHN_MIPS_ACOMMON:
       /* Fall through. XXX Can we treat this as allocated data?  */
     case SHN_MIPS_DATA:
       /* This section is used in a shared object.  */
-      if (elf_tdata (abfd)->elf_data_section == NULL)
+      if (mips_elf_tdata (abfd)->elf_data_section == NULL)
        {
          asymbol *elf_data_symbol;
          asection *elf_data_section;
@@ -7081,11 +7267,11 @@ _bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info,
 
          /* Initialize the section.  */
 
-         elf_tdata (abfd)->elf_data_section = elf_data_section;
-         elf_tdata (abfd)->elf_data_symbol = elf_data_symbol;
+         mips_elf_tdata (abfd)->elf_data_section = elf_data_section;
+         mips_elf_tdata (abfd)->elf_data_symbol = elf_data_symbol;
 
          elf_data_section->symbol = elf_data_symbol;
-         elf_data_section->symbol_ptr_ptr = &elf_tdata (abfd)->elf_data_symbol;
+         elf_data_section->symbol_ptr_ptr = &mips_elf_tdata (abfd)->elf_data_symbol;
 
          elf_data_section->name = ".data";
          elf_data_section->flags = SEC_NO_FLAGS;
@@ -7098,7 +7284,7 @@ _bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info,
       /* This code used to do *secp = bfd_und_section_ptr if
          info->shared.  I don't know why, and that doesn't make sense,
          so I took it out.  */
-      *secp = elf_tdata (abfd)->elf_data_section;
+      *secp = mips_elf_tdata (abfd)->elf_data_section;
       break;
 
     case SHN_MIPS_SUNDEFINED:
@@ -7259,20 +7445,24 @@ _bfd_mips_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
       /* Change alignments of some sections.  */
       s = bfd_get_linker_section (abfd, ".hash");
       if (s != NULL)
-       bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+       (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+
       s = bfd_get_linker_section (abfd, ".dynsym");
       if (s != NULL)
-       bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+       (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+
       s = bfd_get_linker_section (abfd, ".dynstr");
       if (s != NULL)
-       bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+       (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+
       /* ??? */
       s = bfd_get_section_by_name (abfd, ".reginfo");
       if (s != NULL)
-       bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+       (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+
       s = bfd_get_linker_section (abfd, ".dynamic");
       if (s != NULL)
-       bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+       (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
     }
 
   if (!info->shared)
@@ -7322,7 +7512,7 @@ _bfd_mips_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
     }
 
   /* Create the .plt, .rel(a).plt, .dynbss and .rel(a).bss sections.
-     Also create the _PROCEDURE_LINKAGE_TABLE symbol.  */
+     Also, on VxWorks, create the _PROCEDURE_LINKAGE_TABLE_ symbol.  */
   if (!_bfd_elf_create_dynamic_sections (abfd, info))
     return FALSE;
 
@@ -7342,34 +7532,10 @@ _bfd_mips_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
       || !htab->splt)
     abort ();
 
-  if (htab->is_vxworks)
-    {
-      /* 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);
-       }
-    }
-  else if (!info->shared)
-    {
-      /* All variants of the plt0 entry are the same size.  */
-      htab->plt_header_size = 4 * ARRAY_SIZE (mips_o32_exec_plt0_entry);
-      htab->plt_entry_size = 4 * ARRAY_SIZE (mips_exec_plt_entry);
-    }
+  /* Do the usual VxWorks handling.  */
+  if (htab->is_vxworks
+      && !elf_vxworks_create_dynamic_sections (abfd, info, &htab->srelplt2))
+    return FALSE;
 
   return TRUE;
 }
@@ -7497,8 +7663,27 @@ mips_elf_get_section_contents (bfd *abfd, asection *sec, bfd_byte **contents)
   return bfd_malloc_and_get_section (abfd, sec, contents);
 }
 
+/* Make a new PLT record to keep internal data.  */
+
+static struct plt_entry *
+mips_elf_make_plt_record (bfd *abfd)
+{
+  struct plt_entry *entry;
+
+  entry = bfd_zalloc (abfd, sizeof (*entry));
+  if (entry == NULL)
+    return NULL;
+
+  entry->stub_offset = MINUS_ONE;
+  entry->mips_offset = MINUS_ONE;
+  entry->comp_offset = MINUS_ONE;
+  entry->gotplt_index = MINUS_ONE;
+  return entry;
+}
+
 /* Look through the relocs for a section during the first phase, and
-   allocate space in the global offset table.  */
+   allocate space in the global offset table and record the need for
+   standard MIPS and compressed procedure linkage table entries.  */
 
 bfd_boolean
 _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
@@ -7604,7 +7789,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
          /* Record this stub in an array of local symbol stubs for
              this BFD.  */
-         if (elf_tdata (abfd)->local_stubs == NULL)
+         if (mips_elf_tdata (abfd)->local_stubs == NULL)
            {
              unsigned long symcount;
              asection **n;
@@ -7618,11 +7803,11 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              n = bfd_zalloc (abfd, amt);
              if (n == NULL)
                return FALSE;
-             elf_tdata (abfd)->local_stubs = n;
+             mips_elf_tdata (abfd)->local_stubs = n;
            }
 
          sec->flags |= SEC_KEEP;
-         elf_tdata (abfd)->local_stubs[r_symndx] = sec;
+         mips_elf_tdata (abfd)->local_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
@@ -7729,7 +7914,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
          /* Record this stub in an array of local symbol call_stubs for
              this BFD.  */
-         if (elf_tdata (abfd)->local_call_stubs == NULL)
+         if (mips_elf_tdata (abfd)->local_call_stubs == NULL)
            {
              unsigned long symcount;
              asection **n;
@@ -7743,11 +7928,11 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              n = bfd_zalloc (abfd, amt);
              if (n == NULL)
                return FALSE;
-             elf_tdata (abfd)->local_call_stubs = n;
+             mips_elf_tdata (abfd)->local_call_stubs = n;
            }
 
          sec->flags |= SEC_KEEP;
-         elf_tdata (abfd)->local_call_stubs[r_symndx] = sec;
+         mips_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
@@ -7791,6 +7976,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       unsigned int r_type;
       struct elf_link_hash_entry *h;
       bfd_boolean can_make_dynamic_p;
+      bfd_boolean call_reloc_p;
+      bfd_boolean constrain_symbol_p;
 
       r_symndx = ELF_R_SYM (abfd, rel->r_info);
       r_type = ELF_R_TYPE (abfd, rel->r_info);
@@ -7808,21 +7995,45 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       else
        {
          h = sym_hashes[r_symndx - extsymoff];
-         while (h != NULL
-                && (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;
+         if (h != NULL)
+           {
+             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;
+
+             /* PR15323, ref flags aren't set for references in the
+                same object.  */
+             h->root.non_ir_ref = 1;
+           }
        }
 
       /* Set CAN_MAKE_DYNAMIC_P to true if we can convert this
         relocation into a dynamic one.  */
       can_make_dynamic_p = FALSE;
+
+      /* Set CALL_RELOC_P to true if the relocation is for a call,
+        and if pointer equality therefore doesn't matter.  */
+      call_reloc_p = FALSE;
+
+      /* Set CONSTRAIN_SYMBOL_P if we need to take the relocation
+        into account when deciding how to define the symbol.
+        Relocations in nonallocatable sections such as .pdr and
+        .debug* should have no effect.  */
+      constrain_symbol_p = ((sec->flags & SEC_ALLOC) != 0);
+
       switch (r_type)
        {
-       case R_MIPS_GOT16:
        case R_MIPS_CALL16:
        case R_MIPS_CALL_HI16:
        case R_MIPS_CALL_LO16:
+       case R_MIPS16_CALL16:
+       case R_MICROMIPS_CALL16:
+       case R_MICROMIPS_CALL_HI16:
+       case R_MICROMIPS_CALL_LO16:
+         call_reloc_p = TRUE;
+         /* Fall through.  */
+
+       case R_MIPS_GOT16:
        case R_MIPS_GOT_HI16:
        case R_MIPS_GOT_LO16:
        case R_MIPS_GOT_PAGE:
@@ -7832,14 +8043,10 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_MIPS_TLS_GD:
        case R_MIPS_TLS_LDM:
        case R_MIPS16_GOT16:
-       case R_MIPS16_CALL16:
        case R_MIPS16_TLS_GOTTPREL:
        case R_MIPS16_TLS_GD:
        case R_MIPS16_TLS_LDM:
        case R_MICROMIPS_GOT16:
-       case R_MICROMIPS_CALL16:
-       case R_MICROMIPS_CALL_HI16:
-       case R_MICROMIPS_CALL_LO16:
        case R_MICROMIPS_GOT_HI16:
        case R_MICROMIPS_GOT_LO16:
        case R_MICROMIPS_GOT_PAGE:
@@ -7860,12 +8067,27 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              bfd_set_error (bfd_error_bad_value);
              return FALSE;
            }
+         can_make_dynamic_p = TRUE;
          break;
 
-         /* This is just a hint; it can safely be ignored.  Don't set
-            has_static_relocs for the corresponding symbol.  */
+       case R_MIPS_NONE:
        case R_MIPS_JALR:
        case R_MICROMIPS_JALR:
+         /* These relocations have empty fields and are purely there to
+            provide link information.  The symbol value doesn't matter.  */
+         constrain_symbol_p = FALSE;
+         break;
+
+       case R_MIPS_GPREL16:
+       case R_MIPS_GPREL32:
+       case R_MIPS16_GPREL:
+       case R_MICROMIPS_GPREL16:
+         /* GP-relative relocations always resolve to a definition in a
+            regular input file, ignoring the one-definition rule.  This is
+            important for the GP setup sequence in NewABI code, which
+            always resolves to a local function even if other relocations
+            against the symbol wouldn't.  */
+         constrain_symbol_p = FALSE;
          break;
 
        case R_MIPS_32:
@@ -7892,35 +8114,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              can_make_dynamic_p = TRUE;
              if (dynobj == NULL)
                elf_hash_table (info)->dynobj = dynobj = abfd;
-             break;
            }
-         /* For sections that are not SEC_ALLOC a copy reloc would be
-            output if possible (implying questionable semantics for
-            read-only data objects) or otherwise the final link would
-            fail as ld.so will not process them and could not therefore
-            handle any outstanding dynamic relocations.
-
-            For such sections that are also SEC_DEBUGGING, we can avoid
-            these problems by simply ignoring any relocs as these
-            sections have a predefined use and we know it is safe to do
-            so.
-
-            This is needed in cases such as a global symbol definition
-            in a shared library causing a common symbol from an object
-            file to be converted to an undefined reference.  If that
-            happens, then all the relocations against this symbol from
-            SEC_DEBUGGING sections in the object file will resolve to
-            nil.  */
-         if ((sec->flags & SEC_DEBUGGING) != 0)
-           break;
-         /* Fall through.  */
-
-       default:
-         /* Most static relocations require pointer equality, except
-            for branches.  */
-         if (h)
-           h->pointer_equality_needed = TRUE;
-         /* Fall through.  */
+         break;
 
        case R_MIPS_26:
        case R_MIPS_PC16:
@@ -7930,13 +8125,28 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_MICROMIPS_PC10_S1:
        case R_MICROMIPS_PC16_S1:
        case R_MICROMIPS_PC23_S2:
-         if (h)
-           ((struct mips_elf_link_hash_entry *) h)->has_static_relocs = TRUE;
+         call_reloc_p = TRUE;
          break;
        }
 
       if (h)
        {
+         if (constrain_symbol_p)
+           {
+             if (!can_make_dynamic_p)
+               ((struct mips_elf_link_hash_entry *) h)->has_static_relocs = 1;
+
+             if (!call_reloc_p)
+               h->pointer_equality_needed = 1;
+
+             /* We must not create a stub for a symbol that has
+                relocations 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 && (!can_make_dynamic_p || !call_reloc_p))
+               ((struct mips_elf_link_hash_entry *) h)->no_fn_stub = 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.  */
@@ -8016,21 +8226,6 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
        case R_MIPS_GOT_PAGE:
        case R_MICROMIPS_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)
-           {
-             struct mips_elf_link_hash_entry *hmips =
-               (struct mips_elf_link_hash_entry *) h;
-
-             /* This symbol is definitely not overridable.  */
-             if (hmips->root.def_regular
-                 && ! (info->shared && ! info->symbolic
-                       && ! hmips->root.forced_local))
-               h = NULL;
-           }
-         /* Fall through.  */
-
        case R_MIPS16_GOT16:
        case R_MIPS_GOT16:
        case R_MIPS_GOT_HI16:
@@ -8059,10 +8254,24 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                }
              else
                addend = rel->r_addend;
-             if (!mips_elf_record_got_page_entry (info, abfd, r_symndx,
-                                                  addend))
+             if (!mips_elf_record_got_page_ref (info, abfd, r_symndx,
+                                                h, addend))
                return FALSE;
+
+             if (h)
+               {
+                 struct mips_elf_link_hash_entry *hmips =
+                   (struct mips_elf_link_hash_entry *) h;
+
+                 /* This symbol is definitely not overridable.  */
+                 if (hmips->root.def_regular
+                     && ! (info->shared && ! info->symbolic
+                           && ! hmips->root.forced_local))
+                   h = NULL;
+               }
            }
+         /* If this is a global, overridable symbol, GOT_PAGE will
+            decay to GOT_DISP, so we'll need a GOT entry for it.  */
          /* Fall through.  */
 
        case R_MIPS_GOT_DISP:
@@ -8196,27 +8405,27 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          break;
        }
 
-      /* We must not create a stub for a symbol that has relocations
-        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_MIPS16_CALL16:
-         case R_MIPS_CALL16:
-         case R_MIPS_CALL_HI16:
-         case R_MIPS_CALL_LO16:
-         case R_MIPS_JALR:
-         case R_MICROMIPS_CALL16:
-         case R_MICROMIPS_CALL_HI16:
-         case R_MICROMIPS_CALL_LO16:
-         case R_MICROMIPS_JALR:
-           break;
-         }
+      /* Record the need for a PLT entry.  At this point we don't know
+         yet if we are going to create a PLT in the first place, but
+         we only record whether the relocation requires a standard MIPS
+         or a compressed code entry anyway.  If we don't make a PLT after
+         all, then we'll just ignore these arrangements.  Likewise if
+         a PLT entry is not created because the symbol is satisfied
+         locally.  */
+      if (h != NULL
+         && jal_reloc_p (r_type)
+         && !SYMBOL_CALLS_LOCAL (info, h))
+       {
+         if (h->plt.plist == NULL)
+           h->plt.plist = mips_elf_make_plt_record (abfd);
+         if (h->plt.plist == NULL)
+           return FALSE;
+
+         if (r_type == R_MIPS_26)
+           h->plt.plist->need_mips = TRUE;
+         else
+           h->plt.plist->need_comp = TRUE;
+       }
 
       /* See if this reloc would need to refer to a MIPS16 hard-float stub,
         if there is one.  We only need to handle global symbols here;
@@ -8598,11 +8807,16 @@ _bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
           && !(ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
                && h->root.type == bfd_link_hash_undefweak))
     {
-      /* If this is the first symbol to need a PLT entry, allocate room
-        for the header.  */
-      if (htab->splt->size == 0)
+      bfd_boolean micromips_p = MICROMIPS_P (info->output_bfd);
+      bfd_boolean newabi_p = NEWABI_P (info->output_bfd);
+
+      /* If this is the first symbol to need a PLT entry, then make some
+         basic setup.  Also work out PLT entry sizes.  We'll need them
+         for PLT offset calculations.  */
+      if (htab->plt_mips_offset + htab->plt_comp_offset == 0)
        {
          BFD_ASSERT (htab->sgotplt->size == 0);
+         BFD_ASSERT (htab->plt_got_index == 0);
 
          /* If we're using the PLT additions to the psABI, each PLT
             entry is 16 bytes and the PLT0 entry is 32 bytes.
@@ -8618,40 +8832,107 @@ _bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
                                          MIPS_ELF_LOG_FILE_ALIGN (dynobj)))
            return FALSE;
 
-         htab->splt->size += htab->plt_header_size;
-
          /* On non-VxWorks targets, the first two entries in .got.plt
             are reserved.  */
          if (!htab->is_vxworks)
-           htab->sgotplt->size
-             += get_elf_backend_data (dynobj)->got_header_size;
+           htab->plt_got_index
+             += (get_elf_backend_data (dynobj)->got_header_size
+                 / MIPS_ELF_GOT_SIZE (dynobj));
 
          /* On VxWorks, also allocate room for the header's
             .rela.plt.unloaded entries.  */
          if (htab->is_vxworks && !info->shared)
            htab->srelplt2->size += 2 * sizeof (Elf32_External_Rela);
+
+         /* Now work out the sizes of individual PLT entries.  */
+         if (htab->is_vxworks && info->shared)
+           htab->plt_mips_entry_size
+             = 4 * ARRAY_SIZE (mips_vxworks_shared_plt_entry);
+         else if (htab->is_vxworks)
+           htab->plt_mips_entry_size
+             = 4 * ARRAY_SIZE (mips_vxworks_exec_plt_entry);
+         else if (newabi_p)
+           htab->plt_mips_entry_size
+             = 4 * ARRAY_SIZE (mips_exec_plt_entry);
+         else if (!micromips_p)
+           {
+             htab->plt_mips_entry_size
+               = 4 * ARRAY_SIZE (mips_exec_plt_entry);
+             htab->plt_comp_entry_size
+               = 2 * ARRAY_SIZE (mips16_o32_exec_plt_entry);
+           }
+         else if (htab->insn32)
+           {
+             htab->plt_mips_entry_size
+               = 4 * ARRAY_SIZE (mips_exec_plt_entry);
+             htab->plt_comp_entry_size
+               = 2 * ARRAY_SIZE (micromips_insn32_o32_exec_plt_entry);
+           }
+         else
+           {
+             htab->plt_mips_entry_size
+               = 4 * ARRAY_SIZE (mips_exec_plt_entry);
+             htab->plt_comp_entry_size
+               = 2 * ARRAY_SIZE (micromips_o32_exec_plt_entry);
+           }
+       }
+
+      if (h->plt.plist == NULL)
+       h->plt.plist = mips_elf_make_plt_record (dynobj);
+      if (h->plt.plist == NULL)
+       return FALSE;
+
+      /* There are no defined MIPS16 or microMIPS PLT entries for VxWorks,
+         n32 or n64, so always use a standard entry there.
+
+         If the symbol has a MIPS16 call stub and gets a PLT entry, then
+         all MIPS16 calls will go via that stub, and there is no benefit
+         to having a MIPS16 entry.  And in the case of call_stub a
+         standard entry actually has to be used as the stub ends with a J
+         instruction.  */
+      if (newabi_p
+         || htab->is_vxworks
+         || hmips->call_stub
+         || hmips->call_fp_stub)
+       {
+         h->plt.plist->need_mips = TRUE;
+         h->plt.plist->need_comp = FALSE;
+       }
+
+      /* Otherwise, if there are no direct calls to the function, we
+         have a free choice of whether to use standard or compressed
+         entries.  Prefer microMIPS entries if the object is known to
+         contain microMIPS code, so that it becomes possible to create
+         pure microMIPS binaries.  Prefer standard entries otherwise,
+         because MIPS16 ones are no smaller and are usually slower.  */
+      if (!h->plt.plist->need_mips && !h->plt.plist->need_comp)
+       {
+         if (micromips_p)
+           h->plt.plist->need_comp = TRUE;
+         else
+           h->plt.plist->need_mips = TRUE;
+       }
+
+      if (h->plt.plist->need_mips)
+       {
+         h->plt.plist->mips_offset = htab->plt_mips_offset;
+         htab->plt_mips_offset += htab->plt_mips_entry_size;
+       }
+      if (h->plt.plist->need_comp)
+       {
+         h->plt.plist->comp_offset = htab->plt_comp_offset;
+         htab->plt_comp_offset += htab->plt_comp_entry_size;
        }
 
-      /* Assign the next .plt entry to this symbol.  */
-      h->plt.offset = htab->splt->size;
-      htab->splt->size += htab->plt_entry_size;
+      /* Reserve the corresponding .got.plt entry now too.  */
+      h->plt.plist->gotplt_index = htab->plt_got_index++;
 
       /* If the output file has no definition of the symbol, set the
         symbol's value to the address of the stub.  */
       if (!info->shared && !h->def_regular)
-       {
-         h->root.u.def.section = htab->splt;
-         h->root.u.def.value = h->plt.offset;
-         /* For VxWorks, point at the PLT load stub rather than the
-            lazy resolution stub; this stub will become the canonical
-            function address.  */
-         if (htab->is_vxworks)
-           h->root.u.def.value += 8;
-       }
+       hmips->use_plt_entry = TRUE;
 
-      /* Make room for the .got.plt entry and the R_MIPS_JUMP_SLOT
-        relocation.  */
-      htab->sgotplt->size += MIPS_ELF_GOT_SIZE (dynobj);
+      /* Make room for the R_MIPS_JUMP_SLOT relocation.  */
       htab->srelplt->size += (htab->is_vxworks
                              ? MIPS_ELF_RELA_SIZE (dynobj)
                              : MIPS_ELF_REL_SIZE (dynobj));
@@ -8791,14 +9072,13 @@ mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *info)
   g->local_gotno += htab->reserved_gotno;
   g->assigned_gotno = htab->reserved_gotno;
 
-  /* Replace entries for indirect and warning symbols with entries for
-     the target symbol.  */
-  if (!mips_elf_resolve_final_got_entries (g))
-    return FALSE;
-
-  /* Count the number of GOT symbols.  */
+  /* Decide which symbols need to go in the global part of the GOT and
+     count the number of reloc-only GOT symbols.  */
   mips_elf_link_hash_traverse (htab, mips_elf_count_got_symbols, info);
 
+  if (!mips_elf_resolve_final_got_entries (info, g))
+    return FALSE;
+
   /* Calculate the total loadable size of the output.  That
      will give us the maximum number of GOT_PAGE entries
      required.  */
@@ -8827,25 +9107,13 @@ mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *info)
        sections.  Is 5 enough?  */
     page_gotno = (loadable_size >> 16) + 5;
 
-  /* Choose the smaller of the two estimates; both are intended to be
+  /* Choose the smaller of the two page estimates; both are intended to be
      conservative.  */
   if (page_gotno > g->page_gotno)
     page_gotno = g->page_gotno;
 
   g->local_gotno += page_gotno;
 
-  /* Count the number of local GOT entries and TLS relocs.  */
-  tga.info = info;
-  tga.g = g;
-  htab_traverse (g->got_entries, mips_elf_count_local_got_entries, &tga);
-
-  /* We need to calculate tls_gotno for global symbols at this point
-     instead of building it up earlier, to avoid doublecounting
-     entries for one global symbol from multiple input files.  */
-  elf_link_hash_traverse (elf_hash_table (info),
-                         mips_elf_count_global_tls_entries,
-                         &tga);
-
   s->size += g->local_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
   s->size += g->global_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
   s->size += g->tls_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
@@ -8853,20 +9121,7 @@ mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *info)
   /* 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)
-    {
-      /* VxWorks executables do not need a GOT.  */
-      if (info->shared)
-       {
-         /* Each VxWorks GOT entry needs an explicit relocation.  */
-         unsigned int count;
-
-         count = g->global_gotno + g->local_gotno - htab->reserved_gotno;
-         if (count)
-           mips_elf_allocate_dynamic_relocations (dynobj, info, count);
-       }
-    }
-  else if (s->size > MIPS_ELF_GOT_MAX_SIZE (info))
+  if (!htab->is_vxworks && s->size > MIPS_ELF_GOT_MAX_SIZE (info))
     {
       if (!mips_elf_multi_got (output_bfd, info, s, page_gotno))
        return FALSE;
@@ -8891,6 +9146,10 @@ mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *info)
       BFD_ASSERT (g->tls_assigned_gotno
                  == g->global_gotno + g->local_gotno + g->tls_gotno);
 
+      /* Each VxWorks GOT entry needs an explicit relocation.  */
+      if (htab->is_vxworks && info->shared)
+       g->relocs += g->global_gotno + g->local_gotno - htab->reserved_gotno;
+
       /* Allocate room for the TLS relocations.  */
       if (g->relocs)
        mips_elf_allocate_dynamic_relocations (dynobj, info, g->relocs);
@@ -8924,29 +9183,62 @@ mips_elf_estimate_stub_size (bfd *output_bfd, struct bfd_link_info *info)
   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);
+  /* Determine the size of one stub entry.  There's no disadvantage
+     from using microMIPS code here, so for the sake of pure-microMIPS
+     binaries we prefer it whenever there's any microMIPS code in
+     output produced at all.  This has a benefit of stubs being
+     shorter by 4 bytes each too, unless in the insn32 mode.  */
+  if (!MICROMIPS_P (output_bfd))
+    htab->function_stub_size = (dynsymcount > 0x10000
+                               ? MIPS_FUNCTION_STUB_BIG_SIZE
+                               : MIPS_FUNCTION_STUB_NORMAL_SIZE);
+  else if (htab->insn32)
+    htab->function_stub_size = (dynsymcount > 0x10000
+                               ? MICROMIPS_INSN32_FUNCTION_STUB_BIG_SIZE
+                               : MICROMIPS_INSN32_FUNCTION_STUB_NORMAL_SIZE);
+  else
+    htab->function_stub_size = (dynsymcount > 0x10000
+                               ? MICROMIPS_FUNCTION_STUB_BIG_SIZE
+                               : MICROMIPS_FUNCTION_STUB_NORMAL_SIZE);
 
   htab->sstubs->size = htab->lazy_stub_count * htab->function_stub_size;
 }
 
-/* A mips_elf_link_hash_traverse callback for which DATA points to the
-   MIPS hash table.  If H needs a traditional MIPS lazy-binding stub,
-   allocate an entry in the stubs section.  */
+/* A mips_elf_link_hash_traverse callback for which DATA points to a
+   mips_htab_traverse_info.  If H needs a traditional MIPS lazy-binding
+   stub, allocate an entry in the stubs section.  */
 
 static bfd_boolean
-mips_elf_allocate_lazy_stub (struct mips_elf_link_hash_entry *h, void **data)
+mips_elf_allocate_lazy_stub (struct mips_elf_link_hash_entry *h, void *data)
 {
+  struct mips_htab_traverse_info *hti = data;
   struct mips_elf_link_hash_table *htab;
+  struct bfd_link_info *info;
+  bfd *output_bfd;
+
+  info = hti->info;
+  output_bfd = hti->output_bfd;
+  htab = mips_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
 
-  htab = (struct mips_elf_link_hash_table *) data;
   if (h->needs_lazy_stub)
     {
+      bfd_boolean micromips_p = MICROMIPS_P (output_bfd);
+      unsigned int other = micromips_p ? STO_MICROMIPS : 0;
+      bfd_vma isa_bit = micromips_p;
+
+      BFD_ASSERT (htab->root.dynobj != NULL);
+      if (h->root.plt.plist == NULL)
+       h->root.plt.plist = mips_elf_make_plt_record (htab->sstubs->owner);
+      if (h->root.plt.plist == NULL)
+       {
+         hti->error = TRUE;
+         return FALSE;
+       }
       h->root.root.u.def.section = htab->sstubs;
-      h->root.root.u.def.value = htab->sstubs->size;
-      h->root.plt.offset = htab->sstubs->size;
+      h->root.root.u.def.value = htab->sstubs->size + isa_bit;
+      h->root.plt.plist->stub_offset = htab->sstubs->size;
+      h->root.other = other;
       htab->sstubs->size += htab->function_stub_size;
     }
   return TRUE;
@@ -8955,22 +9247,97 @@ mips_elf_allocate_lazy_stub (struct mips_elf_link_hash_entry *h, void **data)
 /* Allocate offsets in the stubs section to each symbol that needs one.
    Set the final size of the .MIPS.stub section.  */
 
-static void
+static bfd_boolean
 mips_elf_lay_out_lazy_stubs (struct bfd_link_info *info)
 {
+  bfd *output_bfd = info->output_bfd;
+  bfd_boolean micromips_p = MICROMIPS_P (output_bfd);
+  unsigned int other = micromips_p ? STO_MICROMIPS : 0;
+  bfd_vma isa_bit = micromips_p;
   struct mips_elf_link_hash_table *htab;
+  struct mips_htab_traverse_info hti;
+  struct elf_link_hash_entry *h;
+  bfd *dynobj;
 
   htab = mips_elf_hash_table (info);
   BFD_ASSERT (htab != NULL);
 
   if (htab->lazy_stub_count == 0)
-    return;
+    return TRUE;
 
   htab->sstubs->size = 0;
-  mips_elf_link_hash_traverse (htab, mips_elf_allocate_lazy_stub, htab);
+  hti.info = info;
+  hti.output_bfd = output_bfd;
+  hti.error = FALSE;
+  mips_elf_link_hash_traverse (htab, mips_elf_allocate_lazy_stub, &hti);
+  if (hti.error)
+    return FALSE;
   htab->sstubs->size += htab->function_stub_size;
   BFD_ASSERT (htab->sstubs->size
              == htab->lazy_stub_count * htab->function_stub_size);
+
+  dynobj = elf_hash_table (info)->dynobj;
+  BFD_ASSERT (dynobj != NULL);
+  h = _bfd_elf_define_linkage_sym (dynobj, info, htab->sstubs, "_MIPS_STUBS_");
+  if (h == NULL)
+    return FALSE;
+  h->root.u.def.value = isa_bit;
+  h->other = other;
+  h->type = STT_FUNC;
+
+  return TRUE;
+}
+
+/* A mips_elf_link_hash_traverse callback for which DATA points to a
+   bfd_link_info.  If H uses the address of a PLT entry as the value
+   of the symbol, then set the entry in the symbol table now.  Prefer
+   a standard MIPS PLT entry.  */
+
+static bfd_boolean
+mips_elf_set_plt_sym_value (struct mips_elf_link_hash_entry *h, void *data)
+{
+  struct bfd_link_info *info = data;
+  bfd_boolean micromips_p = MICROMIPS_P (info->output_bfd);
+  struct mips_elf_link_hash_table *htab;
+  unsigned int other;
+  bfd_vma isa_bit;
+  bfd_vma val;
+
+  htab = mips_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
+
+  if (h->use_plt_entry)
+    {
+      BFD_ASSERT (h->root.plt.plist != NULL);
+      BFD_ASSERT (h->root.plt.plist->mips_offset != MINUS_ONE
+                 || h->root.plt.plist->comp_offset != MINUS_ONE);
+
+      val = htab->plt_header_size;
+      if (h->root.plt.plist->mips_offset != MINUS_ONE)
+       {
+         isa_bit = 0;
+         val += h->root.plt.plist->mips_offset;
+         other = 0;
+       }
+      else
+       {
+         isa_bit = 1;
+         val += htab->plt_mips_offset + h->root.plt.plist->comp_offset;
+         other = micromips_p ? STO_MICROMIPS : STO_MIPS16;
+       }
+      val += isa_bit;
+      /* For VxWorks, point at the PLT load stub rather than the lazy
+         resolution stub; this stub will become the canonical function
+         address.  */
+      if (htab->is_vxworks)
+       val += 8;
+
+      h->root.root.u.def.section = htab->splt;
+      h->root.root.u.def.value = val;
+      h->root.other = other;
+    }
+
+  return TRUE;
 }
 
 /* Set the sizes of the dynamic sections.  */
@@ -9002,18 +9369,68 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
            = (bfd_byte *) ELF_DYNAMIC_INTERPRETER (output_bfd);
        }
 
-      /* Create a symbol for the PLT, if we know that we are using it.  */
-      if (htab->splt && htab->splt->size > 0 && htab->root.hplt == NULL)
+      /* Figure out the size of the PLT header if we know that we
+         are using it.  For the sake of cache alignment always use
+         a standard header whenever any standard entries are present
+         even if microMIPS entries are present as well.  This also
+         lets the microMIPS header rely on the value of $v0 only set
+         by microMIPS entries, for a small size reduction.
+
+         Set symbol table entry values for symbols that use the
+         address of their PLT entry now that we can calculate it.
+
+         Also create the _PROCEDURE_LINKAGE_TABLE_ symbol if we
+         haven't already in _bfd_elf_create_dynamic_sections.  */
+      if (htab->splt && htab->plt_mips_offset + htab->plt_comp_offset != 0)
        {
+         bfd_boolean micromips_p = (MICROMIPS_P (output_bfd)
+                                    && !htab->plt_mips_offset);
+         unsigned int other = micromips_p ? STO_MICROMIPS : 0;
+         bfd_vma isa_bit = micromips_p;
          struct elf_link_hash_entry *h;
+         bfd_vma size;
 
          BFD_ASSERT (htab->use_plts_and_copy_relocs);
+         BFD_ASSERT (htab->sgotplt->size == 0);
+         BFD_ASSERT (htab->splt->size == 0);
+
+         if (htab->is_vxworks && info->shared)
+           size = 4 * ARRAY_SIZE (mips_vxworks_shared_plt0_entry);
+         else if (htab->is_vxworks)
+           size = 4 * ARRAY_SIZE (mips_vxworks_exec_plt0_entry);
+         else if (ABI_64_P (output_bfd))
+           size = 4 * ARRAY_SIZE (mips_n64_exec_plt0_entry);
+         else if (ABI_N32_P (output_bfd))
+           size = 4 * ARRAY_SIZE (mips_n32_exec_plt0_entry);
+         else if (!micromips_p)
+           size = 4 * ARRAY_SIZE (mips_o32_exec_plt0_entry);
+         else if (htab->insn32)
+           size = 2 * ARRAY_SIZE (micromips_insn32_o32_exec_plt0_entry);
+         else
+           size = 2 * ARRAY_SIZE (micromips_o32_exec_plt0_entry);
 
-         h = _bfd_elf_define_linkage_sym (dynobj, info, htab->splt,
-                                          "_PROCEDURE_LINKAGE_TABLE_");
-         htab->root.hplt = h;
-         if (h == NULL)
-           return FALSE;
+         htab->plt_header_is_comp = micromips_p;
+         htab->plt_header_size = size;
+         htab->splt->size = (size
+                             + htab->plt_mips_offset
+                             + htab->plt_comp_offset);
+         htab->sgotplt->size = (htab->plt_got_index
+                                * MIPS_ELF_GOT_SIZE (dynobj));
+
+         mips_elf_link_hash_traverse (htab, mips_elf_set_plt_sym_value, info);
+
+         if (htab->root.hplt == NULL)
+           {
+             h = _bfd_elf_define_linkage_sym (dynobj, info, htab->splt,
+                                              "_PROCEDURE_LINKAGE_TABLE_");
+             htab->root.hplt = h;
+             if (h == NULL)
+               return FALSE;
+           }
+
+         h = htab->root.hplt;
+         h->root.u.def.value = isa_bit;
+         h->other = other;
          h->type = STT_FUNC;
        }
     }
@@ -9363,7 +9780,7 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
       const char *name;
       bfd_vma value = 0;
       reloc_howto_type *howto;
-      bfd_boolean cross_mode_jump_p;
+      bfd_boolean cross_mode_jump_p = FALSE;
       /* TRUE if the relocation is a RELA relocation, rather than a
          REL relocation.  */
       bfd_boolean rela_relocation_p = TRUE;
@@ -9852,68 +10269,158 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
 
   BFD_ASSERT (!htab->is_vxworks);
 
-  if (h->plt.offset != MINUS_ONE && hmips->no_fn_stub)
+  if (h->plt.plist != NULL
+      && (h->plt.plist->mips_offset != MINUS_ONE
+         || h->plt.plist->comp_offset != MINUS_ONE))
     {
       /* We've decided to create a PLT entry for this symbol.  */
       bfd_byte *loc;
-      bfd_vma header_address, plt_index, got_address;
+      bfd_vma header_address, got_address;
       bfd_vma got_address_high, got_address_low, load;
-      const bfd_vma *plt_entry;
+      bfd_vma got_index;
+      bfd_vma isa_bit;
+
+      got_index = h->plt.plist->gotplt_index;
 
       BFD_ASSERT (htab->use_plts_and_copy_relocs);
       BFD_ASSERT (h->dynindx != -1);
       BFD_ASSERT (htab->splt != NULL);
-      BFD_ASSERT (h->plt.offset <= htab->splt->size);
+      BFD_ASSERT (got_index != MINUS_ONE);
       BFD_ASSERT (!h->def_regular);
 
       /* Calculate the address of the PLT header.  */
+      isa_bit = htab->plt_header_is_comp;
       header_address = (htab->splt->output_section->vma
-                       + htab->splt->output_offset);
-
-      /* Calculate the index of the entry.  */
-      plt_index = ((h->plt.offset - htab->plt_header_size)
-                  / htab->plt_entry_size);
+                       + htab->splt->output_offset + isa_bit);
 
       /* Calculate the address of the .got.plt entry.  */
       got_address = (htab->sgotplt->output_section->vma
                     + htab->sgotplt->output_offset
-                    + (2 + plt_index) * MIPS_ELF_GOT_SIZE (dynobj));
+                    + got_index * MIPS_ELF_GOT_SIZE (dynobj));
+
       got_address_high = ((got_address + 0x8000) >> 16) & 0xffff;
       got_address_low = got_address & 0xffff;
 
       /* Initially point the .got.plt entry at the PLT header.  */
-      loc = (htab->sgotplt->contents
-            + (2 + plt_index) * MIPS_ELF_GOT_SIZE (dynobj));
+      loc = (htab->sgotplt->contents + got_index * MIPS_ELF_GOT_SIZE (dynobj));
       if (ABI_64_P (output_bfd))
        bfd_put_64 (output_bfd, header_address, loc);
       else
        bfd_put_32 (output_bfd, header_address, loc);
 
-      /* Find out where the .plt entry should go.  */
-      loc = htab->splt->contents + h->plt.offset;
+      /* Now handle the PLT itself.  First the standard entry (the order
+         does not matter, we just have to pick one).  */
+      if (h->plt.plist->mips_offset != MINUS_ONE)
+       {
+         const bfd_vma *plt_entry;
+         bfd_vma plt_offset;
 
-      /* Pick the load opcode.  */
-      load = MIPS_ELF_LOAD_WORD (output_bfd);
+         plt_offset = htab->plt_header_size + h->plt.plist->mips_offset;
 
-      /* Fill in the PLT entry itself.  */
-      plt_entry = mips_exec_plt_entry;
-      bfd_put_32 (output_bfd, plt_entry[0] | got_address_high, loc);
-      bfd_put_32 (output_bfd, plt_entry[1] | got_address_low | load, loc + 4);
+         BFD_ASSERT (plt_offset <= htab->splt->size);
 
-      if (! LOAD_INTERLOCKS_P (output_bfd))
-       {
-         bfd_put_32 (output_bfd, plt_entry[2] | got_address_low, loc + 8);
-         bfd_put_32 (output_bfd, plt_entry[3], loc + 12);
+         /* Find out where the .plt entry should go.  */
+         loc = htab->splt->contents + plt_offset;
+
+         /* Pick the load opcode.  */
+         load = MIPS_ELF_LOAD_WORD (output_bfd);
+
+         /* Fill in the PLT entry itself.  */
+         plt_entry = mips_exec_plt_entry;
+         bfd_put_32 (output_bfd, plt_entry[0] | got_address_high, loc);
+         bfd_put_32 (output_bfd, plt_entry[1] | got_address_low | load,
+                     loc + 4);
+
+         if (! LOAD_INTERLOCKS_P (output_bfd))
+           {
+             bfd_put_32 (output_bfd, plt_entry[2] | got_address_low, loc + 8);
+             bfd_put_32 (output_bfd, plt_entry[3], loc + 12);
+           }
+         else
+           {
+             bfd_put_32 (output_bfd, plt_entry[3], loc + 8);
+             bfd_put_32 (output_bfd, plt_entry[2] | got_address_low,
+                         loc + 12);
+           }
        }
-      else
+
+      /* Now the compressed entry.  They come after any standard ones.  */
+      if (h->plt.plist->comp_offset != MINUS_ONE)
        {
-         bfd_put_32 (output_bfd, plt_entry[3], loc + 8);
-         bfd_put_32 (output_bfd, plt_entry[2] | got_address_low, loc + 12);
+         bfd_vma plt_offset;
+
+         plt_offset = (htab->plt_header_size + htab->plt_mips_offset
+                       + h->plt.plist->comp_offset);
+
+         BFD_ASSERT (plt_offset <= htab->splt->size);
+
+         /* Find out where the .plt entry should go.  */
+         loc = htab->splt->contents + plt_offset;
+
+         /* Fill in the PLT entry itself.  */
+         if (!MICROMIPS_P (output_bfd))
+           {
+             const bfd_vma *plt_entry = mips16_o32_exec_plt_entry;
+
+             bfd_put_16 (output_bfd, plt_entry[0], loc);
+             bfd_put_16 (output_bfd, plt_entry[1], loc + 2);
+             bfd_put_16 (output_bfd, plt_entry[2], loc + 4);
+             bfd_put_16 (output_bfd, plt_entry[3], loc + 6);
+             bfd_put_16 (output_bfd, plt_entry[4], loc + 8);
+             bfd_put_16 (output_bfd, plt_entry[5], loc + 10);
+             bfd_put_32 (output_bfd, got_address, loc + 12);
+           }
+         else if (htab->insn32)
+           {
+             const bfd_vma *plt_entry = micromips_insn32_o32_exec_plt_entry;
+
+             bfd_put_16 (output_bfd, plt_entry[0], loc);
+             bfd_put_16 (output_bfd, got_address_high, loc + 2);
+             bfd_put_16 (output_bfd, plt_entry[2], loc + 4);
+             bfd_put_16 (output_bfd, got_address_low, loc + 6);
+             bfd_put_16 (output_bfd, plt_entry[4], loc + 8);
+             bfd_put_16 (output_bfd, plt_entry[5], loc + 10);
+             bfd_put_16 (output_bfd, plt_entry[6], loc + 12);
+             bfd_put_16 (output_bfd, got_address_low, loc + 14);
+           }
+         else
+           {
+             const bfd_vma *plt_entry = micromips_o32_exec_plt_entry;
+             bfd_signed_vma gotpc_offset;
+             bfd_vma loc_address;
+
+             BFD_ASSERT (got_address % 4 == 0);
+
+             loc_address = (htab->splt->output_section->vma
+                            + htab->splt->output_offset + plt_offset);
+             gotpc_offset = got_address - ((loc_address | 3) ^ 3);
+
+             /* ADDIUPC has a span of +/-16MB, check we're in range.  */
+             if (gotpc_offset + 0x1000000 >= 0x2000000)
+               {
+                 (*_bfd_error_handler)
+                   (_("%B: `%A' offset of %ld from `%A' "
+                      "beyond the range of ADDIUPC"),
+                    output_bfd,
+                    htab->sgotplt->output_section,
+                    htab->splt->output_section,
+                    (long) gotpc_offset);
+                 bfd_set_error (bfd_error_no_error);
+                 return FALSE;
+               }
+             bfd_put_16 (output_bfd,
+                         plt_entry[0] | ((gotpc_offset >> 18) & 0x7f), loc);
+             bfd_put_16 (output_bfd, (gotpc_offset >> 2) & 0xffff, loc + 2);
+             bfd_put_16 (output_bfd, plt_entry[2], loc + 4);
+             bfd_put_16 (output_bfd, plt_entry[3], loc + 6);
+             bfd_put_16 (output_bfd, plt_entry[4], loc + 8);
+             bfd_put_16 (output_bfd, plt_entry[5], loc + 10);
+           }
        }
 
       /* Emit an R_MIPS_JUMP_SLOT relocation against the .got.plt entry.  */
       mips_elf_output_dynamic_relocation (output_bfd, htab->srelplt,
-                                         plt_index, h->dynindx,
+                                         got_index - 2, h->dynindx,
                                          R_MIPS_JUMP_SLOT, got_address);
 
       /* We distinguish between PLT entries and lazy-binding stubs by
@@ -9922,21 +10429,36 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
         binary where pointer equality matters.  */
       sym->st_shndx = SHN_UNDEF;
       if (h->pointer_equality_needed)
-       sym->st_other = STO_MIPS_PLT;
+       sym->st_other = ELF_ST_SET_MIPS_PLT (sym->st_other);
       else
-       sym->st_value = 0;
+       {
+         sym->st_value = 0;
+         sym->st_other = 0;
+       }
     }
-  else if (h->plt.offset != MINUS_ONE)
+
+  if (h->plt.plist != NULL && h->plt.plist->stub_offset != MINUS_ONE)
     {
       /* We've decided to create a lazy-binding stub.  */
+      bfd_boolean micromips_p = MICROMIPS_P (output_bfd);
+      unsigned int other = micromips_p ? STO_MICROMIPS : 0;
+      bfd_vma stub_size = htab->function_stub_size;
       bfd_byte stub[MIPS_FUNCTION_STUB_BIG_SIZE];
+      bfd_vma isa_bit = micromips_p;
+      bfd_vma stub_big_size;
+
+      if (!micromips_p)
+       stub_big_size = MIPS_FUNCTION_STUB_BIG_SIZE;
+      else if (htab->insn32)
+       stub_big_size = MICROMIPS_INSN32_FUNCTION_STUB_BIG_SIZE;
+      else
+       stub_big_size = MICROMIPS_FUNCTION_STUB_BIG_SIZE;
 
       /* This symbol has a stub.  Set it up.  */
 
       BFD_ASSERT (h->dynindx != -1);
 
-      BFD_ASSERT ((htab->function_stub_size == MIPS_FUNCTION_STUB_BIG_SIZE)
-                  || (h->dynindx <= 0xffff));
+      BFD_ASSERT (stub_size == 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
@@ -9945,35 +10467,95 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
        return FALSE;
 
       /* Fill the stub.  */
-      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);
+      if (micromips_p)
+       {
+         idx = 0;
+         bfd_put_micromips_32 (output_bfd, STUB_LW_MICROMIPS (output_bfd),
+                               stub + idx);
+         idx += 4;
+         if (htab->insn32)
+           {
+             bfd_put_micromips_32 (output_bfd,
+                                   STUB_MOVE32_MICROMIPS (output_bfd),
+                                   stub + idx);
+             idx += 4;
+           }
+         else
+           {
+             bfd_put_16 (output_bfd, STUB_MOVE_MICROMIPS, stub + idx);
+             idx += 2;
+           }
+         if (stub_size == stub_big_size)
+           {
+             long dynindx_hi = (h->dynindx >> 16) & 0x7fff;
+
+             bfd_put_micromips_32 (output_bfd,
+                                   STUB_LUI_MICROMIPS (dynindx_hi),
+                                   stub + idx);
+             idx += 4;
+           }
+         if (htab->insn32)
+           {
+             bfd_put_micromips_32 (output_bfd, STUB_JALR32_MICROMIPS,
+                                   stub + idx);
+             idx += 4;
+           }
+         else
+           {
+             bfd_put_16 (output_bfd, STUB_JALR_MICROMIPS, stub + idx);
+             idx += 2;
+           }
+
+         /* If a large stub is not required and sign extension is not a
+            problem, then use legacy code in the stub.  */
+         if (stub_size == stub_big_size)
+           bfd_put_micromips_32 (output_bfd,
+                                 STUB_ORI_MICROMIPS (h->dynindx & 0xffff),
+                                 stub + idx);
+         else if (h->dynindx & ~0x7fff)
+           bfd_put_micromips_32 (output_bfd,
+                                 STUB_LI16U_MICROMIPS (h->dynindx & 0xffff),
+                                 stub + idx);
+         else
+           bfd_put_micromips_32 (output_bfd,
+                                 STUB_LI16S_MICROMIPS (output_bfd,
+                                                       h->dynindx),
+                                 stub + idx);
+       }
       else
-        bfd_put_32 (output_bfd, STUB_LI16S (output_bfd, h->dynindx),
-                   stub + idx);
+       {
+         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 (stub_size == 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 (stub_size == 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 <= htab->sstubs->size);
-      memcpy (htab->sstubs->contents + h->plt.offset,
-             stub, htab->function_stub_size);
+      BFD_ASSERT (h->plt.plist->stub_offset <= htab->sstubs->size);
+      memcpy (htab->sstubs->contents + h->plt.plist->stub_offset,
+             stub, stub_size);
 
-      /* Mark the symbol as undefined.  plt.offset != -1 occurs
+      /* Mark the symbol as undefined.  stub_offset != -1 occurs
         only for the referenced symbol.  */
       sym->st_shndx = SHN_UNDEF;
 
@@ -9982,7 +10564,9 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
         to its stub address when unlinking a shared object.  */
       sym->st_value = (htab->sstubs->output_section->vma
                       + htab->sstubs->output_offset
-                      + h->plt.offset);
+                      + h->plt.plist->stub_offset
+                      + isa_bit);
+      sym->st_other = other;
     }
 
   /* If we have a MIPS16 function with a stub, the dynamic symbol must
@@ -10027,7 +10611,7 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
       e.abfd = output_bfd;
       e.symndx = -1;
       e.d.h = hmips;
-      e.tls_type = 0;
+      e.tls_type = GOT_TLS_NONE;
 
       for (g = g->next; g->next != gg; g = g->next)
        {
@@ -10036,6 +10620,7 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
                                                           &e)))
            {
              offset = p->gotidx;
+             BFD_ASSERT (offset > 0 && offset < htab->sgot->size);
              if (info->shared
                  || (elf_hash_table (info)->dynamic_sections_created
                      && p->d.h != NULL
@@ -10134,13 +10719,18 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
   if (IRIX_COMPAT (output_bfd) == ict_irix6)
     mips_elf_irix6_finish_dynamic_symbol (output_bfd, name, sym);
 
-  /* Keep dynamic MIPS16 symbols odd.  This allows the dynamic linker to
-     treat MIPS16 symbols like any other.  */
+  /* Keep dynamic compressed symbols odd.  This allows the dynamic linker
+     to treat compressed symbols like any other.  */
   if (ELF_ST_IS_MIPS16 (sym->st_other))
     {
       BFD_ASSERT (sym->st_value & 1);
       sym->st_other -= STO_MIPS16;
     }
+  else if (ELF_ST_IS_MICROMIPS (sym->st_other))
+    {
+      BFD_ASSERT (sym->st_value & 1);
+      sym->st_other -= STO_MICROMIPS;
+    }
 
   return TRUE;
 }
@@ -10164,30 +10754,32 @@ _bfd_mips_vxworks_finish_dynamic_symbol (bfd *output_bfd,
   dynobj = elf_hash_table (info)->dynobj;
   hmips = (struct mips_elf_link_hash_entry *) h;
 
-  if (h->plt.offset != (bfd_vma) -1)
+  if (h->plt.plist != NULL && h->plt.plist->mips_offset != MINUS_ONE)
     {
       bfd_byte *loc;
-      bfd_vma plt_address, plt_index, got_address, got_offset, branch_offset;
+      bfd_vma plt_address, got_address, got_offset, branch_offset;
       Elf_Internal_Rela rel;
       static const bfd_vma *plt_entry;
+      bfd_vma gotplt_index;
+      bfd_vma plt_offset;
+
+      plt_offset = htab->plt_header_size + h->plt.plist->mips_offset;
+      gotplt_index = h->plt.plist->gotplt_index;
 
       BFD_ASSERT (h->dynindx != -1);
       BFD_ASSERT (htab->splt != NULL);
-      BFD_ASSERT (h->plt.offset <= htab->splt->size);
+      BFD_ASSERT (gotplt_index != MINUS_ONE);
+      BFD_ASSERT (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);
+                    + plt_offset);
 
       /* Calculate the address of the .got.plt entry.  */
       got_address = (htab->sgotplt->output_section->vma
                     + htab->sgotplt->output_offset
-                    + plt_index * 4);
+                    + gotplt_index * MIPS_ELF_GOT_SIZE (output_bfd));
 
       /* Calculate the offset of the .got.plt entry from
         _GLOBAL_OFFSET_TABLE_.  */
@@ -10195,20 +10787,21 @@ _bfd_mips_vxworks_finish_dynamic_symbol (bfd *output_bfd,
 
       /* 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;
+      branch_offset = -(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);
+                 (htab->sgotplt->contents
+                  + gotplt_index * MIPS_ELF_GOT_SIZE (output_bfd)));
 
       /* Find out where the .plt entry should go.  */
-      loc = htab->splt->contents + h->plt.offset;
+      loc = htab->splt->contents + 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);
+         bfd_put_32 (output_bfd, plt_entry[1] | gotplt_index, loc + 4);
        }
       else
        {
@@ -10219,7 +10812,7 @@ _bfd_mips_vxworks_finish_dynamic_symbol (bfd *output_bfd,
          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[1] | gotplt_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);
@@ -10228,12 +10821,12 @@ _bfd_mips_vxworks_finish_dynamic_symbol (bfd *output_bfd,
          bfd_put_32 (output_bfd, plt_entry[7], loc + 28);
 
          loc = (htab->srelplt2->contents
-                + (plt_index * 3 + 2) * sizeof (Elf32_External_Rela));
+                + (gotplt_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;
+         rel.r_addend = plt_offset;
          bfd_elf32_swap_reloca_out (output_bfd, &rel, loc);
 
          /* Emit a relocation for the lui of %hi(<.got.plt slot>).  */
@@ -10251,7 +10844,8 @@ _bfd_mips_vxworks_finish_dynamic_symbol (bfd *output_bfd,
        }
 
       /* Emit an R_MIPS_JUMP_SLOT relocation against the .got.plt entry.  */
-      loc = htab->srelplt->contents + plt_index * sizeof (Elf32_External_Rela);
+      loc = (htab->srelplt->contents
+            + gotplt_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;
@@ -10318,7 +10912,7 @@ _bfd_mips_vxworks_finish_dynamic_symbol (bfd *output_bfd,
 
 /* Write out a plt0 entry to the beginning of .plt.  */
 
-static void
+static bfd_boolean
 mips_finish_exec_plt (bfd *output_bfd, struct bfd_link_info *info)
 {
   bfd_byte *loc;
@@ -10333,8 +10927,12 @@ mips_finish_exec_plt (bfd *output_bfd, struct bfd_link_info *info)
     plt_entry = mips_n64_exec_plt0_entry;
   else if (ABI_N32_P (output_bfd))
     plt_entry = mips_n32_exec_plt0_entry;
-  else
+  else if (!htab->plt_header_is_comp)
     plt_entry = mips_o32_exec_plt0_entry;
+  else if (htab->insn32)
+    plt_entry = micromips_insn32_o32_exec_plt0_entry;
+  else
+    plt_entry = micromips_o32_exec_plt0_entry;
 
   /* Calculate the value of .got.plt.  */
   gotplt_value = (htab->sgotplt->output_section->vma
@@ -10349,14 +10947,62 @@ mips_finish_exec_plt (bfd *output_bfd, struct bfd_link_info *info)
 
   /* Install the PLT header.  */
   loc = htab->splt->contents;
-  bfd_put_32 (output_bfd, plt_entry[0] | gotplt_value_high, loc);
-  bfd_put_32 (output_bfd, plt_entry[1] | gotplt_value_low, loc + 4);
-  bfd_put_32 (output_bfd, plt_entry[2] | gotplt_value_low, 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);
-  bfd_put_32 (output_bfd, plt_entry[6], loc + 24);
-  bfd_put_32 (output_bfd, plt_entry[7], loc + 28);
+  if (plt_entry == micromips_o32_exec_plt0_entry)
+    {
+      bfd_vma gotpc_offset;
+      bfd_vma loc_address;
+      size_t i;
+
+      BFD_ASSERT (gotplt_value % 4 == 0);
+
+      loc_address = (htab->splt->output_section->vma
+                    + htab->splt->output_offset);
+      gotpc_offset = gotplt_value - ((loc_address | 3) ^ 3);
+
+      /* ADDIUPC has a span of +/-16MB, check we're in range.  */
+      if (gotpc_offset + 0x1000000 >= 0x2000000)
+       {
+         (*_bfd_error_handler)
+           (_("%B: `%A' offset of %ld from `%A' beyond the range of ADDIUPC"),
+            output_bfd,
+            htab->sgotplt->output_section,
+            htab->splt->output_section,
+            (long) gotpc_offset);
+         bfd_set_error (bfd_error_no_error);
+         return FALSE;
+       }
+      bfd_put_16 (output_bfd,
+                 plt_entry[0] | ((gotpc_offset >> 18) & 0x7f), loc);
+      bfd_put_16 (output_bfd, (gotpc_offset >> 2) & 0xffff, loc + 2);
+      for (i = 2; i < ARRAY_SIZE (micromips_o32_exec_plt0_entry); i++)
+       bfd_put_16 (output_bfd, plt_entry[i], loc + (i * 2));
+    }
+  else if (plt_entry == micromips_insn32_o32_exec_plt0_entry)
+    {
+      size_t i;
+
+      bfd_put_16 (output_bfd, plt_entry[0], loc);
+      bfd_put_16 (output_bfd, gotplt_value_high, loc + 2);
+      bfd_put_16 (output_bfd, plt_entry[2], loc + 4);
+      bfd_put_16 (output_bfd, gotplt_value_low, loc + 6);
+      bfd_put_16 (output_bfd, plt_entry[4], loc + 8);
+      bfd_put_16 (output_bfd, gotplt_value_low, loc + 10);
+      for (i = 6; i < ARRAY_SIZE (micromips_insn32_o32_exec_plt0_entry); i++)
+       bfd_put_16 (output_bfd, plt_entry[i], loc + (i * 2));
+    }
+  else
+    {
+      bfd_put_32 (output_bfd, plt_entry[0] | gotplt_value_high, loc);
+      bfd_put_32 (output_bfd, plt_entry[1] | gotplt_value_low, loc + 4);
+      bfd_put_32 (output_bfd, plt_entry[2] | gotplt_value_low, 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);
+      bfd_put_32 (output_bfd, plt_entry[6], loc + 24);
+      bfd_put_32 (output_bfd, plt_entry[7], loc + 28);
+    }
+
+  return TRUE;
 }
 
 /* Install the PLT header for a VxWorks executable and finalize the
@@ -10878,7 +11524,8 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
       else
        {
          BFD_ASSERT (!info->shared);
-         mips_finish_exec_plt (output_bfd, info);
+         if (!mips_finish_exec_plt (output_bfd, info))
+           return FALSE;
        }
     }
   return TRUE;
@@ -11152,7 +11799,7 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd,
   s = bfd_get_section_by_name (abfd, ".reginfo");
   if (s != NULL && (s->flags & SEC_LOAD) != 0)
     {
-      for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+      for (m = elf_seg_map (abfd); m != NULL; m = m->next)
        if (m->p_type == PT_MIPS_REGINFO)
          break;
       if (m == NULL)
@@ -11167,7 +11814,7 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd,
          m->sections[0] = s;
 
          /* We want to put it after the PHDR and INTERP segments.  */
-         pm = &elf_tdata (abfd)->segment_map;
+         pm = &elf_seg_map (abfd);
          while (*pm != NULL
                 && ((*pm)->p_type == PT_PHDR
                     || (*pm)->p_type == PT_INTERP))
@@ -11197,7 +11844,7 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd,
        {
          struct elf_segment_map *options_segment;
 
-         pm = &elf_tdata (abfd)->segment_map;
+         pm = &elf_seg_map (abfd);
          while (*pm != NULL
                 && ((*pm)->p_type == PT_PHDR
                     || (*pm)->p_type == PT_INTERP))
@@ -11227,7 +11874,7 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd,
              && bfd_get_section_by_name (abfd, ".dynamic") != NULL
              && bfd_get_section_by_name (abfd, ".mdebug") != NULL)
            {
-             for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+             for (m = elf_seg_map (abfd); m != NULL; m = m->next)
                if (m->p_type == PT_MIPS_RTPROC)
                  break;
              if (m == NULL)
@@ -11253,7 +11900,7 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd,
                    }
 
                  /* We want to put it after the DYNAMIC segment.  */
-                 pm = &elf_tdata (abfd)->segment_map;
+                 pm = &elf_seg_map (abfd);
                  while (*pm != NULL && (*pm)->p_type != PT_DYNAMIC)
                    pm = &(*pm)->next;
                  if (*pm != NULL)
@@ -11267,7 +11914,7 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd,
       /* On IRIX5, the PT_DYNAMIC segment includes the .dynamic,
         .dynstr, .dynsym, and .hash sections, and everything in
         between.  */
-      for (pm = &elf_tdata (abfd)->segment_map; *pm != NULL;
+      for (pm = &elf_seg_map (abfd); *pm != NULL;
           pm = &(*pm)->next)
        if ((*pm)->p_type == PT_DYNAMIC)
          break;
@@ -11374,7 +12021,7 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd,
       && !SGI_COMPAT (abfd)
       && bfd_get_section_by_name (abfd, ".dynamic"))
     {
-      for (pm = &elf_tdata (abfd)->segment_map; *pm != NULL; pm = &(*pm)->next)
+      for (pm = &elf_seg_map (abfd); *pm != NULL; pm = &(*pm)->next)
        if ((*pm)->p_type == PT_NULL)
          break;
       if (*pm == NULL)
@@ -11529,11 +12176,6 @@ _bfd_mips_elf_copy_indirect_symbol (struct bfd_link_info *info,
     indmips->global_got_area = GGA_NONE;
   if (indmips->has_nonpic_branches)
     dirmips->has_nonpic_branches = TRUE;
-
-  if (dirmips->tls_ie_type == 0)
-    dirmips->tls_ie_type = indmips->tls_ie_type;
-  if (dirmips->tls_gd_type == 0)
-    dirmips->tls_gd_type = indmips->tls_gd_type;
 }
 \f
 #define PDR_SIZE 32
@@ -11690,7 +12332,7 @@ _bfd_mips_elf_find_nearest_line (bfd *abfd, asection *section,
       if (elf_section_data (msec)->this_hdr.sh_type != SHT_NOBITS)
        msec->flags |= SEC_HAS_CONTENTS;
 
-      fi = elf_tdata (abfd)->find_line_info;
+      fi = mips_elf_tdata (abfd)->find_line_info;
       if (fi == NULL)
        {
          bfd_size_type external_fdr_size;
@@ -11728,7 +12370,7 @@ _bfd_mips_elf_find_nearest_line (bfd *abfd, asection *section,
          for (; fraw_src < fraw_end; fraw_src += external_fdr_size, fdr_ptr++)
            (*swap->swap_fdr_in) (abfd, fraw_src, fdr_ptr);
 
-         elf_tdata (abfd)->find_line_info = fi;
+         mips_elf_tdata (abfd)->find_line_info = fi;
 
          /* Note that we don't bother to ever free this information.
              find_nearest_line is either called all the time, as in
@@ -12401,6 +13043,7 @@ _bfd_mips_elf_relax_section (bfd *abfd, asection *sec,
                             struct bfd_link_info *link_info,
                             bfd_boolean *again)
 {
+  bfd_boolean insn32 = mips_elf_hash_table (link_info)->insn32;
   Elf_Internal_Shdr *symtab_hdr;
   Elf_Internal_Rela *internal_relocs;
   Elf_Internal_Rela *irel, *irelend;
@@ -12683,7 +13326,13 @@ _bfd_mips_elf_relax_section (bfd *abfd, asection *sec,
               && irel->r_offset + 5 < sec->size
               && ((fndopc = find_match (opcode, bz_rs_insns_32)) >= 0
                   || (fndopc = find_match (opcode, bz_rt_insns_32)) >= 0)
-              && MATCH (bfd_get_16 (abfd, ptr + 4), nop_insn_16))
+              && ((!insn32
+                   && (delcnt = MATCH (bfd_get_16 (abfd, ptr + 4),
+                                       nop_insn_16) ? 2 : 0))
+                  || (irel->r_offset + 7 < sec->size
+                      && (delcnt = MATCH (bfd_get_micromips_32 (abfd,
+                                                                ptr + 4),
+                                          nop_insn_32) ? 4 : 0))))
        {
          unsigned long reg;
 
@@ -12696,15 +13345,15 @@ _bfd_mips_elf_relax_section (bfd *abfd, asection *sec,
 
          bfd_put_micromips_32 (abfd, opcode, ptr);
 
-         /* Delete the 16-bit delay slot NOP: two bytes from
-            irel->offset + 4.  */
-         delcnt = 2;
+         /* Delete the delay slot NOP: two or four bytes from
+            irel->offset + 4; delcnt has already been set above.  */
          deloff = 4;
        }
 
       /* R_MICROMIPS_PC16_S1 relaxation to R_MICROMIPS_PC10_S1.  We need
          to check the distance from the next instruction, so subtract 2.  */
-      else if (r_type == R_MICROMIPS_PC16_S1
+      else if (!insn32
+              && r_type == R_MICROMIPS_PC16_S1
               && IS_BITSIZE (pcrval - 2, 11)
               && find_match (opcode, b_insns_32) >= 0)
        {
@@ -12724,7 +13373,8 @@ _bfd_mips_elf_relax_section (bfd *abfd, asection *sec,
 
       /* R_MICROMIPS_PC16_S1 relaxation to R_MICROMIPS_PC7_S1.  We need
          to check the distance from the next instruction, so subtract 2.  */
-      else if (r_type == R_MICROMIPS_PC16_S1
+      else if (!insn32
+              && r_type == R_MICROMIPS_PC16_S1
               && IS_BITSIZE (pcrval - 2, 8)
               && (((fndopc = find_match (opcode, bz_rs_insns_32)) >= 0
                    && OP16_VALID_REG (OP32_SREG (opcode)))
@@ -12751,7 +13401,8 @@ _bfd_mips_elf_relax_section (bfd *abfd, asection *sec,
        }
 
       /* R_MICROMIPS_26_S1 -- JAL to JALS relaxation for microMIPS targets.  */
-      else if (r_type == R_MICROMIPS_26_S1
+      else if (!insn32
+              && r_type == R_MICROMIPS_26_S1
               && target_is_micromips_code_p
               && irel->r_offset + 7 < sec->size
               && MATCH (opcode, jal_insn_32_bd32))
@@ -12876,6 +13527,8 @@ _bfd_mips_elf_link_hash_table_create (bfd *abfd)
       free (ret);
       return NULL;
     }
+  ret->root.init_plt_refcount.plist = NULL;
+  ret->root.init_plt_offset.plist = NULL;
 
   return &ret->root.root;
 }
@@ -12907,6 +13560,15 @@ _bfd_mips_elf_use_plts_and_copy_relocs (struct bfd_link_info *info)
 {
   mips_elf_hash_table (info)->use_plts_and_copy_relocs = TRUE;
 }
+
+/* A function that the linker calls to select between all or only
+   32-bit microMIPS instructions.  */
+
+void
+_bfd_mips_elf_insn32 (struct bfd_link_info *info, bfd_boolean on)
+{
+  mips_elf_hash_table (info)->insn32 = on;
+}
 \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
@@ -13543,7 +14205,8 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 \f
 /* Structure for saying that BFD machine EXTENSION extends BASE.  */
 
-struct mips_mach_extension {
+struct mips_mach_extension
+{
   unsigned long extension, base;
 };
 
@@ -13551,7 +14214,8 @@ struct mips_mach_extension {
 /* An array describing how BFD machines relate to one another.  The entries
    are ordered topologically with MIPS I extensions listed last.  */
 
-static const struct mips_mach_extension mips_mach_extensions[] = {
+static const struct mips_mach_extension mips_mach_extensions[] =
+{
   /* MIPS64r2 extensions.  */
   { bfd_mach_mips_octeon2, bfd_mach_mips_octeonp },
   { bfd_mach_mips_octeonp, bfd_mach_mips_octeon },
@@ -13668,12 +14332,18 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
   obj_attribute *in_attr;
   obj_attribute *out_attr;
   bfd *abi_fp_bfd;
+  bfd *abi_msa_bfd;
 
   abi_fp_bfd = mips_elf_tdata (obfd)->abi_fp_bfd;
   in_attr = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
-  if (!abi_fp_bfd && in_attr[Tag_GNU_MIPS_ABI_FP].i != 0)
+  if (!abi_fp_bfd && in_attr[Tag_GNU_MIPS_ABI_FP].i != Val_GNU_MIPS_ABI_FP_ANY)
     mips_elf_tdata (obfd)->abi_fp_bfd = ibfd;
 
+  abi_msa_bfd = mips_elf_tdata (obfd)->abi_msa_bfd;
+  if (!abi_msa_bfd
+      && in_attr[Tag_GNU_MIPS_ABI_MSA].i != Val_GNU_MIPS_ABI_MSA_ANY)
+    mips_elf_tdata (obfd)->abi_msa_bfd = ibfd;
+
   if (!elf_known_obj_attributes_proc (obfd)[0].i)
     {
       /* This is the first object.  Copy the attributes.  */
@@ -13692,27 +14362,27 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
   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)
+      if (out_attr[Tag_GNU_MIPS_ABI_FP].i == Val_GNU_MIPS_ABI_FP_ANY)
        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 != Val_GNU_MIPS_ABI_FP_ANY)
        switch (out_attr[Tag_GNU_MIPS_ABI_FP].i)
          {
-         case 1:
+         case Val_GNU_MIPS_ABI_FP_DOUBLE:
            switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
              {
-             case 2:
+             case Val_GNU_MIPS_ABI_FP_SINGLE:
                _bfd_error_handler
                  (_("Warning: %B uses %s (set by %B), %B uses %s"),
                   obfd, abi_fp_bfd, ibfd, "-mdouble-float", "-msingle-float");
                break;
 
-             case 3:
+             case Val_GNU_MIPS_ABI_FP_SOFT:
                _bfd_error_handler
                  (_("Warning: %B uses %s (set by %B), %B uses %s"),
                   obfd, abi_fp_bfd, ibfd, "-mhard-float", "-msoft-float");
                break;
 
-             case 4:
+             case Val_GNU_MIPS_ABI_FP_64:
                _bfd_error_handler
                  (_("Warning: %B uses %s (set by %B), %B uses %s"),
                   obfd, abi_fp_bfd, ibfd,
@@ -13729,22 +14399,22 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
              }
            break;
 
-         case 2:
+         case Val_GNU_MIPS_ABI_FP_SINGLE:
            switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
              {
-             case 1:
+             case Val_GNU_MIPS_ABI_FP_DOUBLE:
                _bfd_error_handler
                  (_("Warning: %B uses %s (set by %B), %B uses %s"),
                   obfd, abi_fp_bfd, ibfd, "-msingle-float", "-mdouble-float");
                break;
 
-             case 3:
+             case Val_GNU_MIPS_ABI_FP_SOFT:
                _bfd_error_handler
                  (_("Warning: %B uses %s (set by %B), %B uses %s"),
                   obfd, abi_fp_bfd, ibfd, "-mhard-float", "-msoft-float");
                break;
 
-             case 4:
+             case Val_GNU_MIPS_ABI_FP_64:
                _bfd_error_handler
                  (_("Warning: %B uses %s (set by %B), %B uses %s"),
                   obfd, abi_fp_bfd, ibfd,
@@ -13761,12 +14431,12 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
              }
            break;
 
-         case 3:
+         case Val_GNU_MIPS_ABI_FP_SOFT:
            switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
              {
-             case 1:
-             case 2:
-             case 4:
+             case Val_GNU_MIPS_ABI_FP_DOUBLE:
+             case Val_GNU_MIPS_ABI_FP_SINGLE:
+             case Val_GNU_MIPS_ABI_FP_64:
                _bfd_error_handler
                  (_("Warning: %B uses %s (set by %B), %B uses %s"),
                   obfd, abi_fp_bfd, ibfd, "-msoft-float", "-mhard-float");
@@ -13782,24 +14452,24 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
              }
            break;
 
-         case 4:
+         case Val_GNU_MIPS_ABI_FP_64:
            switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
              {
-             case 1:
+             case Val_GNU_MIPS_ABI_FP_DOUBLE:
                _bfd_error_handler
                  (_("Warning: %B uses %s (set by %B), %B uses %s"),
                   obfd, abi_fp_bfd, ibfd,
                   "-mips32r2 -mfp64", "-mdouble-float");
                break;
 
-             case 2:
+             case Val_GNU_MIPS_ABI_FP_SINGLE:
                _bfd_error_handler
                  (_("Warning: %B uses %s (set by %B), %B uses %s"),
                   obfd, abi_fp_bfd, ibfd,
                   "-mips32r2 -mfp64", "-msingle-float");
                break;
 
-             case 3:
+             case Val_GNU_MIPS_ABI_FP_SOFT:
                _bfd_error_handler
                  (_("Warning: %B uses %s (set by %B), %B uses %s"),
                   obfd, abi_fp_bfd, ibfd, "-mhard-float", "-msoft-float");
@@ -13818,7 +14488,7 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
          default:
            switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
              {
-             case 1:
+             case Val_GNU_MIPS_ABI_FP_DOUBLE:
                _bfd_error_handler
                  (_("Warning: %B uses unknown floating point ABI %d "
                     "(set by %B), %B uses %s"),
@@ -13826,7 +14496,7 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
                   out_attr[Tag_GNU_MIPS_ABI_FP].i, "-mdouble-float");
                break;
 
-             case 2:
+             case Val_GNU_MIPS_ABI_FP_SINGLE:
                _bfd_error_handler
                  (_("Warning: %B uses unknown floating point ABI %d "
                     "(set by %B), %B uses %s"),
@@ -13834,7 +14504,7 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
                   out_attr[Tag_GNU_MIPS_ABI_FP].i, "-msingle-float");
                break;
 
-             case 3:
+             case Val_GNU_MIPS_ABI_FP_SOFT:
                _bfd_error_handler
                  (_("Warning: %B uses unknown floating point ABI %d "
                     "(set by %B), %B uses %s"),
@@ -13842,7 +14512,7 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
                   out_attr[Tag_GNU_MIPS_ABI_FP].i, "-msoft-float");
                break;
 
-             case 4:
+             case Val_GNU_MIPS_ABI_FP_64:
                _bfd_error_handler
                  (_("Warning: %B uses unknown floating point ABI %d "
                     "(set by %B), %B uses %s"),
@@ -13863,6 +14533,47 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
          }
     }
 
+  /* Check for conflicting Tag_GNU_MIPS_ABI_MSA attributes and merge
+     non-conflicting ones.  */
+  if (in_attr[Tag_GNU_MIPS_ABI_MSA].i != out_attr[Tag_GNU_MIPS_ABI_MSA].i)
+    {
+      out_attr[Tag_GNU_MIPS_ABI_MSA].type = 1;
+      if (out_attr[Tag_GNU_MIPS_ABI_MSA].i == Val_GNU_MIPS_ABI_MSA_ANY)
+       out_attr[Tag_GNU_MIPS_ABI_MSA].i = in_attr[Tag_GNU_MIPS_ABI_MSA].i;
+      else if (in_attr[Tag_GNU_MIPS_ABI_MSA].i != Val_GNU_MIPS_ABI_MSA_ANY)
+       switch (out_attr[Tag_GNU_MIPS_ABI_MSA].i)
+         {
+         case Val_GNU_MIPS_ABI_MSA_128:
+           _bfd_error_handler
+             (_("Warning: %B uses %s (set by %B), "
+                "%B uses unknown MSA ABI %d"),
+              obfd, abi_msa_bfd, ibfd,
+              "-mmsa", in_attr[Tag_GNU_MIPS_ABI_MSA].i);
+           break;
+
+         default:
+           switch (in_attr[Tag_GNU_MIPS_ABI_MSA].i)
+             {
+             case Val_GNU_MIPS_ABI_MSA_128:
+               _bfd_error_handler
+                 (_("Warning: %B uses unknown MSA ABI %d "
+                    "(set by %B), %B uses %s"),
+                    obfd, abi_msa_bfd, ibfd,
+                    out_attr[Tag_GNU_MIPS_ABI_MSA].i, "-mmsa");
+                 break;
+
+             default:
+               _bfd_error_handler
+                 (_("Warning: %B uses unknown MSA ABI %d "
+                    "(set by %B), %B uses unknown MSA ABI %d"),
+                  obfd, abi_msa_bfd, ibfd,
+                  out_attr[Tag_GNU_MIPS_ABI_MSA].i,
+                  in_attr[Tag_GNU_MIPS_ABI_MSA].i);
+               break;
+             }
+         }
+    }
+
   /* Merge Tag_compatibility attributes and any common GNU ones.  */
   _bfd_elf_merge_object_attributes (ibfd, obfd);
 
@@ -14085,6 +14796,20 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
       old_flags &= ~ EF_MIPS_ARCH_ASE;
     }
 
+  /* Compare NaN encodings.  */
+  if ((new_flags & EF_MIPS_NAN2008) != (old_flags & EF_MIPS_NAN2008))
+    {
+      _bfd_error_handler (_("%B: linking %s module with previous %s modules"),
+                         ibfd,
+                         (new_flags & EF_MIPS_NAN2008
+                          ? "-mnan=2008" : "-mnan=legacy"),
+                         (old_flags & EF_MIPS_NAN2008
+                          ? "-mnan=2008" : "-mnan=legacy"));
+      ok = FALSE;
+      new_flags &= ~EF_MIPS_NAN2008;
+      old_flags &= ~EF_MIPS_NAN2008;
+    }
+
   /* Warn about any other mismatches */
   if (new_flags != old_flags)
     {
@@ -14276,6 +15001,12 @@ _bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr)
   if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS)
     fprintf (file, " [micromips]");
 
+  if (elf_elfheader (abfd)->e_flags & EF_MIPS_NAN2008)
+    fprintf (file, " [nan2008]");
+
+  if (elf_elfheader (abfd)->e_flags & EF_MIPS_FP64)
+    fprintf (file, " [fp64]");
+
   if (elf_elfheader (abfd)->e_flags & EF_MIPS_32BITMODE)
     fprintf (file, " [32bitmode]");
   else
@@ -14363,6 +15094,246 @@ _bfd_mips_elf_plt_sym_val (bfd_vma i, const asection *plt,
          + i * 4 * ARRAY_SIZE (mips_exec_plt_entry));
 }
 
+/* Build a table of synthetic symbols to represent the PLT.  As with MIPS16
+   and microMIPS PLT slots we may have a many-to-one mapping between .plt
+   and .got.plt and also the slots may be of a different size each we walk
+   the PLT manually fetching instructions and matching them against known
+   patterns.  To make things easier standard MIPS slots, if any, always come
+   first.  As we don't create proper ELF symbols we use the UDATA.I member
+   of ASYMBOL to carry ISA annotation.  The encoding used is the same as
+   with the ST_OTHER member of the ELF symbol.  */
+
+long
+_bfd_mips_elf_get_synthetic_symtab (bfd *abfd,
+                                   long symcount ATTRIBUTE_UNUSED,
+                                   asymbol **syms ATTRIBUTE_UNUSED,
+                                   long dynsymcount, asymbol **dynsyms,
+                                   asymbol **ret)
+{
+  static const char pltname[] = "_PROCEDURE_LINKAGE_TABLE_";
+  static const char microsuffix[] = "@micromipsplt";
+  static const char m16suffix[] = "@mips16plt";
+  static const char mipssuffix[] = "@plt";
+
+  bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  bfd_boolean micromips_p = MICROMIPS_P (abfd);
+  Elf_Internal_Shdr *hdr;
+  bfd_byte *plt_data;
+  bfd_vma plt_offset;
+  unsigned int other;
+  bfd_vma entry_size;
+  bfd_vma plt0_size;
+  asection *relplt;
+  bfd_vma opcode;
+  asection *plt;
+  asymbol *send;
+  size_t size;
+  char *names;
+  long counti;
+  arelent *p;
+  asymbol *s;
+  char *nend;
+  long count;
+  long pi;
+  long i;
+  long n;
+
+  *ret = NULL;
+
+  if ((abfd->flags & (DYNAMIC | EXEC_P)) == 0 || dynsymcount <= 0)
+    return 0;
+
+  relplt = bfd_get_section_by_name (abfd, ".rel.plt");
+  if (relplt == NULL)
+    return 0;
+
+  hdr = &elf_section_data (relplt)->this_hdr;
+  if (hdr->sh_link != elf_dynsymtab (abfd) || hdr->sh_type != SHT_REL)
+    return 0;
+
+  plt = bfd_get_section_by_name (abfd, ".plt");
+  if (plt == NULL)
+    return 0;
+
+  slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
+  if (!(*slurp_relocs) (abfd, relplt, dynsyms, TRUE))
+    return -1;
+  p = relplt->relocation;
+
+  /* Calculating the exact amount of space required for symbols would
+     require two passes over the PLT, so just pessimise assuming two
+     PLT slots per relocation.  */
+  count = relplt->size / hdr->sh_entsize;
+  counti = count * bed->s->int_rels_per_ext_rel;
+  size = 2 * count * sizeof (asymbol);
+  size += count * (sizeof (mipssuffix) +
+                  (micromips_p ? sizeof (microsuffix) : sizeof (m16suffix)));
+  for (pi = 0; pi < counti; pi += bed->s->int_rels_per_ext_rel)
+    size += 2 * strlen ((*p[pi].sym_ptr_ptr)->name);
+
+  /* Add the size of "_PROCEDURE_LINKAGE_TABLE_" too.  */
+  size += sizeof (asymbol) + sizeof (pltname);
+
+  if (!bfd_malloc_and_get_section (abfd, plt, &plt_data))
+    return -1;
+
+  if (plt->size < 16)
+    return -1;
+
+  s = *ret = bfd_malloc (size);
+  if (s == NULL)
+    return -1;
+  send = s + 2 * count + 1;
+
+  names = (char *) send;
+  nend = (char *) s + size;
+  n = 0;
+
+  opcode = bfd_get_micromips_32 (abfd, plt_data + 12);
+  if (opcode == 0x3302fffe)
+    {
+      if (!micromips_p)
+       return -1;
+      plt0_size = 2 * ARRAY_SIZE (micromips_o32_exec_plt0_entry);
+      other = STO_MICROMIPS;
+    }
+  else if (opcode == 0x0398c1d0)
+    {
+      if (!micromips_p)
+       return -1;
+      plt0_size = 2 * ARRAY_SIZE (micromips_insn32_o32_exec_plt0_entry);
+      other = STO_MICROMIPS;
+    }
+  else
+    {
+      plt0_size = 4 * ARRAY_SIZE (mips_o32_exec_plt0_entry);
+      other = 0;
+    }
+
+  s->the_bfd = abfd;
+  s->flags = BSF_SYNTHETIC | BSF_FUNCTION | BSF_LOCAL;
+  s->section = plt;
+  s->value = 0;
+  s->name = names;
+  s->udata.i = other;
+  memcpy (names, pltname, sizeof (pltname));
+  names += sizeof (pltname);
+  ++s, ++n;
+
+  pi = 0;
+  for (plt_offset = plt0_size;
+       plt_offset + 8 <= plt->size && s < send;
+       plt_offset += entry_size)
+    {
+      bfd_vma gotplt_addr;
+      const char *suffix;
+      bfd_vma gotplt_hi;
+      bfd_vma gotplt_lo;
+      size_t suffixlen;
+
+      opcode = bfd_get_micromips_32 (abfd, plt_data + plt_offset + 4);
+
+      /* Check if the second word matches the expected MIPS16 instruction.  */
+      if (opcode == 0x651aeb00)
+       {
+         if (micromips_p)
+           return -1;
+         /* Truncated table???  */
+         if (plt_offset + 16 > plt->size)
+           break;
+         gotplt_addr = bfd_get_32 (abfd, plt_data + plt_offset + 12);
+         entry_size = 2 * ARRAY_SIZE (mips16_o32_exec_plt_entry);
+         suffixlen = sizeof (m16suffix);
+         suffix = m16suffix;
+         other = STO_MIPS16;
+       }
+      /* Likewise the expected microMIPS instruction (no insn32 mode).  */
+      else if (opcode == 0xff220000)
+       {
+         if (!micromips_p)
+           return -1;
+         gotplt_hi = bfd_get_16 (abfd, plt_data + plt_offset) & 0x7f;
+         gotplt_lo = bfd_get_16 (abfd, plt_data + plt_offset + 2) & 0xffff;
+         gotplt_hi = ((gotplt_hi ^ 0x40) - 0x40) << 18;
+         gotplt_lo <<= 2;
+         gotplt_addr = gotplt_hi + gotplt_lo;
+         gotplt_addr += ((plt->vma + plt_offset) | 3) ^ 3;
+         entry_size = 2 * ARRAY_SIZE (micromips_o32_exec_plt_entry);
+         suffixlen = sizeof (microsuffix);
+         suffix = microsuffix;
+         other = STO_MICROMIPS;
+       }
+      /* Likewise the expected microMIPS instruction (insn32 mode).  */
+      else if ((opcode & 0xffff0000) == 0xff2f0000)
+       {
+         gotplt_hi = bfd_get_16 (abfd, plt_data + plt_offset + 2) & 0xffff;
+         gotplt_lo = bfd_get_16 (abfd, plt_data + plt_offset + 6) & 0xffff;
+         gotplt_hi = ((gotplt_hi ^ 0x8000) - 0x8000) << 16;
+         gotplt_lo = (gotplt_lo ^ 0x8000) - 0x8000;
+         gotplt_addr = gotplt_hi + gotplt_lo;
+         entry_size = 2 * ARRAY_SIZE (micromips_insn32_o32_exec_plt_entry);
+         suffixlen = sizeof (microsuffix);
+         suffix = microsuffix;
+         other = STO_MICROMIPS;
+       }
+      /* Otherwise assume standard MIPS code.  */
+      else
+       {
+         gotplt_hi = bfd_get_32 (abfd, plt_data + plt_offset) & 0xffff;
+         gotplt_lo = bfd_get_32 (abfd, plt_data + plt_offset + 4) & 0xffff;
+         gotplt_hi = ((gotplt_hi ^ 0x8000) - 0x8000) << 16;
+         gotplt_lo = (gotplt_lo ^ 0x8000) - 0x8000;
+         gotplt_addr = gotplt_hi + gotplt_lo;
+         entry_size = 4 * ARRAY_SIZE (mips_exec_plt_entry);
+         suffixlen = sizeof (mipssuffix);
+         suffix = mipssuffix;
+         other = 0;
+       }
+      /* Truncated table???  */
+      if (plt_offset + entry_size > plt->size)
+       break;
+
+      for (i = 0;
+          i < count && p[pi].address != gotplt_addr;
+          i++, pi = (pi + bed->s->int_rels_per_ext_rel) % counti);
+
+      if (i < count)
+       {
+         size_t namelen;
+         size_t len;
+
+         *s = **p[pi].sym_ptr_ptr;
+         /* Undefined syms won't have BSF_LOCAL or BSF_GLOBAL set.  Since
+            we are defining a symbol, ensure one of them is set.  */
+         if ((s->flags & BSF_LOCAL) == 0)
+           s->flags |= BSF_GLOBAL;
+         s->flags |= BSF_SYNTHETIC;
+         s->section = plt;
+         s->value = plt_offset;
+         s->name = names;
+         s->udata.i = other;
+
+         len = strlen ((*p[pi].sym_ptr_ptr)->name);
+         namelen = len + suffixlen;
+         if (names + namelen > nend)
+           break;
+
+         memcpy (names, (*p[pi].sym_ptr_ptr)->name, len);
+         names += len;
+         memcpy (names, suffix, suffixlen);
+         names += suffixlen;
+
+         ++s, ++n;
+         pi = (pi + bed->s->int_rels_per_ext_rel) % counti;
+       }
+    }
+
+  free (plt_data);
+
+  return n;
+}
+
 void
 _bfd_mips_post_process_headers (bfd *abfd, struct bfd_link_info *link_info)
 {
@@ -14378,4 +15349,6 @@ _bfd_mips_post_process_headers (bfd *abfd, struct bfd_link_info *link_info)
       if (htab->use_plts_and_copy_relocs && !htab->is_vxworks)
        i_ehdrp->e_ident[EI_ABIVERSION] = 1;
     }
+
+  _bfd_elf_post_process_headers (abfd, link_info);
 }
This page took 0.095145 seconds and 4 git commands to generate.