]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
MIPS: MIPS16 and microMIPS PLT entry support for binutils 2.24
authorMaciej W. Rozycki <macro@codesourcery.com>
Fri, 6 Dec 2013 23:20:14 +0000 (23:20 +0000)
committerMaciej W. Rozycki <macro@codesourcery.com>
Fri, 6 Dec 2013 23:20:14 +0000 (23:20 +0000)
This is a backport from trunk to handle MIPS16 and microMIPS code in
PLT entries as produced by LD from binutils version 2.24 onwards.  This
feature requires explicit support in GDB for correct operation when
debugging through function calls made via the PLT.

bfd/
* elfxx-mips.h (_bfd_mips_elf_get_synthetic_symtab): New
prototype.
* elf32-mips.c (elf_backend_plt_sym_val): Remove macro.
(bfd_elf32_get_synthetic_symtab): New macro.
* elfxx-mips.c (micromips_o32_exec_plt0_entry): New variable.
(micromips_insn32_o32_exec_plt0_entry): Likewise.
(mips16_o32_exec_plt_entry): Likewise.
(micromips_o32_exec_plt_entry): Likewise.
(micromips_insn32_o32_exec_plt_entry): Likewise.
(_bfd_mips_elf_get_synthetic_symtab): New
function.

gdb/
* mips-tdep.c (mips_elf_make_msymbol_special): Handle MIPS16 and
microMIPS synthetic symbols.

opcodes/
* mips-dis.c (is_mips16_plt_tail): New function.
(print_insn_mips16): Handle MIPS16 PLT entry's GOT slot address
word.
(is_compressed_mode_p): Handle MIPS16/microMIPS PLT entries.

bfd/ChangeLog
bfd/elf32-mips.c
bfd/elfxx-mips.c
bfd/elfxx-mips.h
gdb/ChangeLog
gdb/mips-tdep.c
opcodes/ChangeLog
opcodes/mips-dis.c

index 42d6c27dfa74857be1a1af20e5488bb9681aea51..8798b8657230fd4f0bd80d3f30d1324d848aae9f 100644 (file)
@@ -1,3 +1,18 @@
+2013-12-06  Maciej W. Rozycki  <macro@codesourcery.com>
+            Paul Brook  <paul@codesourcery.com>
+
+       * elfxx-mips.h (_bfd_mips_elf_get_synthetic_symtab): New
+       prototype.
+       * elf32-mips.c (elf_backend_plt_sym_val): Remove macro.
+       (bfd_elf32_get_synthetic_symtab): New macro.
+       * elfxx-mips.c (micromips_o32_exec_plt0_entry): New variable.
+       (micromips_insn32_o32_exec_plt0_entry): Likewise.
+       (mips16_o32_exec_plt_entry): Likewise.
+       (micromips_o32_exec_plt_entry): Likewise.
+       (micromips_insn32_o32_exec_plt_entry): Likewise.
+       (_bfd_mips_elf_get_synthetic_symtab): New
+       function.
+
 2013-03-08  Venkataramanan Kumar  <venkataramanan.kumar@linaro.org>
 
         * elf64-aarch64.c (elf_backend_can_gc_sections): Enable gc-section
index cb7692bf08f37e34a33e98246591c40c70cb3748..a65de2489503e3f2fdd32d66c1aa64c1aea3ef59 100644 (file)
@@ -2344,7 +2344,6 @@ static const struct ecoff_debug_swap mips_elf32_ecoff_debug_swap = {
 #define elf_backend_default_use_rela_p 0
 #define elf_backend_sign_extend_vma    TRUE
 #define elf_backend_plt_readonly       1
-#define elf_backend_plt_sym_val                _bfd_mips_elf_plt_sym_val
 
 #define elf_backend_discard_info       _bfd_mips_elf_discard_info
 #define elf_backend_ignore_discarded_relocs \
@@ -2356,6 +2355,7 @@ static const struct ecoff_debug_swap mips_elf32_ecoff_debug_swap = {
                                        mips_elf_is_local_label_name
 #define bfd_elf32_bfd_is_target_special_symbol \
                                        _bfd_mips_elf_is_target_special_symbol
+#define bfd_elf32_get_synthetic_symtab _bfd_mips_elf_get_synthetic_symtab
 #define bfd_elf32_find_nearest_line    _bfd_mips_elf_find_nearest_line
 #define bfd_elf32_find_inliner_info    _bfd_mips_elf_find_inliner_info
 #define bfd_elf32_new_section_hook     _bfd_mips_elf_new_section_hook
@@ -2483,7 +2483,6 @@ mips_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker)
 #define elf_backend_default_use_rela_p         1
 #undef elf_backend_got_header_size
 #define elf_backend_got_header_size            (4 * 3)
-#undef elf_backend_plt_sym_val
 
 #undef elf_backend_finish_dynamic_symbol
 #define elf_backend_finish_dynamic_symbol \
@@ -2509,4 +2508,6 @@ mips_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker)
 #undef elf_backend_symbol_processing
 /* NOTE: elf_backend_rela_normal is not defined for MIPS.  */
 
