daily update
[deliverable/binutils-gdb.git] / bfd / elfxx-mips.c
index a3b50f30d53d6ddcf422e19b8ac66823aedada6e..f51845b7b5e5dbea21cc55496eef509e36cf06f3 100644 (file)
@@ -317,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.  */
 
@@ -381,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.  */
@@ -408,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;
 
@@ -435,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;
@@ -466,6 +510,9 @@ struct mips_elf_link_hash_table
 
   /* 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.  */
@@ -495,6 +542,9 @@ 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;
 
@@ -854,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.  */
@@ -967,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)                 */
@@ -976,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[] =
 {
@@ -1114,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;
@@ -2728,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;
@@ -2737,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
@@ -3213,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
@@ -3948,9 +4089,10 @@ mips_elf_pages_for_range (const struct mips_got_page_range *range)
 /* Record that G requires a page entry that can reach SEC + ADDEND.  */
 
 static bfd_boolean
-mips_elf_record_got_page_entry (struct mips_got_info *g,
+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;
@@ -3967,7 +4109,7 @@ mips_elf_record_got_page_entry (struct mips_got_info *g,
   entry = (struct mips_got_page_entry *) *loc;
   if (!entry)
     {
-      entry = bfd_zalloc (sec->owner, sizeof (*entry));
+      entry = bfd_zalloc (arg->info->output_bfd, sizeof (*entry));
       if (!entry)
        return FALSE;
 
@@ -3987,7 +4129,7 @@ mips_elf_record_got_page_entry (struct mips_got_info *g,
   range = *range_ptr;
   if (!range || addend < range->min_addend - 0xffff)
     {
-      range = bfd_zalloc (sec->owner, sizeof (*range));
+      range = bfd_zalloc (arg->info->output_bfd, sizeof (*range));
       if (!range)
        return FALSE;
 
@@ -4107,7 +4249,7 @@ mips_elf_resolve_got_page_ref (void **refp, void *data)
       else
        addend = isym->st_value + ref->addend;
     }
-  if (!mips_elf_record_got_page_entry (arg->g, sec, addend))
+  if (!mips_elf_record_got_page_entry (arg, sec, addend))
     {
       arg->g = NULL;
       return 0;
@@ -4161,6 +4303,36 @@ mips_elf_resolve_final_got_entries (struct bfd_link_info *info,
   return TRUE;
 }
 
+/* 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.
@@ -4180,25 +4352,15 @@ 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)))
+        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.  */
@@ -4879,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
@@ -5177,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
@@ -5231,16 +5391,16 @@ 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
                   && mips_elf_tdata (input_bfd)->local_call_stubs != NULL
                   && mips_elf_tdata (input_bfd)->local_call_stubs[r_symndx] != NULL))
-          && !target_is_16_bit_code_p)
+          && ((h != NULL && h->use_plt_entry) || !target_is_16_bit_code_p))
     {
       if (local_p)
        sec = mips_elf_tdata (input_bfd)->local_call_stubs[r_symndx];
@@ -5282,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)
@@ -5307,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);
@@ -7350,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;
 }
@@ -7505,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,
@@ -7799,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);
@@ -7831,12 +8010,30 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       /* 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:
@@ -7846,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:
@@ -7874,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:
@@ -7906,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:
@@ -7944,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.  */
@@ -8209,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;
@@ -8611,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.
@@ -8631,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);
+           }
        }
 
-      /* Assign the next .plt entry to this symbol.  */
-      h->plt.offset = htab->splt->size;
-      htab->splt->size += htab->plt_entry_size;
+      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;
+       }
+
+      /* 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));
@@ -8915,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)
 {
+  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;
@@ -8946,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.  */
@@ -8993,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;
        }
     }
@@ -9843,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
@@ -9913,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
@@ -9936,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;
 
@@ -9973,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
@@ -10161,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_.  */
@@ -10192,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
        {
@@ -10216,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);
@@ -10225,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>).  */
@@ -10248,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;
@@ -10315,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;
@@ -10330,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
@@ -10346,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
@@ -10875,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;
@@ -12393,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;
@@ -12675,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;
 
@@ -12688,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)
        {
@@ -12716,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)))
@@ -12743,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))
@@ -12868,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;
 }
@@ -12899,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
@@ -13662,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.  */
@@ -13686,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,
@@ -13723,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,
@@ -13755,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");
@@ -13776,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");
@@ -13812,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"),
@@ -13820,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"),
@@ -13828,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"),
@@ -13836,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"),
@@ -13857,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);
 
@@ -14079,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)
     {
@@ -14270,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
@@ -14357,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)
 {
@@ -14372,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.052555 seconds and 4 git commands to generate.