+#undef bfd_elf32_get_synthetic_symtab
+
 #include "elf32-target.h"
index a5ad454a79bfec131e3ae547338e6e481b33adea..3388cfe9212fd332f382cd2a4303d8a432484c63 100644 (file)
@@ -969,7 +969,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)                 */
@@ -978,6 +1011,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[] =
 {
@@ -14347,6 +14413,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)
 {
index 589f1ce8b67bbb3f6de6b1a92312ccb5b81c601f..45ccabb209068849f8b2cb80354452b744ab3da3 100644 (file)
@@ -152,6 +152,8 @@ extern bfd_boolean _bfd_mips_elf_init_stubs
    asection *(*) (const char *, asection *, asection *));
 extern bfd_vma _bfd_mips_elf_plt_sym_val
   (bfd_vma, const asection *, const arelent *rel);
+extern long _bfd_mips_elf_get_synthetic_symtab
+  (bfd *, long, asymbol **, long, asymbol **, asymbol **);
 extern void _bfd_mips_post_process_headers
   (bfd *abfd, struct bfd_link_info *link_info);
 
index 4944882f0b4a93f39ff2958538dc7cf5a0300f93..16b3a4fb030b932681130d4d890140e60773c208 100644 (file)
@@ -1,3 +1,8 @@
+2013-12-06  Maciej W. Rozycki  <macro@codesourcery.com>
+
+       * mips-tdep.c (mips_elf_make_msymbol_special): Handle MIPS16 and
+       microMIPS synthetic symbols.
+
 2013-09-12  Andrew Pinski  <apinski@cavium.com>
 
        * aarch64-linux-nat.c (aarch64_linux_set_debug_regs): Zero out regs.
index f83bc99295ed3436d4d79c1fa2af13c3c54ad0c3..80e6a78da7c75e0af7483c509f13a19aff72ab7b 100644 (file)
@@ -343,8 +343,9 @@ make_compact_addr (CORE_ADDR addr)
    "special", i.e. refers to a MIPS16 or microMIPS function, and sets
    one of the "special" bits in a minimal symbol to mark it accordingly.
    The test checks an ELF-private flag that is valid for true function
-   symbols only; in particular synthetic symbols such as for PLT stubs
-   have no ELF-private part at all.
+   symbols only; for synthetic symbols such as for PLT stubs that have
+   no ELF-private part at all the MIPS BFD backend arranges for this
+   information to be carried in the asymbol's udata field instead.
 
    msymbol_is_mips16 and msymbol_is_micromips test the "special" bit
    in a minimal symbol.  */
@@ -353,13 +354,18 @@ static void
 mips_elf_make_msymbol_special (asymbol * sym, struct minimal_symbol *msym)
 {
   elf_symbol_type *elfsym = (elf_symbol_type *) sym;
+  unsigned char st_other;
 
-  if ((sym->flags & BSF_SYNTHETIC) != 0)
+  if ((sym->flags & BSF_SYNTHETIC) == 0)
+    st_other = elfsym->internal_elf_sym.st_other;
+  else if ((sym->flags & BSF_FUNCTION) != 0)
+    st_other = sym->udata.i;
+  else
     return;
 
-  if (ELF_ST_IS_MICROMIPS (elfsym->internal_elf_sym.st_other))
+  if (ELF_ST_IS_MICROMIPS (st_other))
     MSYMBOL_TARGET_FLAG_2 (msym) = 1;
-  else if (ELF_ST_IS_MIPS16 (elfsym->internal_elf_sym.st_other))
+  else if (ELF_ST_IS_MIPS16 (st_other))
     MSYMBOL_TARGET_FLAG_1 (msym) = 1;
 }
 
index 66556f1c913c4ef5934d6e78011418ed43f819e1..c86f8cccd476745b208b0f9a8dd761d6d24c92de 100644 (file)
@@ -1,3 +1,10 @@
+2013-12-06  Maciej W. Rozycki  <macro@codesourcery.com>
+
+       * mips-dis.c (is_mips16_plt_tail): New function.
+       (print_insn_mips16): Handle MIPS16 PLT entry's GOT slot address
+       word.
+       (is_compressed_mode_p): Handle MIPS16/microMIPS PLT entries.
+
 2013-03-12  Michael Eager <eager@eagercon.com>
 
        * opcodes/mips-dis.c (print_insn_args): Modify def of reg. 
index 018ac94f302c0c68e2ac7f3a339c18afcfb5857e..9a1ffdec856205421788418a62adce941bb43f59 100644 (file)
@@ -2033,6 +2033,23 @@ print_mips16_insn_arg (char type,
     }
 }
 
+
+/* Check if the given address is the last word of a MIPS16 PLT entry.
+   This word is data and depending on the value it may interfere with
+   disassembly of further PLT entries.  We make use of the fact PLT
+   symbols are marked BSF_SYNTHETIC.  */
+static bfd_boolean
+is_mips16_plt_tail (struct disassemble_info *info, bfd_vma addr)
+{
+  if (info->symbols
+      && info->symbols[0]
+      && (info->symbols[0]->flags & BSF_SYNTHETIC)
+      && addr == bfd_asymbol_value (info->symbols[0]) + 12)
+    return TRUE;
+
+  return FALSE;
+}
+
 /* Disassemble mips16 instructions.  */
 
 static int
@@ -2040,7 +2057,7 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
 {
   const fprintf_ftype infprintf = info->fprintf_func;
   int status;
-  bfd_byte buffer[2];
+  bfd_byte buffer[4];
   int length;
   int insn;
   bfd_boolean use_extend;
@@ -2053,11 +2070,32 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
   info->insn_info_valid = 1;
   info->branch_delay_insns = 0;
   info->data_size = 0;
-  info->insn_type = dis_nonbranch;
   info->target = 0;
   info->target2 = 0;
 
-  status = (*info->read_memory_func) (memaddr, buffer, 2, info);
+  /* Decode PLT entry's GOT slot address word.  */
+  if (is_mips16_plt_tail (info, memaddr))
+    {
+      info->insn_type = dis_noninsn;
+      status = (*info->read_memory_func) (memaddr, buffer, 4, info);
+      if (status == 0)
+       {
+         unsigned int gotslot;
+
+         if (info->endian == BFD_ENDIAN_BIG)
+           gotslot = bfd_getb32 (buffer);
+         else
+           gotslot = bfd_getl32 (buffer);
+         infprintf (is, ".word\t0x%x", gotslot);
+
+         return 4;
+       }
+    }
+  else
+    {
+      info->insn_type = dis_nonbranch;
+      status = (*info->read_memory_func) (memaddr, buffer, 2, info);
+    }
   if (status != 0)
     {
       (*info->memory_error_func) (status, memaddr, info);
@@ -2931,27 +2969,26 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info)
 static bfd_boolean
 is_compressed_mode_p (struct disassemble_info *info)
 {
-  elf_symbol_type *symbol;
-  int pos;
   int i;
-
-  for (i = 0; i < info->num_symbols; i++)
-    {
-      pos = info->symtab_pos + i;
-
-      if (bfd_asymbol_flavour (info->symtab[pos]) != bfd_target_elf_flavour)
-       continue;
-
-      if (info->symtab[pos]->section != info->section)
-       continue;
-
-      symbol = (elf_symbol_type *) info->symtab[pos];
-      if ((!micromips_ase
-          && ELF_ST_IS_MIPS16 (symbol->internal_elf_sym.st_other))
-         || (micromips_ase
-             && ELF_ST_IS_MICROMIPS (symbol->internal_elf_sym.st_other)))
-           return 1;
-    }
+  int l;
+
+  for (i = info->symtab_pos, l = i + info->num_symbols; i < l; i++)
+    if (((info->symtab[i])->flags & BSF_SYNTHETIC) != 0
+       && ((!micromips_ase
+            && ELF_ST_IS_MIPS16 ((*info->symbols)->udata.i))
+           || (micromips_ase
+               && ELF_ST_IS_MICROMIPS ((*info->symbols)->udata.i))))
+      return 1;
+    else if (bfd_asymbol_flavour (info->symtab[i]) == bfd_target_elf_flavour
+             && info->symtab[i]->section == info->section)
+      {
+       elf_symbol_type *symbol = (elf_symbol_type *) info->symtab[i];
+       if ((!micromips_ase
+            && ELF_ST_IS_MIPS16 (symbol->internal_elf_sym.st_other))
+           || (micromips_ase
+               && ELF_ST_IS_MICROMIPS (symbol->internal_elf_sym.st_other)))
+         return 1;
+      }
 
   return 0;
 }