Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH 1/2] MIPS: Compressed PLT/stubs support
@ 2013-02-19 20:44 Maciej W. Rozycki
  2013-02-19 20:45 ` [PATCH 2/2] MIPS: Compressed PLT/stubs support test cases Maciej W. Rozycki
                   ` (3 more replies)
  0 siblings, 4 replies; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-02-19 20:44 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Catherine Moore, binutils, gdb-patches

Hi,

 First an administrative note: this change spans both binutils and GDB -- 
and due to incompatible internal BFD API changes needs to be applied to 
both tools at a time.  I'll take advantage of the shared src tree to push 
it as a single commit when the time comes.

 This change adds support for compressed PLT and traditional SVR4 MIPS 
psABI lazy binding stubs.  Both MIPS16 and microMIPS code is handled, 
because this is how this change was originally developed, although there 
were four different objectives applying to both compressed ISA modes to a 
varying degree:

1. Performance improvement (MIPS16, possibly microMIPS).

2. Code size reduction (microMIPS).

3. Pure compressed code (microMIPS).

4. Tail and short-delay slot call support (microMIPS).

 These are detailed as follows:

1. There is a considerable pipeline reconfiguration overhead at least in 
   some implementations for cross-mode jumps made to switch to and from 
   the MIPS16 mode -- the pipeline has to be drained, the execution 
   decoder reconfigured and instruction fetches for the new mode started 
   from scratch.  The overhead in some cores is I'm told a number between 
   ten and twenty cycles; closer to the latter figure than the former 
   even.  When making a jump from MIPS16 code via the PLT to MIPS16 code 
   (the common MIPS16 use case), this number of course has to be doubled.

   The solution is straightforward, individual PLT entries are reencoded 
   in the MIPS16 mode.  There are limitations on which registers can be 
   used as the destination of memory loads in the MIPS16 mode, so the new 
   code uses $v0 and $v1 as temporaries.  This unfortunately means the 
   resulting code is not any shorter than its standard MIPS counterpart.

   We can however take advantage of a PC-relative load though, so at least 
   the resulting code does not have to be any longer.  Such loads are safe 
   to use, because on systems using the Read Inhibit page protection 
   feature these loads, like code fetches, examine the Execute Inhibit 
   page protection bit instead.  We also don't have to support problematic 
   SPRAM configurations where PC-relative loads go to the data RAM rather 
   than code RAM, as these systems do not support the TLB MMU.

   There is no need to reencode the PLT header as it's going to be 
   executed once per each symbol's lazy resolution only, so it's not 
   worth the complication; all processors that support MIPS16 execution 
   also have to support the standard MIPS mode.

   The performance evaluation of microMIPS cross-mode jumps is not known 
   at this time, however there may be some gain too.

2. Code size reduction can be gained with the use of the microMIPS ADDIUPC
   instruction and 16-bit instruction encodings to access the GOT.  For 
   this to be possible, the GOT has to be within reach from the PLT, 
   specifically half of the range of the instruction whose span is 32MB, 
   signed.  This means the distance between the farthest from each other 
   corresponding PLT and GOT locations cannot exceed 16MB.

   For this the change adjusts the location of the PLT and the GOT with 
   respect to each other -- as arranged by the relevant linker script -- 
   such that they are as close to each other as possibly.  This should 
   suit the fast majority of binaries.  For the remaining few I haven't 
   come across yet that may genuinely have either the PLT or the GOT (with 
   the intervening exception/TLS/init-fini data included) exceed the 16MB 
   limit I have a follow-up change that as a side effect provides a way to 
   address it.

   A further code size reduction is gained the same way where the PLT 
   header is made of microMIPS code as well.  As is where lazy binding 
   stubs are used instead, with the lone use of 16-bit instruction 
   encodings and the assembly-language sequence they correspond to 
   unchanged.

   Just like the MIPS16 mode, the microMIPS mode has some restrictions on 
   register usage and to take advantage of the ADDIUPC instruction 
   individual microMIPS PLT entries use $v0 as a temporary, and the PLT 
   header uses both $v0 and $v1.

3. Further to the consideration #2 above the lazy binding are built as 
   microMIPS code whenever the ELF header of any input files indicates 
   there's microMIPS code within there.  Similarly the PLT header is
   built as microMIPS code, but only if all the individual PLT entries
   are made of microMIPS code too.  The primary reason for this is cache 
   alignment of standard-MIPS individual PLT entries, but there's also a 
   side benefit of $v0 being available for a small size improvement when 
   all the calls to the header are made from microMIPS entries.

   Either condition ultimately means that code already relies on run-time 
   microMIPS support in the intended target environment, so it's safe to 
   emit stubs or the PLT header as microMIPS code.

   This has the effect of making binaries consist of purely microMIPS code
   if no standard MIPS code is present in input already given, ultimately 
   allowing such code to run on processors whose execution unit only 
   supports the microMIPS ISA.

   Note that unlike with PLT, support for compressed code is added for
   NewABI lazy binding stubs -- purely because it was a low-hanging fruit.

4. And finally the use of microMIPS PLT entries enables the use of tail
   calls (direct jumps) and short delay slots as there are suitable 
   microMIPS instructions available to make such control flow changes as 
   long as the execution mode remains unchanged, but there are no such 
   cross-mode jumps available -- i.e. ones whose mnemonics would be JX and 
   JALXS, respectively.  The same applies to cross-mode jumps from the 
   standard  MIPS mode, except there is no concept of short delay slots in 
   the standard MIPS mode, so the lone instruction missing is JX.

   This is addressed by producing extra individual PLT entries as 
   required -- they are allocated as jump relocations are seen in input, 
   matching the respective relocation's instruction encoding.  If there 
   are other relocations then they are resolved to a PLT entry already 
   allocated for jump relocations if present (the standard MIPS entry 
   takes precedence if two PLT entries have been allocated).  If there 
   are no jump relocations and a PLT entry is required, then a microMIPS 
   entry is created for binaries that have the microMIPS flag set in the 
   ELF header, or a standard MIPS entry otherwise.  If two PLT entries 
   have been created, then the standard MIPS entry is used as the 
   corresponding dynamic symbol's value for pointer equality.

 The presence of compressed PLT entries required an update of the API used 
by the disassembler to determine whether code requested is compressed or 
not.  Compressed annotation (ELF symbol's st_other value) is now passed in 
the udata.i member of the asymbol structure used for synthetic symbols.  
The assumption here is that valid pointers will always have some bits set 
in bytes higher than the least significant one.  I hope this assumption is 
going to be OK, please let me know otherwise.  From my look at GDB code 
the only other user of udata is the PPC backend.

 Similarly, to aid the disassembler with microMIPS lazy binding stubs I've 
decided to define a special magic _MIPS_STUBS_ symbol, invented after 
_PROCEDURE_LINKAGE_TABLE_, that has its st_other value set according to 
mode used throughout the table.

 As to the semantics change of the in_plt_section GDB helper -- the `name' 
argument is unused and all the callers pass it as NULL.  I've tracked down 
the history of this function, and it was introduced with GDB 4.13:

Fri Apr  1 00:44:00 1994  Peter Schauer  (pes@regent.e-technik.tu-muenchen.de)

	* sparc-tdep.c (in_solib_trampoline):  Renamed to in_plt_section
	and moved to objfiles.c.
	* objfiles.c (in_plt_section):  Moved to here from sparc-tdep.
	* config/tm-sysv4.h (IN_SOLIB_TRAMPOLINE):  Use new in_plt_section.
	* config/sparc/tm-sun4sol2.h (IN_SOLIB_TRAMPOLINE):  Removed,
	the new generic definition from tm-sysv4.h works for Solaris.

-- with this argument already unused.  Furthermore, `in_solib_trampoline' 
was introduced in GDB 4.9:

Tue Mar 30 15:46:14 1993  K. Richard Pixley  (rich@rtl.cygnus.com)

	* sparc-tdep.c (in_solib_trampoline): new function.
	* config/sparc/tm-sun4sol2.h (IN_SOLIB_TRAMPOLINE): redefine to
	  in_solib_trampoline.

with this argument also unused.  I was unable to track down the pre-4.9
tm-sun4sol2.h version of IN_SOLIB_TRAMPOLINE as GDB 4.8 didn't have the 
macro there yet, so no GDB version was ever released that provided it.  

 However, the tm-sysv4.h version was defined like this:

#define IN_SOLIB_TRAMPOLINE(pc,name) ((name) && (STREQ ("_init", name)))

-- and then redefined in terms of in_plt_section as recorded in the 
ChangeLog entry quoted above like this:

#define IN_SOLIB_TRAMPOLINE(pc, name) in_plt_section((pc), (name))

at which point the `name' argument became unused as well.

 HP-PA had its own version:

#define IN_SOLIB_TRAMPOLINE(pc, name) skip_trampoline_code (pc, name)

-- but skip_trampoline_code didn't make any use of its `name' argument 
either -- just as does't current code in hppa_in_solib_call_trampoline the 
former has evolved to (and neither does code in 
hppa32_hpux_in_solib_call_trampoline, hppa64_hpux_in_solib_call_trampoline 
or hppa_hpux_in_solib_return_trampoline).

 With the above consideration in mind, I think it is safe to redefine 
in_plt_section's API as proposed in this change -- remembering that MIPS 
stubs are the functional equivalent of PLT entries -- for the sake of code 
duplication avoidance.

 This change does not provide a testsuite update to cover the change in 
functionality.  It only updates preexisting test cases so that they do not 
regress as the PLT or lazy binding stubs, as appropriate, are changed.  
New test cases are a huge addition and sent next so as not to clutter 
review of the compressed PLT/stubs support change proper.

 This change was regression-tested with the binutils test suites for the 
mips-sde-elf and mips-linux-gnu targets.  It was likewise tested with the 
GDB test suite for the following configurations/multilibs, both 
endiannesses each:

* o32 (-mabi=32),

* n64 (-mabi=64),

* MIPS16 o32 (-mips16 -mabi=32),

* microMIPS o32 (-mmicromips -mabi=32).

There have been no regressions; however I must note that GDB testing was 
made with the ISA-bit change proposed sometime last year.  I regret I was 
unable to push that change any further and I intend to get back to it as 
time permits.  I am posting the change considered here out of band -- 
specifically microMIPS PLT support it introduces -- for the purpose of 
making upstream integration of GCC microMIPS support easier.

 Nevertheless, it's a fully-functional change, so I will appreciate your 
review, and -- ultimately -- approval of the binutils and the generic GDB 
parts (there's a date to update at commit there, I know); notes on MIPS 
GDB parts are of course welcome as well.

 And last but not least, I think it would be good to have compressed PLT 
support in released GDB before it finds its way into a binutils release as 
single-stepping support relies on getting this right.  So while it's 
relatively late in the game I would appreciate if this change was 
included in the upcoming GDB release as long as its hits the deadline.

2013-02-19  Maciej W. Rozycki  <macro@codesourcery.com>

	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 (plt_entry): New structure.
        (mips_elf_link_hash_entry): Add has_plt_entry member.
        (mips_elf_link_hash_table): Rename plt_entry_size member to
        plt_mips_entry_size.  Add plt_comp_entry_size, plt_mips_offset,
        plt_comp_offset, plt_got_index entries and plt_header_is_comp
	members.
	(STUB_LW_MICROMIPS, STUB_MOVE_MICROMIPS): New macros.
	(STUB_LUI_MICROMIPS, STUB_JALR_MICROMIPS): Likewise.
	(STUB_ORI_MICROMIPS, STUB_LI16U_MICROMIPS): Likewise.
	(STUB_LI16S_MICROMIPS): Likewise.
	(MICROMIPS_FUNCTION_STUB_NORMAL_SIZE): Likewise.
	(MICROMIPS_FUNCTION_STUB_BIG_SIZE): Likewise.
	(micromips_o32_exec_plt0_entry): New variable.
	(mips16_o32_exec_plt_entry): Likewise.
	(micromips_o32_exec_plt_entry): Likewise.
	(mips_elf_link_hash_newfunc): Initialize has_plt_entry.
        (mips_elf_output_extsym): Update to use gotplt_union's plist
        member rather than offset.
        (mips_elf_gotplt_index): Likewise.  Remove the VxWorks
        restriction.
        (mips_elf_calculate_relocation): Handle MIPS16/microMIPS PLT
        entries.  Set the symbol's value in the symbol table for PLT
	references on non-VxWorks targets here.
	(_bfd_mips_elf_create_dynamic_sections): Don't set PLT sizes
	here.
	(mips_elf_make_plt_record): New function.
        (_bfd_mips_elf_check_relocs): Update comment.  Record occurences
        of JAL relocations that might need a PLT entry.
        (_bfd_mips_elf_adjust_dynamic_symbol): Update to use
        gotplt_union's plist member rather than offset.  Set individual
	PLT entry sizes here.  Handle MIPS16/microMIPS PLT entries.
	Don't set the symbol's value in the symbol table for PLT
	references on non-VxWorks targets here.
        (mips_elf_estimate_stub_size): Handle microMIPS stubs.
        (mips_elf_allocate_lazy_stub): Likewise.
        (mips_elf_lay_out_lazy_stubs): Likewise.  Define a _MIPS_STUBS_
        magic symbol.
        (_bfd_mips_elf_size_dynamic_sections): Set PLT header size here.
        Handle microMIPS annotation of the _PROCEDURE_LINKAGE_TABLE_
        magic symbol.
        (_bfd_mips_elf_finish_dynamic_symbol): Update to use
        gotplt_union's plist member rather than offset.  Handle
        MIPS16/microMIPS PLT entries.  Handle microMIPS stubs.
	(_bfd_mips_vxworks_finish_dynamic_symbol): Update to use
	gotplt_union's plist member rather than offset.
	(mips_finish_exec_plt): Handle microMIPS PLT.  Return status.
        (_bfd_mips_elf_finish_dynamic_sections): Handle result from
        mips_finish_exec_plt.
        (_bfd_mips_elf_link_hash_table_create): Update to use
        gotplt_union's plist member rather than offset.
        (_bfd_mips_elf_get_synthetic_symtab): New function.

	gdb/
        * elfread.c (elf_symtab_read): Handle the case where for
        synthetic symbols udata.i is used rather than udata.p.
	* mips-linux-tdep.c (mips_linux_in_dynsym_stub): Handle
	.MIPS.stubs section like .plt.  Remove unused `name' argument.
	Return 1 rather than the low 16-bit halfword of any instruction
	examined.
	(mips_linux_in_dynsym_resolve_code): Update accordingly.
        * mips-tdep.c (mips_elf_make_msymbol_special): Handle MIPS16 and
        microMIPS synthetic symbols.
	(mips_stub_frame_sniffer): Call in_plt_section in place of an
	equivalent hand-coded sequence.
	* objfiles.c (in_plt_section): Reuse the `name' argument as an
	trampoline section name override.

	ld/
	* emulparams/elf32btsmip.sh: Arrange for .got.plt to be placed
	as close to .plt as possible.
        * scripttempl/elf.sc: Handle $INITIAL_READWRITE_SECTIONS and
        $PLT_NEXT_DATA variables.

	ld/testsuite/
	* ld-mips-elf/jalx-2.dd: Update for microMIPS PLT support.
        * ld-mips-elf/pic-and-nonpic-3a.dd: Update for the _MIPS_STUBS_
        magic symbol.
        * ld-mips-elf/pic-and-nonpic-3b.dd: Likewise.
        * ld-mips-elf/pic-and-nonpic-6-n32.dd: Likewise.
        * ld-mips-elf/pic-and-nonpic-6-n64.dd: Likewise.
        * ld-mips-elf/pic-and-nonpic-6-o32.dd: Likewise.
        * ld-mips-elf/stub-dynsym-1-10000.d: Likewise.
        * ld-mips-elf/stub-dynsym-1-2fe80.d: Likewise.
        * ld-mips-elf/stub-dynsym-1-7fff.d: Likewise.
        * ld-mips-elf/stub-dynsym-1-8000.d: Likewise.
        * ld-mips-elf/stub-dynsym-1-fff0.d: Likewise.
        * ld-mips-elf/tlslib-o32.d: Likewise.

	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.

  Maciej

binutils-umips16-plt-stubs.diff
Index: binutils-fsf-trunk-quilt/bfd/elf32-mips.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/bfd/elf32-mips.c	2013-02-19 16:54:37.715088126 +0000
+++ binutils-fsf-trunk-quilt/bfd/elf32-mips.c	2013-02-19 16:55:14.325430517 +0000
@@ -2344,7 +2344,6 @@ static const struct ecoff_debug_swap mip
 #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 mip
 					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
 #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
 #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: binutils-fsf-trunk-quilt/bfd/elfxx-mips.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/bfd/elfxx-mips.c	2013-02-19 16:55:12.715464859 +0000
+++ binutils-fsf-trunk-quilt/bfd/elfxx-mips.c	2013-02-19 18:50:12.755480601 +0000
@@ -319,6 +319,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 of MIPS16 or microMIPS one.  We
+   keep the record of a stub if one is used instead separately, 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.  */
 
@@ -383,6 +409,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 have a PLT entry?  */
+  unsigned int has_plt_entry : 1;
 };
 
 /* MIPS ELF linker hash table.  */
@@ -437,8 +466,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;
@@ -468,6 +509,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.  */
@@ -856,8 +900,28 @@ 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_LUI_MICROMIPS(VAL)						\
+   (0x41b80000 + (VAL))				/* lui t8,VAL */
+#define STUB_JALR_MICROMIPS 0x45d9		/* jalr 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
 
 /* The name of the dynamic interpreter.  This is put in the .interp
    section.  */
@@ -969,7 +1033,26 @@ static const bfd_vma mips_n64_exec_plt0_
   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 subsequent standard PLT entries.  */
 static const bfd_vma mips_exec_plt_entry[] =
 {
   0x3c0f0000,	/* lui $15, %hi(.got.plt entry)			*/
@@ -978,6 +1061,30 @@ 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 the first PLT entry in a VxWorks executable.  */
 static const bfd_vma mips_vxworks_exec_plt0_entry[] =
 {
@@ -1116,6 +1223,7 @@ mips_elf_link_hash_newfunc (struct bfd_h
       ret->need_fn_stub = FALSE;
       ret->has_nonpic_branches = FALSE;
       ret->needs_lazy_stub = FALSE;
+      ret->has_plt_entry = FALSE;
     }
 
   return (struct bfd_hash_entry *) ret;
@@ -2730,6 +2838,8 @@ mips_elf_output_extsym (struct mips_elf_
 
       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;
@@ -2739,7 +2849,7 @@ mips_elf_output_extsym (struct mips_elf_
 	    {
 	      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
@@ -3215,25 +3325,19 @@ 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 * 4);
 
   /* Calculate the value of _GLOBAL_OFFSET_TABLE_.  */
   got_value = (htab->root.hgot->root.u.def.section->output_section->vma
@@ -5098,6 +5202,9 @@ mips_elf_calculate_relocation (bfd *abfd
       /* Record the name of this symbol, for our caller.  */
       *namep = h->root.root.root.string;
 
+      target_is_16_bit_code_p = ELF_ST_IS_MIPS16 (h->root.other);
+      target_is_micromips_code_p = ELF_ST_IS_MICROMIPS (h->root.other);
+
       /* See if this is the special _gp_disp symbol.  Note that such a
 	 symbol must always be a global symbol.  */
       if (strcmp (*namep, "_gp_disp") == 0
@@ -5124,13 +5231,63 @@ mips_elf_calculate_relocation (bfd *abfd
 		|| h->root.root.type == bfd_link_hash_defweak)
 	       && h->root.root.u.def.section)
 	{
-	  sec = h->root.root.u.def.section;
-	  if (sec->output_section)
-	    symbol = (h->root.root.u.def.value
-		      + sec->output_section->vma
-		      + sec->output_offset);
+	  if (h->has_plt_entry)
+	    {
+	      bfd_boolean micromips_p = MICROMIPS_P (abfd);
+	      unsigned int other;
+	      bfd_vma plt_offset;
+	      bfd_vma isa_bit;
+	      bfd_vma val;
+
+	      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);
+
+	      plt_offset = htab->plt_header_size;
+	      if (h->root.plt.plist->comp_offset == MINUS_ONE
+		  || (h->root.plt.plist->mips_offset != MINUS_ONE
+		      && r_type != R_MIPS16_26 && r_type != R_MICROMIPS_26_S1))
+		{
+		  isa_bit = 0;
+		  target_is_16_bit_code_p = FALSE;
+		  target_is_micromips_code_p = FALSE;
+		  plt_offset += h->root.plt.plist->mips_offset;
+		}
+	      else
+		{
+		  isa_bit = 1;
+		  target_is_16_bit_code_p = !micromips_p;
+		  target_is_micromips_code_p = micromips_p;
+		  plt_offset += (htab->plt_mips_offset
+				 + h->root.plt.plist->comp_offset);
+		}
+	      BFD_ASSERT (plt_offset <= htab->splt->size);
+
+	      sec = htab->splt;
+	      val = plt_offset + isa_bit;
+	      symbol = sec->output_section->vma + sec->output_offset + val;
+
+	      /* Set the symbol's value in the symbol table to the address
+	         of the stub too.  Prefer the standard MIPS one.  */
+	      other = 0;
+	      if (h->root.plt.plist->mips_offset != MINUS_ONE)
+		val = htab->plt_header_size + h->root.plt.plist->mips_offset;
+	      else
+		other = micromips_p ? STO_MICROMIPS : STO_MIPS16;
+	      h->root.root.u.def.section = sec;
+	      h->root.root.u.def.value = val;
+	      h->root.other = other;
+	    }
 	  else
-	    symbol = h->root.root.u.def.value;
+	    {
+	      sec = h->root.root.u.def.section;
+	      if (sec->output_section)
+		symbol = (h->root.root.u.def.value
+			  + sec->output_section->vma
+			  + sec->output_offset);
+	      else
+		symbol = h->root.root.u.def.value;
+	    }
 	}
       else if (h->root.root.type == bfd_link_hash_undefweak)
 	/* We allow relocations against undefined weak symbols, giving
@@ -5177,12 +5334,6 @@ mips_elf_calculate_relocation (bfd *abfd
 	{
 	  return bfd_reloc_notsupported;
 	}
-
-      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));
     }
 
   /* If this is a reference to a 16-bit function with a stub, we need
@@ -5242,7 +5393,7 @@ mips_elf_calculate_relocation (bfd *abfd
 	       || (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)
+	   && (!target_is_16_bit_code_p || (h != NULL && h->has_plt_entry)))
     {
       if (local_p)
 	sec = mips_elf_tdata (input_bfd)->local_call_stubs[r_symndx];
@@ -7348,34 +7499,10 @@ _bfd_mips_elf_create_dynamic_sections (b
       || !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;
 }
@@ -7503,8 +7630,29 @@ mips_elf_get_section_contents (bfd *abfd
   return bfd_malloc_and_get_section (abfd, sec, contents);
 }
 
+/* Make a new PLT record to keep internal data.  */
+
+static void
+mips_elf_make_plt_record (bfd *abfd, struct plt_entry **plist)
+{
+  struct plt_entry *entry;
+
+  BFD_ASSERT (plist);
+  entry = bfd_alloc (abfd, sizeof (**plist));
+  if (entry == NULL)
+    return;
+  entry->need_mips = FALSE;
+  entry->need_comp = FALSE;
+  entry->stub_offset = MINUS_ONE;
+  entry->mips_offset = MINUS_ONE;
+  entry->comp_offset = MINUS_ONE;
+  entry->gotplt_index = MINUS_ONE;
+  *plist = 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,
@@ -8201,6 +8349,40 @@ _bfd_mips_elf_check_relocs (bfd *abfd, s
 	  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
+	  && !info->shared
+	  && jal_reloc_p (r_type)
+	  && !SYMBOL_CALLS_LOCAL (info, h)
+	  && !(ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+	       && h->root.type == bfd_link_hash_undefweak))
+      {
+	if (h->plt.plist == NULL)
+	  mips_elf_make_plt_record (abfd, &h->plt.plist);
+	if (h->plt.plist == NULL)
+	  return FALSE;
+	switch (r_type)
+	  {
+	  case R_MIPS16_26:
+	  case R_MICROMIPS_26_S1:
+	    h->plt.plist->need_comp = TRUE;
+	    break;
+
+	  case R_MIPS_26:
+	    h->plt.plist->need_mips = TRUE;
+	    break;
+
+	  default:
+	    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
@@ -8603,11 +8785,16 @@ _bfd_mips_elf_adjust_dynamic_symbol (str
 	   && !(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.  */
+      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->splt->size == 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.
@@ -8623,40 +8810,111 @@ _bfd_mips_elf_adjust_dynamic_symbol (str
 					  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 (micromips_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 (mips16_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;
+      /* See if we preallocated any PLT entries for this symbol.  If none,
+         then if microMIPS code is built, then make a compressed entry or
+         if no microMIPS code is built, then make a standard entry.  Also
+         if a call stub is used, then it is the call stub's standard MIPS
+         code that jumps to the PLT entry, so we may not be able to use a
+         MIPS16 entry in case the stub tail-jumps to it, and in any case
+         we would not benefit from using one, so revert to a standard one
+         in this case too.  Lastly, NewABI and VxWorks targets never use
+         compressed entries.  */
+      if (h->plt.plist == NULL)
+	mips_elf_make_plt_record (dynobj, &h->plt.plist);
+      if (h->plt.plist == NULL)
+	return FALSE;
+      if (h->plt.plist->need_comp && (hmips->call_stub || hmips->call_fp_stub))
+	h->plt.plist->need_comp = FALSE;
+      if (newabi_p || htab->is_vxworks)
+	h->plt.plist->need_mips = !(h->plt.plist->need_comp = FALSE);
+      if (!h->plt.plist->need_mips && !h->plt.plist->need_comp)
+	h->plt.plist->need_mips = !(h->plt.plist->need_comp = micromips_p);
+      if (h->plt.plist->need_mips && h->plt.plist->mips_offset == MINUS_ONE)
+	{
+	  bfd_vma offset;
 
-      /* If the output file has no definition of the symbol, set the
-	 symbol's value to the address of the stub.  */
+	  h->plt.plist->mips_offset = offset = htab->plt_mips_offset;
+	  htab->plt_mips_offset = offset + htab->plt_mips_entry_size;
+	}
+      if (h->plt.plist->need_comp && h->plt.plist->comp_offset == MINUS_ONE)
+	{
+	  bfd_vma offset;
+
+	  h->plt.plist->comp_offset = offset = htab->plt_comp_offset;
+	  htab->plt_comp_offset = offset + htab->plt_comp_entry_size;
+	}
+
+      /* Reserve the corresponding .got.plt entry now too.  */
+      if (h->plt.plist->gotplt_index == MINUS_ONE)
+	{
+	  bfd_vma gpindex;
+
+	  h->plt.plist->gotplt_index = gpindex = htab->plt_got_index;
+	  htab->plt_got_index = gpindex + 1;
+	}
+
+      /* If the output file has no definition of the symbol, we'll use
+         the address of the stub.
+
+         For VxWorks, point at the PLT load stub rather than the lazy
+         resolution stub; this stub will become the canonical function
+         address.
+
+         Otherwise we cannot determine the address of the stub yet, so
+         just record that we'll create a PLT entry for this symbol.  */
       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;
+	    {
+	      h->root.u.def.section = htab->splt;
+	      h->root.u.def.value = h->plt.offset + 8;
+	    }
+	  else
+	    hmips->has_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);
+      htab->splt->size = htab->plt_mips_offset + htab->plt_comp_offset;
+      htab->sgotplt->size = htab->plt_got_index * 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));
@@ -8907,10 +9165,19 @@ mips_elf_estimate_stub_size (bfd *output
   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.  */
+  if (MICROMIPS_P (output_bfd))
+    htab->function_stub_size = (dynsymcount > 0x10000
+				? MICROMIPS_FUNCTION_STUB_BIG_SIZE
+				: MICROMIPS_FUNCTION_STUB_NORMAL_SIZE);
+  else
+    htab->function_stub_size = (dynsymcount > 0x10000
+				? MIPS_FUNCTION_STUB_BIG_SIZE
+				: MIPS_FUNCTION_STUB_NORMAL_SIZE);
 
   htab->sstubs->size = htab->lazy_stub_count * htab->function_stub_size;
 }
@@ -8923,13 +9190,27 @@ static bfd_boolean
 mips_elf_allocate_lazy_stub (struct mips_elf_link_hash_entry *h, void **data)
 {
   struct mips_elf_link_hash_table *htab;
+  struct bfd_link_info *info;
+
+  info = (struct bfd_link_info *) data;
+  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 (info->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)
+	mips_elf_make_plt_record (htab->sstubs->owner, &h->root.plt.plist);
+      if (h->root.plt.plist == NULL)
+	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;
@@ -8938,22 +9219,38 @@ mips_elf_allocate_lazy_stub (struct mips
 /* 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_boolean micromips_p = MICROMIPS_P (info->output_bfd);
+  unsigned int other = micromips_p ? STO_MICROMIPS : 0;
+  bfd_vma isa_bit = micromips_p;
   struct mips_elf_link_hash_table *htab;
+  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);
+  mips_elf_link_hash_traverse (htab, mips_elf_allocate_lazy_stub, info);
   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;
 }
 
 /* Set the sizes of the dynamic sections.  */
@@ -8985,18 +9282,55 @@ _bfd_mips_elf_size_dynamic_sections (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.
+
+         Also create the _PROCEDURE_LINKAGE_TABLE_ symbol if we
+         haven't already in _bfd_elf_create_dynamic_sections.  */
+      if (htab->splt && htab->splt->size > 0)
 	{
+	  bfd_boolean micromips_p = MICROMIPS_P (output_bfd);
+	  bfd_boolean std_mips_p = !micromips_p || htab->plt_mips_offset;
+	  unsigned int other = std_mips_p ? 0 : STO_MICROMIPS;
+	  bfd_vma isa_bit = !std_mips_p;
 	  struct elf_link_hash_entry *h;
+	  bfd_vma size;
 
 	  BFD_ASSERT (htab->use_plts_and_copy_relocs);
 
-	  h = _bfd_elf_define_linkage_sym (dynobj, info, htab->splt,
-					   "_PROCEDURE_LINKAGE_TABLE_");
-	  htab->root.hplt = h;
-	  if (h == NULL)
-	    return FALSE;
+	  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 (std_mips_p)
+	    size = 4 * ARRAY_SIZE (mips_o32_exec_plt0_entry);
+	  else
+	    size = 2 * ARRAY_SIZE (micromips_o32_exec_plt0_entry);
+
+	  htab->plt_header_is_comp = !std_mips_p;
+	  htab->plt_header_size = size;
+	  htab->splt->size += size;
+
+	  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;
 	}
     }
@@ -9835,68 +10169,135 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd
 
   BFD_ASSERT (!htab->is_vxworks);
 
-  if (h->plt.offset != MINUS_ONE && hmips->no_fn_stub)
+  if (h->plt.plist != NULL && hmips->no_fn_stub
+      && (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 = micromips_o32_exec_plt_entry;
+	      bfd_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)
+		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);
+	    }
+	  else
+	    {
+	      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);
+	    }
 	}
 
       /* 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
@@ -9905,21 +10306,39 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd
 	 binary where pointer equality matters.  */
       sym->st_shndx = SHN_UNDEF;
       if (h->pointer_equality_needed)
-	sym->st_other = STO_MIPS_PLT;
+	{
+	  if (ELF_ST_IS_MIPS16 (sym->st_other))
+	    sym->st_other
+	      = ELF_ST_SET_MIPS16 (ELF_ST_SET_MIPS_PLT (sym->st_other));
+	  else
+	    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)
+  else 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);
+      bfd_vma stub_size = htab->function_stub_size;
       bfd_byte stub[MIPS_FUNCTION_STUB_BIG_SIZE];
+      bfd_vma stub_big_size;
+      unsigned int other;
+      bfd_vma isa_bit;
+
+      if (micromips_p)
+	stub_big_size = MICROMIPS_FUNCTION_STUB_BIG_SIZE;
+      else
+	stub_big_size = MIPS_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
@@ -9928,35 +10347,80 @@ _bfd_mips_elf_finish_dynamic_symbol (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 (micromips_p)
+	{
+	  idx = 0;
+	  bfd_put_micromips_32 (output_bfd, STUB_LW_MICROMIPS (output_bfd),
+				stub + idx);
+	  idx += 4;
+	  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;
 
-      /* 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);
+	      bfd_put_micromips_32 (output_bfd,
+				    STUB_LUI_MICROMIPS (dynindx_hi),
+				    stub + idx);
+	      idx += 4;
+	    }
+	  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);
+	  isa_bit = 1;
+	  other = STO_MICROMIPS;
+	}
       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;
 
-      BFD_ASSERT (h->plt.offset <= htab->sstubs->size);
-      memcpy (htab->sstubs->contents + h->plt.offset,
-	      stub, htab->function_stub_size);
+	  /* 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);
+	  isa_bit = 0;
+	  other = 0;
+	}
 
-      /* Mark the symbol as undefined.  plt.offset != -1 occurs
+      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.  stub_offset != -1 occurs
 	 only for the referenced symbol.  */
       sym->st_shndx = SHN_UNDEF;
 
@@ -9965,7 +10429,9 @@ _bfd_mips_elf_finish_dynamic_symbol (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
@@ -10153,30 +10619,32 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
   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 * 4);
 
       /* Calculate the offset of the .got.plt entry from
 	 _GLOBAL_OFFSET_TABLE_.  */
@@ -10184,20 +10652,20 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 
       /* 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 * 4);
 
       /* 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
 	{
@@ -10208,7 +10676,7 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 	  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);
@@ -10217,12 +10685,12 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 	  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>).  */
@@ -10240,7 +10708,8 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 	}
 
       /* 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;
@@ -10307,7 +10776,7 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 
 /* 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;
@@ -10322,6 +10791,8 @@ mips_finish_exec_plt (bfd *output_bfd, s
     plt_entry = mips_n64_exec_plt0_entry;
   else if (ABI_N32_P (output_bfd))
     plt_entry = mips_n32_exec_plt0_entry;
+  else if (htab->plt_header_is_comp)
+    plt_entry = micromips_o32_exec_plt0_entry;
   else
     plt_entry = mips_o32_exec_plt0_entry;
 
@@ -10338,14 +10809,40 @@ mips_finish_exec_plt (bfd *output_bfd, s
 
   /* 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)
+	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
+    {
+      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
@@ -10452,6 +10949,7 @@ _bfd_mips_elf_finish_dynamic_sections (b
   asection *sgot;
   struct mips_got_info *gg, *g;
   struct mips_elf_link_hash_table *htab;
+  bfd_boolean ok = TRUE;
 
   htab = mips_elf_hash_table (info);
   BFD_ASSERT (htab != NULL);
@@ -10867,10 +11365,10 @@ _bfd_mips_elf_finish_dynamic_sections (b
       else
 	{
 	  BFD_ASSERT (!info->shared);
-	  mips_finish_exec_plt (output_bfd, info);
+	  ok = mips_finish_exec_plt (output_bfd, info);
 	}
     }
-  return TRUE;
+  return ok;
 }
 
 
@@ -12860,6 +13358,10 @@ _bfd_mips_elf_link_hash_table_create (bf
       free (ret);
       return NULL;
     }
+  ret->root.init_plt_refcount.refcount = 0;
+  ret->root.init_plt_refcount.plist = NULL;
+  ret->root.init_plt_offset.offset = 0;
+  ret->root.init_plt_offset.plist = NULL;
 
   return &ret->root.root;
 }
@@ -14347,6 +14849,243 @@ _bfd_mips_elf_plt_sym_val (bfd_vma i, co
 	  + 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 addlen;
+  size_t size;
+  char *names;
+  arelent *p;
+  asymbol *s;
+  char *nend;
+  long count;
+  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;
+
+  /* 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;
+  size = 2 * count * sizeof (asymbol);
+  size += count * (sizeof (mipssuffix) +
+		   (micromips_p ? sizeof (microsuffix) : sizeof (m16suffix)));
+  addlen = 2 * (sizeof ("+0x") - 1 + 8);
+#ifdef BFD64
+  addlen += 2 * 8 * (bed->s->elfclass == ELFCLASS64);
+#endif
+  p = relplt->relocation;
+  for (i = 0; i < count; i++, p += bed->s->int_rels_per_ext_rel)
+    {
+      size += 2 * strlen ((*p->sym_ptr_ptr)->name);
+      if (p->addend != 0)
+	size += addlen;
+    }
+
+  /* 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
+    {
+      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;
+
+  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.  */
+      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;
+	}
+      /* 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, p = relplt->relocation;
+	   i < count && p->address != gotplt_addr;
+	   i++, p += bed->s->int_rels_per_ext_rel);
+
+      if (i < count)
+	{
+	  size_t namelen;
+	  size_t len;
+
+	  *s = **p->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->sym_ptr_ptr)->name);
+	  namelen = len + (p->addend != 0 ? addlen : 0) + suffixlen;
+	  if (names + namelen > nend)
+	    break;
+
+	  memcpy (names, (*p->sym_ptr_ptr)->name, len);
+	  names += len;
+	  if (p->addend != 0)
+	    {
+	      char buf[30], *a;
+
+	      memcpy (names, "+0x", sizeof ("+0x") - 1);
+	      names += sizeof ("+0x") - 1;
+	      bfd_sprintf_vma (abfd, buf, p->addend);
+	      for (a = buf; *a == '0'; ++a)
+		;
+	      len = strlen (a);
+	      memcpy (names, a, len);
+	      names += len;
+	    }
+	  memcpy (names, suffix, suffixlen);
+	  names += suffixlen;
+
+	  ++s, ++n;
+	}
+    }
+
+  free (plt_data);
+
+  return n;
+}
+
 void
 _bfd_mips_post_process_headers (bfd *abfd, struct bfd_link_info *link_info)
 {
Index: binutils-fsf-trunk-quilt/bfd/elfxx-mips.h
===================================================================
--- binutils-fsf-trunk-quilt.orig/bfd/elfxx-mips.h	2013-02-19 16:54:37.715088126 +0000
+++ binutils-fsf-trunk-quilt/bfd/elfxx-mips.h	2013-02-19 18:23:24.375438056 +0000
@@ -152,6 +152,8 @@ extern bfd_boolean _bfd_mips_elf_init_st
    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: binutils-fsf-trunk-quilt/gdb/elfread.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/gdb/elfread.c	2013-02-19 16:55:04.524078261 +0000
+++ binutils-fsf-trunk-quilt/gdb/elfread.c	2013-02-19 16:55:14.355453753 +0000
@@ -563,11 +563,17 @@ elf_symtab_read (struct objfile *objfile
 		 ELF-private part.  However, in some cases (e.g. synthetic
 		 'dot' symbols on ppc64) the udata.p entry is set to point back
 		 to the original ELF symbol it was derived from.  Get the size
-		 from that symbol.  */
+		 from that symbol.
+		 NOTE: macro-20130129: The MIPS backend uses the 8 LSBs of
+		 udata.i to store what would normally be provided by the ELF
+		 symbol's st_other field; assume if no higher bits are set,
+		 then udata.i is in use and udata.p is NULL.  */
 	      if (type != ST_SYNTHETIC)
 		elf_sym = (elf_symbol_type *) sym;
-	      else
+	      else if (((sym->udata.i | 0xff) ^ 0xff) != 0)
 		elf_sym = (elf_symbol_type *) sym->udata.p;
+	      else
+		elf_sym = NULL;
 
 	      if (elf_sym)
 		SET_MSYMBOL_SIZE (msym, elf_sym->internal_elf_sym.st_size);
Index: binutils-fsf-trunk-quilt/gdb/mips-linux-tdep.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/gdb/mips-linux-tdep.c	2013-02-19 16:54:37.715088126 +0000
+++ binutils-fsf-trunk-quilt/gdb/mips-linux-tdep.c	2013-02-19 16:55:14.355453753 +0000
@@ -30,6 +30,7 @@
 #include "trad-frame.h"
 #include "tramp-frame.h"
 #include "gdbtypes.h"
+#include "objfiles.h"
 #include "solib.h"
 #include "solib-svr4.h"
 #include "solist.h"
@@ -666,25 +667,34 @@ mips_linux_core_read_description (struct
 
 
 /* Check the code at PC for a dynamic linker lazy resolution stub.
-   Because they aren't in the .plt section, we pattern-match on the
-   code generated by GNU ld.  They look like this:
+   GNU ld for MIPS has put lazy resolution stubs into a ".MIPS.stubs"
+   section uniformly since version 2.15.  If the pc is in that section,
+   then we are in such a stub.  Before that ".stub" was used in 32-bit
+   ELF binaries, however we do not bother checking for that since we
+   have never had and that case should be extremely rare these days.
+   Instead we pattern-match on the code generated by GNU ld.  They look
+   like this:
 
    lw t9,0x8010(gp)
    addu t7,ra
    jalr t9,ra
    addiu t8,zero,INDEX
 
-   (with the appropriate doubleword instructions for N64).  Also
-   return the dynamic symbol index used in the last instruction.  */
+   (with the appropriate doubleword instructions for N64).  As any lazy
+   resolution stubs in microMIPS binaries will always be in a
+   ".MIPS.stubs" section we only ever verify standard MIPS patterns. */
 
 static int
-mips_linux_in_dynsym_stub (CORE_ADDR pc, char *name)
+mips_linux_in_dynsym_stub (CORE_ADDR pc)
 {
   unsigned char buf[28], *p;
   ULONGEST insn, insn1;
   int n64 = (mips_abi (target_gdbarch ()) == MIPS_ABI_N64);
   enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
 
+  if (in_plt_section (pc, ".MIPS.stubs"))
+    return 1;
+
   read_memory (pc - 12, buf, 28);
 
   if (n64)
@@ -742,7 +752,7 @@ mips_linux_in_dynsym_stub (CORE_ADDR pc,
 	return 0;
     }
 
-  return (insn & 0xffff);
+  return 1;
 }
 
 /* Return non-zero iff PC belongs to the dynamic linker resolution
@@ -756,9 +766,10 @@ mips_linux_in_dynsym_resolve_code (CORE_
   if (svr4_in_dynsym_resolve_code (pc))
     return 1;
 
-  /* Pattern match for the stub.  It would be nice if there were a
-     more efficient way to avoid this check.  */
-  if (mips_linux_in_dynsym_stub (pc, NULL))
+  /* Likewise for the stubs.  They live in the .MIPS.stubs section these
+     days, so we check if the PC is within, than fall back to a pattern
+     match.  */
+  if (mips_linux_in_dynsym_stub (pc))
     return 1;
 
   return 0;
Index: binutils-fsf-trunk-quilt/gdb/mips-tdep.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/gdb/mips-tdep.c	2013-02-19 16:54:37.715088126 +0000
+++ binutils-fsf-trunk-quilt/gdb/mips-tdep.c	2013-02-19 16:55:14.355453753 +0000
@@ -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;
 }
 
@@ -3590,12 +3596,7 @@ mips_stub_frame_sniffer (const struct fr
   if (in_plt_section (pc, NULL))
     return 1;
 
-  /* Binutils for MIPS puts lazy resolution stubs into .MIPS.stubs.  */
-  s = find_pc_section (pc);
-
-  if (s != NULL
-      && strcmp (bfd_get_section_name (s->objfile->obfd, s->the_bfd_section),
-		 ".MIPS.stubs") == 0)
+  if (in_plt_section (pc, ".MIPS.stubs"))
     return 1;
 
   /* Calling a PIC function from a non-PIC function passes through a
Index: binutils-fsf-trunk-quilt/gdb/objfiles.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/gdb/objfiles.c	2013-02-19 16:54:37.715088126 +0000
+++ binutils-fsf-trunk-quilt/gdb/objfiles.c	2013-02-19 16:55:14.364142386 +0000
@@ -1383,9 +1383,11 @@ find_pc_section (CORE_ADDR pc)
 }
 
 
-/* In SVR4, we recognize a trampoline by it's section name. 
-   That is, if the pc is in a section named ".plt" then we are in
-   a trampoline.  */
+/* In SVR4, we recognize a trampoline by it's section name.  That is,
+   if the pc is in a section named ".plt" then we are in a trampoline.
+   We let targets request an alternative name, this is currently used
+   by the MIPS backend to handle the SVR4 lazy resolution stubs that
+   binutils put into ".MIPS.stubs" instead.  */
 
 int
 in_plt_section (CORE_ADDR pc, char *name)
@@ -1397,7 +1399,7 @@ in_plt_section (CORE_ADDR pc, char *name
 
   retval = (s != NULL
 	    && s->the_bfd_section->name != NULL
-	    && strcmp (s->the_bfd_section->name, ".plt") == 0);
+	    && strcmp (s->the_bfd_section->name, name ? name : ".plt") == 0);
   return (retval);
 }
 \f
Index: binutils-fsf-trunk-quilt/ld/emulparams/elf32btsmip.sh
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/emulparams/elf32btsmip.sh	2013-02-19 16:54:37.705494896 +0000
+++ binutils-fsf-trunk-quilt/ld/emulparams/elf32btsmip.sh	2013-02-19 16:55:14.364142386 +0000
@@ -8,3 +8,10 @@ LITTLE_OUTPUT_FORMAT="elf32-tradlittlemi
 unset DATA_ADDR
 SHLIB_TEXT_START_ADDR=0
 ENTRY=__start
+
+# Place .got.plt as close to .plt as possible so that the former can be
+# referred to from the latter with the microMIPS ADDIUPC instruction
+# that only has a span of +/-16MB.
+PLT_NEXT_DATA=
+INITIAL_READWRITE_SECTIONS=$OTHER_READWRITE_SECTIONS
+unset OTHER_READWRITE_SECTIONS
Index: binutils-fsf-trunk-quilt/ld/scripttempl/elf.sc
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/scripttempl/elf.sc	2013-02-19 16:54:37.705494896 +0000
+++ binutils-fsf-trunk-quilt/ld/scripttempl/elf.sc	2013-02-19 16:55:14.374058452 +0000
@@ -10,6 +10,7 @@
 #	OTHER_READONLY_SECTIONS - other than .text .init .rodata ...
 #		(e.g., .PARISC.milli)
 #	OTHER_TEXT_SECTIONS - these get put in .text when relocating
+#	INITIAL_READWRITE_SECTIONS - at start of data segment (after relro)
 #	OTHER_READWRITE_SECTIONS - other than .data .bss .ctors .sdata ...
 #		(e.g., .PARISC.global)
 #	OTHER_RELRO_SECTIONS - other than .data.rel.ro ...
@@ -33,6 +34,7 @@
 #	OTHER_SDATA_SECTIONS - sections just after .sdata.
 #	OTHER_BSS_SYMBOLS - symbols that appear at the start of the
 #		.bss section besides __bss_start.
+#	PLT_NEXT_DATA - .plt next to data segment when .plt is in text segment.
 #	DATA_PLT - .plt should be in data segment, not text segment.
 #	PLT_BEFORE_GOT - .plt just before .got when .plt is in data segement.
 #	BSS_PLT - .plt should be in bss segment
@@ -132,7 +134,7 @@ if test -z "$PLT"; then
   PLT=".plt          ${RELOCATING-0} : { *(.plt)${IREL_IN_PLT+ *(.iplt)} }
   ${IREL_IN_PLT-$IPLT}"
 fi
-test -n "${DATA_PLT-${BSS_PLT-text}}" && TEXT_PLT=yes
+test -n "${DATA_PLT-${BSS_PLT-text}}" && TEXT_PLT=
 if test -z "$GOT"; then
   if test -z "$SEPARATE_GOTPLT"; then
     GOT=".got          ${RELOCATING-0} : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }"
@@ -465,7 +467,7 @@ cat <<EOF
     ${RELOCATING+${INIT_END}}
   } ${FILL}
 
-  ${TEXT_PLT+${PLT}}
+  ${TEXT_PLT+${PLT_NEXT_DATA-${PLT}}}
   ${TINY_READONLY_SECTION}
   .text         ${RELOCATING-0} :
   {
@@ -529,6 +531,7 @@ cat <<EOF
   /* These sections are generated by the Sun/Oracle C++ compiler.  */
   .exception_ranges ${RELOCATING-0} : ONLY_IF_RO { *(.exception_ranges
   .exception_ranges*) }
+  ${TEXT_PLT+${PLT_NEXT_DATA+${PLT}}}
 
   /* Adjust the address for the data segment.  We want to adjust up to
      the same address within the page on the next page up.  */
@@ -564,6 +567,7 @@ cat <<EOF
   ${DATA_GOT+${RELRO_NOW+${GOTPLT}}}
   ${DATA_GOT+${RELRO_NOW-${SEPARATE_GOTPLT+${GOT}}}}
   ${RELOCATING+${DATA_SEGMENT_RELRO_END}}
+  ${INITIAL_READWRITE_SECTIONS}
   ${DATA_GOT+${RELRO_NOW-${SEPARATE_GOTPLT-${GOT}}}}
   ${DATA_GOT+${RELRO_NOW-${GOTPLT}}}
 
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/jalx-2.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/jalx-2.dd	2013-02-19 16:54:37.715088126 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/jalx-2.dd	2013-02-19 16:55:14.374058452 +0000
@@ -28,8 +28,8 @@
  4400034:	f89e 0020 	sw	a0,32\(s8\)
  4400038:	f8be 0024 	sw	a1,36\(s8\)
  440003c:	41a2 0440 	lui	v0,0x440
- 4400040:	3082 02a0 	addiu	a0,v0,672
- 4400044:	f110 0028 	jalx	44000a0 <printf@plt>
+ 4400040:	3082 0290 	addiu	a0,v0,656
+ 4400044:	f620 004c 	jal	4400098 <printf@micromipsplt>
  4400048:	0000 0000 	nop
  440004c:	f620 0010 	jal	4400020 <internal_function>
  4400050:	0000 0000 	nop
@@ -44,17 +44,18 @@
 Disassembly of section \.plt:
 
 04400080 <_PROCEDURE_LINKAGE_TABLE_>:
- 4400080:	3c1c0440 	lui	gp,0x440
- 4400084:	8f9900d8 	lw	t9,216\(gp\)
- 4400088:	279c00d8 	addiu	gp,gp,216
- 440008c:	031cc023 	subu	t8,t8,gp
- 4400090:	03e07821 	move	t7,ra
- 4400094:	0018c082 	srl	t8,t8,0x2
- 4400098:	0320f809 	jalr	t9
- 440009c:	2718fffe 	addiu	t8,t8,-2
+ 4400080:	7980 0012 	addiu	v1,\$pc,72
+ 4400084:	ff23 0000 	lw	t9,0\(v1\)
+ 4400088:	0535      	subu	v0,v0,v1
+ 440008a:	2525      	srl	v0,v0,2
+ 440008c:	3302 fffe 	addiu	t8,v0,-2
+ 4400090:	0dff      	move	t7,ra
+ 4400092:	45f9      	jalrs	t9
+ 4400094:	0f83      	move	gp,v1
+ 4400096:	0c00      	nop
 
-044000a0 <printf@plt>:
- 44000a0:	3c0f0440 	lui	t7,0x440
- 44000a4:	8df900e0 	lw	t9,224\(t7\)
- 44000a8:	03200008 	jr	t9
- 44000ac:	25f800e0 	addiu	t8,t7,224
+04400098 <printf@micromipsplt>:
+ 4400098:	7900 000e 	addiu	v0,\$pc,56
+ 440009c:	ff22 0000 	lw	t9,0\(v0\)
+ 44000a0:	4599      	jr	t9
+ 44000a2:	0f02      	move	t8,v0
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-3a.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-3a.dd	2013-02-19 16:54:37.715088126 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-3a.dd	2013-02-19 16:55:14.374058452 +0000
@@ -31,7 +31,7 @@
 #...
 Disassembly of section \.MIPS\.stubs:
 
-00000c00 <.MIPS.stubs>:
+00000c00 <_MIPS_STUBS_>:
  c00:	8f998010 	lw	t9,-32752\(gp\)
  c04:	03e07821 	move	t7,ra
  c08:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-3b.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-3b.dd	2013-02-19 16:54:37.705494896 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-3b.dd	2013-02-19 16:55:14.384830950 +0000
@@ -42,9 +42,10 @@
 .*:	03200008 	jr	t9
 .*:	00000000 	nop
 .*:	00000000 	nop
-Disassembly of section .MIPS.stubs:
 
-00044030 <\.MIPS\.stubs>:
+Disassembly of section \.MIPS\.stubs:
+
+00044030 <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n32.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n32.dd	2013-02-19 16:54:37.705494896 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n32.dd	2013-02-19 16:55:14.394030836 +0000
@@ -91,9 +91,10 @@
    44090:	3c02000a 	lui	v0,0xa
    44094:	24422018 	addiu	v0,v0,8216
 	\.\.\.
+
 Disassembly of section \.MIPS\.stubs:
 
-000440a0 <\.MIPS\.stubs>:
+000440a0 <_MIPS_STUBS_>:
    440a0:	8f998010 	lw	t9,-32752\(gp\)
    440a4:	03e07821 	move	t3,ra
    440a8:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n64.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n64.dd	2013-02-19 16:54:37.715088126 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n64.dd	2013-02-19 16:55:14.394030836 +0000
@@ -91,9 +91,10 @@
    44090:	3c02000a 	lui	v0,0xa
    44094:	24422018 	addiu	v0,v0,8216
 	\.\.\.
+
 Disassembly of section \.MIPS\.stubs:
 
-0+440a0 <\.MIPS\.stubs>:
+0+440a0 <_MIPS_STUBS_>:
    440a0:	df998010 	ld	t9,-32752\(gp\)
    440a4:	03e0782d 	move	t3,ra
    440a8:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-o32.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-o32.dd	2013-02-19 16:54:37.705494896 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-o32.dd	2013-02-19 16:55:14.415431182 +0000
@@ -91,9 +91,10 @@
    44090:	3c02000a 	lui	v0,0xa
    44094:	24422018 	addiu	v0,v0,8216
 	\.\.\.
+
 Disassembly of section \.MIPS\.stubs:
 
-000440a0 <\.MIPS\.stubs>:
+000440a0 <_MIPS_STUBS_>:
    440a0:	8f998010 	lw	t9,-32752\(gp\)
    440a4:	03e07821 	move	t7,ra
    440a8:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-10000.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-10000.d	2013-02-19 16:54:37.705494896 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-10000.d	2013-02-19 16:55:14.415431182 +0000
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	3c180001 	lui	t8,0x1
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-2fe80.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-2fe80.d	2013-02-19 16:54:37.705494896 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-2fe80.d	2013-02-19 16:55:14.424031154 +0000
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	3c180002 	lui	t8,0x2
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-7fff.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-7fff.d	2013-02-19 16:54:37.715088126 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-7fff.d	2013-02-19 16:55:14.424031154 +0000
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-8000.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-8000.d	2013-02-19 16:54:37.715088126 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-8000.d	2013-02-19 16:55:14.424031154 +0000
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-fff0.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-fff0.d	2013-02-19 16:54:37.715088126 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-fff0.d	2013-02-19 16:55:14.445487090 +0000
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/tlslib-o32.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/tlslib-o32.d	2013-02-19 16:54:37.705494896 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/tlslib-o32.d	2013-02-19 16:55:14.454031250 +0000
@@ -35,9 +35,10 @@
  .*:	03e00008 	jr	ra
  .*:	27bd0010 	addiu	sp,sp,16
 	...
+
 Disassembly of section .MIPS.stubs:
 
-.* <.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
  .*:	8f998010 	lw	t9,-32752\(gp\)
  .*:	03e07821 	move	t7,ra
  .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/opcodes/mips-dis.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/opcodes/mips-dis.c	2013-02-19 16:54:37.705494896 +0000
+++ binutils-fsf-trunk-quilt/opcodes/mips-dis.c	2013-02-19 16:55:14.464080952 +0000
@@ -2031,6 +2031,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
@@ -2038,7 +2055,7 @@ print_insn_mips16 (bfd_vma memaddr, stru
 {
   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;
@@ -2051,11 +2068,32 @@ print_insn_mips16 (bfd_vma memaddr, stru
   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);
@@ -2929,27 +2967,26 @@ print_insn_micromips (bfd_vma memaddr, s
 static bfd_boolean
 is_compressed_mode_p (struct disassemble_info *info)
 {
-  elf_symbol_type *symbol;
-  int pos;
   int i;
+  int l;
 
-  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;
-    }
+  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;
 }


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH 2/2] MIPS: Compressed PLT/stubs support test cases
  2013-02-19 20:44 [PATCH 1/2] MIPS: Compressed PLT/stubs support Maciej W. Rozycki
@ 2013-02-19 20:45 ` Maciej W. Rozycki
  2013-02-20 21:53 ` [PATCH 1/2] MIPS: Compressed PLT/stubs support Richard Sandiford
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-02-19 20:45 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Catherine Moore, binutils, gdb-patches

[-- Attachment #1: Type: text/plain, Size: 18734 bytes --]

Hi,

 Not much to say here, except that it's a huge pile of stuff. ;)

 I believe that the selection of samples I made is representative for the 
functionality covered.  It certainly proved itself in that it revealed a 
number of corner-case problems that I have either addressed with separate 
patches already posted to cover individual issues or by adjusting the 
compressed PLT/stubs support change itself.  Which is also why I'm making 
this submission now rather than a fortnight ago as I originally expected.

 I realise this piece is extensive, which is why I have designed this test 
case subset with long-term maintainability in mind.  Therefore the 
mips-elf.exp part is relatively simple (for a reasonable definition of 
"simple"), in that it's a loop over 16 cases tested for various o32
configurations.  The selection of sources chosen for these tests is meant 
to cover various internal paths of code being examined, it is meant to be 
exhaustive.  There is some redundancy however, for example for lazy 
binding stubs some of these individual tests add no value.

 I have decided however, that it will be easier to maintain this uniform 
set of selections rather than having more individual tests prepared for 
each of the individual configuration covered.  This is also why I decided 
there's less harm from some sources being assembled multiple times (i.e. 
some performance hit with testsuite runs) than gain from having the 
individual test cases self-contained.

 Similarly, there's a smaller loop for NewABI tests that just have 2 cases 
to verify compressed code does not sneak into PLT there and check lazy 
stubs at the same time.  The tests may be merged back into the loop used 
for o32 configurations if support for compressed PLT is ever added for 
NewABIs, except that currently we have some restrictions on what MIPS16 
code we can produce for the NewABIs due to missing relocations.  I had to 
address it already in one of the MIPS16 cases where the corresponding 
standard MIPS and microMIPS code uses the DLA macro, but there's no 
equivalent, perhaps using the %higher and %highest operators, in the 
MIPS16 mode.

 Now the good side of this approach is the dumps are easy to maintain.  
Assuming a legitimate regression occurs in the future because of a change 
in functionality, then the testsuite can simply be run to produce all the 
binaries required and failures ignored.  Then dumps can be easily updated 
with a script and differences examined for correctness.

 I can post the script I used (although it's a bit rough) -- I don't 
suppose anybody might have even briefly thought I produced these dumps 
manually. ;)  I did review the results to match expectations though (and 
actually adjusted code accordingly in a couple cases till they matched).

 Comments are welcome, as usually, and I hope this is OK once the change 
proper goes in; this part isn't supposed to change as the test patterns 
are meant to serve as a conformance check.

 Apologies about the ChangeLog entry. ;)

2013-02-19  Maciej W. Rozycki  <macro@codesourcery.com>

	ld/testsuite/
	* ld-mips-elf/mixed-dyn-aux-micromips.dd: New test.
	* ld-mips-elf/mixed-dyn-aux-micromips.nd: New test.
	* ld-mips-elf/mixed-dyn-aux-mips.dd: New test.
	* ld-mips-elf/mixed-dyn-aux-mips.nd: New test.
	* ld-mips-elf/mixed-dyn-aux-mips16.dd: New test.
	* ld-mips-elf/mixed-dyn-aux-mips16.nd: New test.
	* ld-mips-elf/mixed-dyn-aux-n32-mips.dd: New test.
	* ld-mips-elf/mixed-dyn-aux-n32-mips.nd: New test.
	* ld-mips-elf/mixed-dyn-aux-n64-mips.dd: New test.
	* ld-mips-elf/mixed-dyn-aux-n64-mips.nd: New test.
	* ld-mips-elf/mixed-dyn-n32-pic-cmm-cmm-cmm-st.dd: New test.
	* ld-mips-elf/mixed-dyn-n32-pic-cmm-cmm-cmm-st.nd: New test.
	* ld-mips-elf/mixed-dyn-n32-pic-cst-cst-cst-st.dd: New test.
	* ld-mips-elf/mixed-dyn-n32-pic-cst-cst-cst-st.nd: New test.
	* ld-mips-elf/mixed-dyn-n32-plt-c16-c16-c16-st.dd: New test.
	* ld-mips-elf/mixed-dyn-n32-plt-c16-c16-c16-st.nd: New test.
	* ld-mips-elf/mixed-dyn-n32-plt-cmm-cmm-cmm-st.dd: New test.
	* ld-mips-elf/mixed-dyn-n32-plt-cmm-cmm-cmm-st.nd: New test.
	* ld-mips-elf/mixed-dyn-n64-pic-cmm-cmm-cmm-st.dd: New test.
	* ld-mips-elf/mixed-dyn-n64-pic-cmm-cmm-cmm-st.nd: New test.
	* ld-mips-elf/mixed-dyn-n64-pic-cst-cst-cst-st.dd: New test.
	* ld-mips-elf/mixed-dyn-n64-pic-cst-cst-cst-st.nd: New test.
	* ld-mips-elf/mixed-dyn-n64-plt-c16-c16-c16-st.dd: New test.
	* ld-mips-elf/mixed-dyn-n64-plt-c16-c16-c16-st.nd: New test.
	* ld-mips-elf/mixed-dyn-n64-plt-cmm-cmm-cmm-st.dd: New test.
	* ld-mips-elf/mixed-dyn-n64-plt-cmm-cmm-cmm-st.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-c16-c16-c16-16.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-c16-c16-c16-16.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-c16-cst-c16-st.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-c16-cst-c16-st.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cmm-cmm-cmm-mm.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cmm-cmm-cmm-mm.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cmm-cst-cmm-st.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cmm-cst-cmm-st.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-c16-c16-cst-c16-st.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-c16-c16-cst-c16-st.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-c16-cst-16.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-c16-cst-16.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-c16-cst-c16-cst-c16-16.dd: New
	test.
	* ld-mips-elf/mixed-dyn-pic-cst-c16-cst-c16-cst-c16-16.nd: New
	test.
	* ld-mips-elf/mixed-dyn-pic-cst-c16-cst-cst-c16-16.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-c16-cst-cst-c16-16.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-cmm-cmm-cst-cmm-st.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-cmm-cmm-cst-cmm-st.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-cmm-cst-cmm-cst-cmm-mm.dd: New
	test.
	* ld-mips-elf/mixed-dyn-pic-cst-cmm-cst-cmm-cst-cmm-mm.nd: New
	test.
	* ld-mips-elf/mixed-dyn-pic-cst-cmm-cst-cst-cmm-mm.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-cmm-cst-cst-cmm-mm.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-cmm-cst-mm.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-cmm-cst-mm.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-cst-c16-c16-st.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-cst-c16-c16-st.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-cst-cmm-cmm-st.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-cst-cmm-cmm-st.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-cst-cst-st.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-cst-cst-st.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-d16-c16-dst-c16-dst-cst-d16-c16-dst-st.dd:
	New test.
	* ld-mips-elf/mixed-dyn-pic-cst-d16-c16-dst-c16-dst-cst-d16-c16-dst-st.nd:
	New test.
	* ld-mips-elf/mixed-dyn-pic-cst-d16-c16-dst-c16-dst-cst-d16-c16-dst-st.xd:
	New test.
	* ld-mips-elf/mixed-dyn-pic-cst-d16-c16-dst-cst-d16-16.dd: New
	test.
	* ld-mips-elf/mixed-dyn-pic-cst-d16-c16-dst-cst-d16-16.nd: New
	test.
	* ld-mips-elf/mixed-dyn-pic-cst-d16-c16-dst-cst-d16-16.xd: New
	test.
	* ld-mips-elf/mixed-dyn-pic-cst-d16-dst-cst-d16-16.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-d16-dst-cst-d16-16.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-d16-dst-cst-d16-16.xd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dmm-cmm-dst-cmm-dst-cst-dmm-cmm-dst-st.dd:
	New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dmm-cmm-dst-cmm-dst-cst-dmm-cmm-dst-st.nd:
	New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dmm-cmm-dst-cmm-dst-cst-dmm-cmm-dst-st.xd:
	New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dmm-cmm-dst-cst-dmm-mm.dd: New
	test.
	* ld-mips-elf/mixed-dyn-pic-cst-dmm-cmm-dst-cst-dmm-mm.nd: New
	test.
	* ld-mips-elf/mixed-dyn-pic-cst-dmm-cmm-dst-cst-dmm-mm.xd: New
	test.
	* ld-mips-elf/mixed-dyn-pic-cst-dmm-dst-cst-dmm-mm.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dmm-dst-cst-dmm-mm.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dmm-dst-cst-dmm-mm.xd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-c16-d16-cst-dst-c16-d16-cst-dst-c16-d16-16.dd:
	New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-c16-d16-cst-dst-c16-d16-cst-dst-c16-d16-16.nd:
	New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-c16-d16-cst-dst-c16-d16-cst-dst-c16-d16-16.xd:
	New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-cmm-dmm-cst-dst-cmm-dmm-cst-dst-cmm-dmm-mm.dd:
	New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-cmm-dmm-cst-dst-cmm-dmm-cst-dst-cmm-dmm-mm.nd:
	New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-cmm-dmm-cst-dst-cmm-dmm-cst-dst-cmm-dmm-mm.xd:
	New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-cst-dst-cst-dst-st.dd: New
	test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-cst-dst-cst-dst-st.nd: New
	test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-cst-dst-cst-dst-st.xd: New
	test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-d16-c16-st.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-d16-c16-st.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-d16-c16-st.xd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-dmm-cmm-st.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-dmm-cmm-st.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-cst-dst-dmm-cmm-st.xd: New test.
	* ld-mips-elf/mixed-dyn-pic-dst-d16-dst-16.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-dst-d16-dst-16.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-dst-d16-dst-16.xd: New test.
	* ld-mips-elf/mixed-dyn-pic-dst-dmm-dst-mm.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-dst-dmm-dst-mm.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-dst-dmm-dst-mm.xd: New test.
	* ld-mips-elf/mixed-dyn-pic-dst-dst-dst-st.dd: New test.
	* ld-mips-elf/mixed-dyn-pic-dst-dst-dst-st.nd: New test.
	* ld-mips-elf/mixed-dyn-pic-dst-dst-dst-st.xd: New test.
	* ld-mips-elf/mixed-dyn-plt-c16-c16-c16-16.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-c16-c16-c16-16.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-c16-cst-c16-st.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-c16-cst-c16-st.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cmm-cmm-cmm-mm.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cmm-cmm-cmm-mm.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cmm-cst-cmm-st.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cmm-cst-cmm-st.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-c16-c16-cst-c16-st.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-c16-c16-cst-c16-st.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-c16-cst-16.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-c16-cst-16.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-c16-cst-c16-cst-c16-16.dd: New
	test.
	* ld-mips-elf/mixed-dyn-plt-cst-c16-cst-c16-cst-c16-16.nd: New
	test.
	* ld-mips-elf/mixed-dyn-plt-cst-c16-cst-cst-c16-16.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-c16-cst-cst-c16-16.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-cmm-cmm-cst-cmm-st.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-cmm-cmm-cst-cmm-st.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-cmm-cst-cmm-cst-cmm-mm.dd: New
	test.
	* ld-mips-elf/mixed-dyn-plt-cst-cmm-cst-cmm-cst-cmm-mm.nd: New
	test.
	* ld-mips-elf/mixed-dyn-plt-cst-cmm-cst-cst-cmm-mm.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-cmm-cst-cst-cmm-mm.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-cmm-cst-mm.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-cmm-cst-mm.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-cst-c16-c16-st.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-cst-c16-c16-st.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-cst-cmm-cmm-st.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-cst-cmm-cmm-st.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-cst-cst-st.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-cst-cst-st.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-d16-c16-dst-c16-dst-cst-d16-c16-dst-st.dd:
	New test.
	* ld-mips-elf/mixed-dyn-plt-cst-d16-c16-dst-c16-dst-cst-d16-c16-dst-st.nd:
	New test.
	* ld-mips-elf/mixed-dyn-plt-cst-d16-c16-dst-c16-dst-cst-d16-c16-dst-st.xd:
	New test.
	* ld-mips-elf/mixed-dyn-plt-cst-d16-c16-dst-cst-d16-16.dd: New
	test.
	* ld-mips-elf/mixed-dyn-plt-cst-d16-c16-dst-cst-d16-16.nd: New
	test.
	* ld-mips-elf/mixed-dyn-plt-cst-d16-c16-dst-cst-d16-16.xd: New
	test.
	* ld-mips-elf/mixed-dyn-plt-cst-d16-dst-cst-d16-16.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-d16-dst-cst-d16-16.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-d16-dst-cst-d16-16.xd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dmm-cmm-dst-cmm-dst-cst-dmm-cmm-dst-st.dd:
	New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dmm-cmm-dst-cmm-dst-cst-dmm-cmm-dst-st.nd:
	New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dmm-cmm-dst-cmm-dst-cst-dmm-cmm-dst-st.xd:
	New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dmm-cmm-dst-cst-dmm-mm.dd: New
	test.
	* ld-mips-elf/mixed-dyn-plt-cst-dmm-cmm-dst-cst-dmm-mm.nd: New
	test.
	* ld-mips-elf/mixed-dyn-plt-cst-dmm-cmm-dst-cst-dmm-mm.xd: New
	test.
	* ld-mips-elf/mixed-dyn-plt-cst-dmm-dst-cst-dmm-mm.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dmm-dst-cst-dmm-mm.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dmm-dst-cst-dmm-mm.xd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-c16-d16-cst-dst-c16-d16-cst-dst-c16-d16-16.dd:
	New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-c16-d16-cst-dst-c16-d16-cst-dst-c16-d16-16.nd:
	New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-c16-d16-cst-dst-c16-d16-cst-dst-c16-d16-16.xd:
	New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-cmm-dmm-cst-dst-cmm-dmm-cst-dst-cmm-dmm-mm.dd:
	New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-cmm-dmm-cst-dst-cmm-dmm-cst-dst-cmm-dmm-mm.nd:
	New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-cmm-dmm-cst-dst-cmm-dmm-cst-dst-cmm-dmm-mm.xd:
	New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-cst-dst-cst-dst-st.dd: New
	test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-cst-dst-cst-dst-st.nd: New
	test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-cst-dst-cst-dst-st.xd: New
	test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-d16-c16-st.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-d16-c16-st.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-d16-c16-st.xd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-dmm-cmm-st.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-dmm-cmm-st.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-cst-dst-dmm-cmm-st.xd: New test.
	* ld-mips-elf/mixed-dyn-plt-dst-d16-dst-16.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-dst-d16-dst-16.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-dst-d16-dst-16.xd: New test.
	* ld-mips-elf/mixed-dyn-plt-dst-dmm-dst-mm.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-dst-dmm-dst-mm.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-dst-dmm-dst-mm.xd: New test.
	* ld-mips-elf/mixed-dyn-plt-dst-dst-dst-st.dd: New test.
	* ld-mips-elf/mixed-dyn-plt-dst-dst-dst-st.nd: New test.
	* ld-mips-elf/mixed-dyn-plt-dst-dst-dst-st.xd: New test.
	* ld-mips-elf/mixed-dyn-aux-micromips.s: New test source.
	* ld-mips-elf/mixed-dyn-aux-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-aux-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-1-call-micromips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-1-call-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-1-call-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-1-call-n32-micromips.s: New test
	source.
	* ld-mips-elf/mixed-dyn-pic-1-call-n32-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-1-call-n64-micromips.s: New test
	source.
	* ld-mips-elf/mixed-dyn-pic-1-call-n64-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-1-data-micromips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-1-data-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-1-data-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-2-call-micromips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-2-call-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-2-call-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-2-call-n32-micromips.s: New test
	source.
	* ld-mips-elf/mixed-dyn-pic-2-call-n32-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-2-call-n64-micromips.s: New test
	source.
	* ld-mips-elf/mixed-dyn-pic-2-call-n64-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-2-data-micromips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-2-data-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-2-data-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-3-call-micromips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-3-call-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-3-call-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-3-call-n32-micromips.s: New test
	source.
	* ld-mips-elf/mixed-dyn-pic-3-call-n32-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-3-call-n64-micromips.s: New test
	source.
	* ld-mips-elf/mixed-dyn-pic-3-call-n64-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-3-data-micromips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-3-data-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-pic-3-data-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-1-call-micromips.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-1-call-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-1-call-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-1-call-n32-micromips.s: New test
	source.
	* ld-mips-elf/mixed-dyn-plt-1-call-n32-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-1-call-n64-micromips.s: New test
	source.
	* ld-mips-elf/mixed-dyn-plt-1-call-n64-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-1-data-micromips.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-1-data-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-1-data-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-2-call-micromips.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-2-call-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-2-call-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-2-call-n32-micromips.s: New test
	source.
	* ld-mips-elf/mixed-dyn-plt-2-call-n32-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-2-call-n64-micromips.s: New test
	source.
	* ld-mips-elf/mixed-dyn-plt-2-call-n64-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-2-data-micromips.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-2-data-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-2-data-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-3-call-micromips.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-3-call-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-3-call-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-3-call-n32-micromips.s: New test
	source.
	* ld-mips-elf/mixed-dyn-plt-3-call-n32-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-3-call-n64-micromips.s: New test
	source.
	* ld-mips-elf/mixed-dyn-plt-3-call-n64-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-3-data-micromips.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-3-data-mips.s: New test source.
	* ld-mips-elf/mixed-dyn-plt-3-data-mips16.s: New test source.
	* ld-mips-elf/mixed-dyn-pic.ld: New test linker script.
	* ld-mips-elf/mixed-dyn-plt.ld: New test linker script.
	* ld-mips-elf/mips-elf.exp: Run the new tests.

  Maciej

binutils-umips16-plt-stubs-test.diff
[Patch attached compressed due to its size.]

[-- Attachment #2: Type: application/octet-stream, Size: 22126 bytes --]

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-02-19 20:44 [PATCH 1/2] MIPS: Compressed PLT/stubs support Maciej W. Rozycki
  2013-02-19 20:45 ` [PATCH 2/2] MIPS: Compressed PLT/stubs support test cases Maciej W. Rozycki
@ 2013-02-20 21:53 ` Richard Sandiford
  2013-03-09  4:04   ` Maciej W. Rozycki
  2013-02-21 21:06 ` Tom Tromey
  2013-06-07 13:25 ` [PATCH] in_plt_section: support alternate stub section names (was: [PATCH 1/2] MIPS: Compressed PLT/stubs support) Maciej W. Rozycki
  3 siblings, 1 reply; 28+ messages in thread
From: Richard Sandiford @ 2013-02-20 21:53 UTC (permalink / raw)
  To: Maciej W. Rozycki; +Cc: Catherine Moore, binutils, gdb-patches

Looks good.

"Maciej W. Rozycki" <macro@codesourcery.com> writes:
> @@ -3215,25 +3325,19 @@ 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 * 4);
>  
>    /* Calculate the value of _GLOBAL_OFFSET_TABLE_.  */
>    got_value = (htab->root.hgot->root.u.def.section->output_section->vma

If we remove the is_vxworks assert, I think we should use MIPS_ELF_GOT_SIZE
instead of 4.

The patch updates most uses of plt.offset, but we still have:

      else if (htab->is_vxworks
	       && h->got_only_for_calls
	       && h->root.plt.offset != MINUS_ONE)
	/* On VxWorks, calls can refer directly to the .got.plt entry;
	   they don't need entries in the regular GOT.  .got.plt entries
	   will be allocated by _bfd_mips_elf_adjust_dynamic_symbol.  */
	h->global_got_area = GGA_NONE;

Shouldn't that be using plt.plist instead?

> @@ -5124,13 +5231,63 @@ mips_elf_calculate_relocation (bfd *abfd
>  		|| h->root.root.type == bfd_link_hash_defweak)
>  	       && h->root.root.u.def.section)
>  	{
> -	  sec = h->root.root.u.def.section;
> -	  if (sec->output_section)
> -	    symbol = (h->root.root.u.def.value
> -		      + sec->output_section->vma
> -		      + sec->output_offset);
> +	  if (h->has_plt_entry)
> +	    {
> +	      bfd_boolean micromips_p = MICROMIPS_P (abfd);
> +	      unsigned int other;
> +	      bfd_vma plt_offset;
> +	      bfd_vma isa_bit;
> +	      bfd_vma val;
> +
> +	      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);
> +
> +	      plt_offset = htab->plt_header_size;
> +	      if (h->root.plt.plist->comp_offset == MINUS_ONE
> +		  || (h->root.plt.plist->mips_offset != MINUS_ONE
> +		      && r_type != R_MIPS16_26 && r_type != R_MICROMIPS_26_S1))
> +		{
> +		  isa_bit = 0;
> +		  target_is_16_bit_code_p = FALSE;
> +		  target_is_micromips_code_p = FALSE;
> +		  plt_offset += h->root.plt.plist->mips_offset;
> +		}
> +	      else
> +		{
> +		  isa_bit = 1;
> +		  target_is_16_bit_code_p = !micromips_p;
> +		  target_is_micromips_code_p = micromips_p;
> +		  plt_offset += (htab->plt_mips_offset
> +				 + h->root.plt.plist->comp_offset);
> +		}

When can we have a microMIPS call to a PLT without a microMIPS PLT for
it to call?  I was expecting something like:

    if (r_type == R_MIPS16_26 || r_type == R_MICROMIPS_26_S1)
      {
	BFD_ASSERT (h->root.plt.plist->comp_offset != MINUS_ONE);
	...
      }
    else
      {
	BFD_ASSERT (h->root.plt.plist->mips_offset != MINUS_ONE);
	...
      }

although see the comment about MIPS16 stubs below.

> +	      BFD_ASSERT (plt_offset <= htab->splt->size);
> +
> +	      sec = htab->splt;
> +	      val = plt_offset + isa_bit;
> +	      symbol = sec->output_section->vma + sec->output_offset + val;
> +
> +	      /* Set the symbol's value in the symbol table to the address
> +	         of the stub too.  Prefer the standard MIPS one.  */
> +	      other = 0;
> +	      if (h->root.plt.plist->mips_offset != MINUS_ONE)
> +		val = htab->plt_header_size + h->root.plt.plist->mips_offset;
> +	      else
> +		other = micromips_p ? STO_MICROMIPS : STO_MIPS16;
> +	      h->root.root.u.def.section = sec;
> +	      h->root.root.u.def.value = val;
> +	      h->root.other = other;

calculate_relocation doesn't feel like the right place to do this.
We should either do it in size_dynamic_sections (where we also set _P_L_T_)
or finish_dynamic_symbol.  Probably the former.  E.g. we could do it in
mips_elf_allocate_lazy_stub, renaming that and mips_elf_lay_out_lazy_stubs
to something more general.

> @@ -5242,7 +5393,7 @@ mips_elf_calculate_relocation (bfd *abfd
>  	       || (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)
> +	   && (!target_is_16_bit_code_p || (h != NULL && h->has_plt_entry)))
>      {
>        if (local_p)
>  	sec = mips_elf_tdata (input_bfd)->local_call_stubs[r_symndx];

It'd be worth describing this in the comment (and getting rid of the weird
"64-bit" thing that seems to be there now :-)).  Something like:

  /* If this is a MIPS16 call that has an associated call stub, we need to
     use that stub when calling non-MIPS16 functions or PLTs (which themselves
     are MIPS16, but the target might not be).  Note that we specifically
     exclude R_MIPS16_CALL16 from this behavior; indirect calls should
     use an indirect stub instead.  */

if that's accurate.  Although, if it is, why are we creating and choosing
a MIPS16 PLT in the first place?  It looks like you rightly try to avoid
that further down.

> +/* Make a new PLT record to keep internal data.  */
> +
> +static void
> +mips_elf_make_plt_record (bfd *abfd, struct plt_entry **plist)
> +{
> +  struct plt_entry *entry;
> +
> +  BFD_ASSERT (plist);
> +  entry = bfd_alloc (abfd, sizeof (**plist));
> +  if (entry == NULL)
> +    return;
> +  entry->need_mips = FALSE;
> +  entry->need_comp = FALSE;

Please use zalloc and remove the FALSE stuff.

I think it would be cleaner to return a boolean success code.

> @@ -8201,6 +8349,40 @@ _bfd_mips_elf_check_relocs (bfd *abfd, s
>  	  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
> +	  && !info->shared
> +	  && jal_reloc_p (r_type)
> +	  && !SYMBOL_CALLS_LOCAL (info, h)
> +	  && !(ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
> +	       && h->root.type == bfd_link_hash_undefweak))

It feels strange to check the last condition in check_relocs.  I realise
you added it because the same condition is used in adjust_dynamic_symbol
(and is boilerplate elsewhere too, across targets), but I think it'd be
better to abstract away:

	   && htab->use_plts_and_copy_relocs
	   && !SYMBOL_CALLS_LOCAL (info, h)
	   && !(ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
		&& h->root.type == bfd_link_hash_undefweak))

(I think we should be using use_plts_and_copy_relocs here too.)

> +
> +	  /* 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 (micromips_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 (mips16_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;
> +      /* See if we preallocated any PLT entries for this symbol.  If none,
> +         then if microMIPS code is built, then make a compressed entry or
> +         if no microMIPS code is built, then make a standard entry.  Also
> +         if a call stub is used, then it is the call stub's standard MIPS
> +         code that jumps to the PLT entry, so we may not be able to use a
> +         MIPS16 entry in case the stub tail-jumps to it, and in any case
> +         we would not benefit from using one, so revert to a standard one
> +         in this case too.  Lastly, NewABI and VxWorks targets never use
> +         compressed entries.  */
> +      if (h->plt.plist == NULL)
> +	mips_elf_make_plt_record (dynobj, &h->plt.plist);
> +      if (h->plt.plist == NULL)
> +	return FALSE;
> +      if (h->plt.plist->need_comp && (hmips->call_stub || hmips->call_fp_stub))
> +	h->plt.plist->need_comp = FALSE;
> +      if (newabi_p || htab->is_vxworks)
> +	h->plt.plist->need_mips = !(h->plt.plist->need_comp = FALSE);
> +      if (!h->plt.plist->need_mips && !h->plt.plist->need_comp)
> +	h->plt.plist->need_mips = !(h->plt.plist->need_comp = micromips_p);

Suggest:

      if (!h->plt.plist && !mips_elf_make_plt_record (dynobj, &h->plt.plist))
	return FALSE;

      /* There are no defined microMIPS PLT entries for VxWorks, n32 or n64,
	 so always use a standard entry there.

	 If the symbol has a MIPS16 call stub, then all MIPS16 calls
	 should go via that stub, and there is no benefit to having a
	 MIPS16 entry.  */
      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 == MINUS_ONE)
> +	{
> +	  bfd_vma offset;
>  
> -      /* If the output file has no definition of the symbol, set the
> -	 symbol's value to the address of the stub.  */
> +	  h->plt.plist->mips_offset = offset = htab->plt_mips_offset;
> +	  htab->plt_mips_offset = offset + htab->plt_mips_entry_size;
> +	}

When is mips_offset not MINUS_ONE?

The offset variable seems a bit obfuscating.  I'd prefer:

	  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 == MINUS_ONE)
> +	{
> +	  bfd_vma offset;
> +
> +	  h->plt.plist->comp_offset = offset = htab->plt_comp_offset;
> +	  htab->plt_comp_offset = offset + htab->plt_comp_entry_size;
> +	}

Same comments here.

> +
> +      /* Reserve the corresponding .got.plt entry now too.  */
> +      if (h->plt.plist->gotplt_index == MINUS_ONE)
> +	{
> +	  bfd_vma gpindex;
> +
> +	  h->plt.plist->gotplt_index = gpindex = htab->plt_got_index;
> +	  htab->plt_got_index = gpindex + 1;
> +	}

Here too.

> +
> +      /* If the output file has no definition of the symbol, we'll use
> +         the address of the stub.
> +
> +         For VxWorks, point at the PLT load stub rather than the lazy
> +         resolution stub; this stub will become the canonical function
> +         address.
> +
> +         Otherwise we cannot determine the address of the stub yet, so
> +         just record that we'll create a PLT entry for this symbol.  */

I'd prefer we set the address in the same place for VxWorks.  Also:

>  	{
> -	  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;
> +	    {
> +	      h->root.u.def.section = htab->splt;
> +	      h->root.u.def.value = h->plt.offset + 8;
> +	    }
> +	  else
> +	    hmips->has_plt_entry = TRUE;
>  	}

shouldn't the plt.offset be plt.plist->...?

> @@ -8923,13 +9190,27 @@ static bfd_boolean
>  mips_elf_allocate_lazy_stub (struct mips_elf_link_hash_entry *h, void **data)
>  {
>    struct mips_elf_link_hash_table *htab;
> +  struct bfd_link_info *info;
> +
> +  info = (struct bfd_link_info *) data;
> +  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 (info->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)
> +	mips_elf_make_plt_record (htab->sstubs->owner, &h->root.plt.plist);
> +      if (h->root.plt.plist == NULL)
> +	return FALSE;

This won't propagate the error up.  Other callbacks typically handle
this by having data be a pointer to a pointer that gets nullified
on error.  Or you could simply allocate the PLT record when setting
needs_lazy_stub in adjust_dynamic_symbol, I don't mind which.

Also, preexisting problem, but it should be "void *data" rather than
"void **data".

> @@ -8985,18 +9282,55 @@ _bfd_mips_elf_size_dynamic_sections (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.
> +
> +         Also create the _PROCEDURE_LINKAGE_TABLE_ symbol if we
> +         haven't already in _bfd_elf_create_dynamic_sections.  */
> +      if (htab->splt && htab->splt->size > 0)
>  	{
> +	  bfd_boolean micromips_p = MICROMIPS_P (output_bfd);
> +	  bfd_boolean std_mips_p = !micromips_p || htab->plt_mips_offset;
> +	  unsigned int other = std_mips_p ? 0 : STO_MICROMIPS;
> +	  bfd_vma isa_bit = !std_mips_p;
>  	  struct elf_link_hash_entry *h;
> +	  bfd_vma size;

I'd prefer this as:

	  bfd_boolean micromips_p = (MICROMIPS_P (output_bfd)
				     && htab->plt_mips_offset == 0);
	  unsigned int other = micromips_p ? STO_MICROMIPS : 0;
	  bfd_vma isa_bit = micromips_p;

which also matches what you did earlier.

> @@ -9835,68 +10169,135 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd
>  
>    BFD_ASSERT (!htab->is_vxworks);
>  
> -  if (h->plt.offset != MINUS_ONE && hmips->no_fn_stub)
> +  if (h->plt.plist != NULL && hmips->no_fn_stub
> +      && (h->plt.plist->mips_offset != MINUS_ONE
> +	  || h->plt.plist->comp_offset != MINUS_ONE))

The old code was written that way because plt.offset was used for both PLTs
and lazy stubs.  I don't think we need or want to test hmips->no_fn_stub now.
By the same token, the "else if" should probably now be "if".

> +      /* 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 = micromips_o32_exec_plt_entry;
> +	      bfd_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)
> +		return FALSE;

We shouldn't just return false without reporting an error.

> @@ -9905,21 +10306,39 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd
>  	 binary where pointer equality matters.  */
>        sym->st_shndx = SHN_UNDEF;
>        if (h->pointer_equality_needed)
> -	sym->st_other = STO_MIPS_PLT;
> +	{
> +	  if (ELF_ST_IS_MIPS16 (sym->st_other))
> +	    sym->st_other
> +	      = ELF_ST_SET_MIPS16 (ELF_ST_SET_MIPS_PLT (sym->st_other));
> +	  else
> +	    sym->st_other = ELF_ST_SET_MIPS_PLT (sym->st_other);
> +	}

Please update the definition of ELF_ST_SET_MIPS_PLT instead, so that
STO_MIPS16 gets preserved in the same way as STO_MICROMIPS.

> +  else 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);
> +      bfd_vma stub_size = htab->function_stub_size;
>        bfd_byte stub[MIPS_FUNCTION_STUB_BIG_SIZE];
> +      bfd_vma stub_big_size;
> +      unsigned int other;
> +      bfd_vma isa_bit;
> +
> +      if (micromips_p)
> +	stub_big_size = MICROMIPS_FUNCTION_STUB_BIG_SIZE;
> +      else
> +	stub_big_size = MIPS_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
> @@ -9928,35 +10347,80 @@ _bfd_mips_elf_finish_dynamic_symbol (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 (micromips_p)
> +	{
[...]
> +	  isa_bit = 1;
> +	  other = STO_MICROMIPS;

Minor consistency thing, but please use the same idiom as you used elsewhere.

> @@ -10338,14 +10809,40 @@ mips_finish_exec_plt (bfd *output_bfd, s
>  
>    /* 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)
> +	return FALSE;

Here too we shouldn't just return false without reporting an error.

> @@ -10452,6 +10949,7 @@ _bfd_mips_elf_finish_dynamic_sections (b
>    asection *sgot;
>    struct mips_got_info *gg, *g;
>    struct mips_elf_link_hash_table *htab;
> +  bfd_boolean ok = TRUE;
>  
>    htab = mips_elf_hash_table (info);
>    BFD_ASSERT (htab != NULL);
> @@ -10867,10 +11365,10 @@ _bfd_mips_elf_finish_dynamic_sections (b
>        else
>  	{
>  	  BFD_ASSERT (!info->shared);
> -	  mips_finish_exec_plt (output_bfd, info);
> +	  ok = mips_finish_exec_plt (output_bfd, info);
>  	}
>      }
> -  return TRUE;
> +  return ok;
>  }

No need for the ok variable.  Just return early on error.

> @@ -12860,6 +13358,10 @@ _bfd_mips_elf_link_hash_table_create (bf
>        free (ret);
>        return NULL;
>      }
> +  ret->root.init_plt_refcount.refcount = 0;
> +  ret->root.init_plt_refcount.plist = NULL;
> +  ret->root.init_plt_offset.offset = 0;
> +  ret->root.init_plt_offset.plist = NULL;

These are unions, so we should only initialise one field each.

> +      for (i = 0, p = relplt->relocation;
> +	   i < count && p->address != gotplt_addr;
> +	   i++, p += bed->s->int_rels_per_ext_rel);

This looks needlessly quadratic.  If we start the search from the previous
match, won't a well-formed .plt only need two walks of the relocs?

> +      if (i < count)
> +	{
> +	  size_t namelen;
> +	  size_t len;
> +
> +	  *s = **p->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->sym_ptr_ptr)->name);
> +	  namelen = len + (p->addend != 0 ? addlen : 0) + suffixlen;
> +	  if (names + namelen > nend)
> +	    break;
> +
> +	  memcpy (names, (*p->sym_ptr_ptr)->name, len);
> +	  names += len;
> +	  if (p->addend != 0)
> +	    {
> +	      char buf[30], *a;
> +
> +	      memcpy (names, "+0x", sizeof ("+0x") - 1);
> +	      names += sizeof ("+0x") - 1;
> +	      bfd_sprintf_vma (abfd, buf, p->addend);
> +	      for (a = buf; *a == '0'; ++a)
> +		;
> +	      len = strlen (a);
> +	      memcpy (names, a, len);
> +	      names += len;
> +	    }

Maybe I'm showing my ignorance, but when do R_MIPS_JUMP_SLOTs have addends?
If they shouldn't, lets just ignore the addend or skip entries with nonzero
addends.

Rather than making a general assumption that udata.i == st_other for
BSF_SYNTHETIC, I think we should just do it for MIPS, and only (for now)
when the defined symbol is in a section called .plt.  What do others think?

Thanks,
Richard


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-02-19 20:44 [PATCH 1/2] MIPS: Compressed PLT/stubs support Maciej W. Rozycki
  2013-02-19 20:45 ` [PATCH 2/2] MIPS: Compressed PLT/stubs support test cases Maciej W. Rozycki
  2013-02-20 21:53 ` [PATCH 1/2] MIPS: Compressed PLT/stubs support Richard Sandiford
@ 2013-02-21 21:06 ` Tom Tromey
  2013-02-22  0:58   ` Alan Modra
  2013-06-20 16:20   ` [PING^2][PATCH] in_plt_section: support alternate stub section names (was: [PATCH 1/2] MIPS: Compressed PLT/stubs support) Maciej W. Rozycki
  2013-06-07 13:25 ` [PATCH] in_plt_section: support alternate stub section names (was: [PATCH 1/2] MIPS: Compressed PLT/stubs support) Maciej W. Rozycki
  3 siblings, 2 replies; 28+ messages in thread
From: Tom Tromey @ 2013-02-21 21:06 UTC (permalink / raw)
  To: Maciej W. Rozycki
  Cc: Richard Sandiford, Catherine Moore, binutils, gdb-patches

>>>>> "Maciej" == Maciej W Rozycki <macro@codesourcery.com> writes:

Maciej> +		 NOTE: macro-20130129: The MIPS backend uses the 8 LSBs of

I prefer not putting usernames and dates into the comments.
That is readily found in the history -- but also more commonly just not
needed.

Maciej>  	      if (type != ST_SYNTHETIC)
Maciej>  		elf_sym = (elf_symbol_type *) sym;
Maciej> -	      else
Maciej> +	      else if (((sym->udata.i | 0xff) ^ 0xff) != 0)
Maciej>  		elf_sym = (elf_symbol_type *) sym->udata.p;
Maciej> +	      else
Maciej> +		elf_sym = NULL;

This seems iffy to me.  I suppose what gives me pause is the possibility
that this condition will trigger on some other port that does something
odd in BFD.

I'm not really an expert here -- I'd appreciate other comments.

Maciej> -/* In SVR4, we recognize a trampoline by it's section name. 
Maciej> -   That is, if the pc is in a section named ".plt" then we are in
Maciej> -   a trampoline.  */
Maciej> +/* In SVR4, we recognize a trampoline by it's section name.  That is,
Maciej> +   if the pc is in a section named ".plt" then we are in a trampoline.
Maciej> +   We let targets request an alternative name, this is currently used
Maciej> +   by the MIPS backend to handle the SVR4 lazy resolution stubs that
Maciej> +   binutils put into ".MIPS.stubs" instead.  */

IMO comments like this tend to get stale over time.
It is better, I think, to describe the function's interface and purpose,
and leave it to future developers to grep for uses of it, should they
need to know.

Tom


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-02-21 21:06 ` Tom Tromey
@ 2013-02-22  0:58   ` Alan Modra
  2013-02-22  6:06     ` Alan Modra
  2013-06-20 16:20   ` [PING^2][PATCH] in_plt_section: support alternate stub section names (was: [PATCH 1/2] MIPS: Compressed PLT/stubs support) Maciej W. Rozycki
  1 sibling, 1 reply; 28+ messages in thread
From: Alan Modra @ 2013-02-22  0:58 UTC (permalink / raw)
  To: Tom Tromey
  Cc: Maciej W. Rozycki, Richard Sandiford, Catherine Moore, binutils,
	gdb-patches

On Thu, Feb 21, 2013 at 02:06:34PM -0700, Tom Tromey wrote:
> Maciej>  	      if (type != ST_SYNTHETIC)
> Maciej>  		elf_sym = (elf_symbol_type *) sym;
> Maciej> -	      else
> Maciej> +	      else if (((sym->udata.i | 0xff) ^ 0xff) != 0)
> Maciej>  		elf_sym = (elf_symbol_type *) sym->udata.p;
> Maciej> +	      else
> Maciej> +		elf_sym = NULL;
> 
> This seems iffy to me.  I suppose what gives me pause is the possibility
> that this condition will trigger on some other port that does something
> odd in BFD.

ppc64 is the only target that currently provides get_synthetic_symtab
returning non-NULL values of udata.

We have

  union
    {
      void *p;
      bfd_vma i;
    }
  udata;

"i" is always 64 bits when ppc64 is supported by BFD.  The size of "p"
depends on the host.  If they happen to both be 64-bit the analysis is
easy and Maciej's change will break ppc64 if "p" can have an address
in the range [0,255].  For 32-bit little-endian hosts we get the same
result.  For 32-bit big-endian hosts, "p" occupies the high 32 bits of
"i", so any non-NULL value of "p" will pass the new test.

I doubt we care about hosts that might allocate memory from the zero
page, so Maciej's change is OK as far as I'm concerned.

Hmm, perhaps a cleaner change would be implement make_msymbol_special
for ppc64 and move the udata.p special case out of elf_symtab_read?

-- 
Alan Modra
Australia Development Lab, IBM


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-02-22  0:58   ` Alan Modra
@ 2013-02-22  6:06     ` Alan Modra
  2013-02-22 20:09       ` Tom Tromey
  0 siblings, 1 reply; 28+ messages in thread
From: Alan Modra @ 2013-02-22  6:06 UTC (permalink / raw)
  To: Tom Tromey, gdb-patches

On Fri, Feb 22, 2013 at 11:28:33AM +1030, Alan Modra wrote:
> Hmm, perhaps a cleaner change would be implement make_msymbol_special
> for ppc64 and move the udata.p special case out of elf_symtab_read?

Like so.

	* elfread.c (elf_symtab_read): Do not use udata.p here to find
	symbol size.
	* ppc64-tdep.c (ppc64_elf_make_msymbol_special): New function.
	* ppc64-tdep.h (ppc64_elf_make_msymbol_special): Declare.
	* ppc-linux-tdep.c (ppc_linux_init_abi): Set up to use the above.
	* ppcfbsd-tdep.c (ppcfbsd_init_abi): Likewise.

Index: gdb/elfread.c
===================================================================
RCS file: /cvs/src/src/gdb/elfread.c,v
retrieving revision 1.145
diff -u -p -r1.145 elfread.c
--- gdb/elfread.c	21 Feb 2013 04:35:21 -0000	1.145
+++ gdb/elfread.c	22 Feb 2013 05:42:53 -0000
@@ -556,21 +556,14 @@ elf_symtab_read (struct objfile *objfile
 
 	  if (msym)
 	    {
-	      /* Pass symbol size field in via BFD.  FIXME!!!  */
-	      elf_symbol_type *elf_sym;
-
 	      /* NOTE: uweigand-20071112: A synthetic symbol does not have an
-		 ELF-private part.  However, in some cases (e.g. synthetic
-		 'dot' symbols on ppc64) the udata.p entry is set to point back
-		 to the original ELF symbol it was derived from.  Get the size
-		 from that symbol.  */
+		 ELF-private part.  */
 	      if (type != ST_SYNTHETIC)
-		elf_sym = (elf_symbol_type *) sym;
-	      else
-		elf_sym = (elf_symbol_type *) sym->udata.p;
-
-	      if (elf_sym)
-		SET_MSYMBOL_SIZE (msym, elf_sym->internal_elf_sym.st_size);
+		{
+		  /* Pass symbol size field in via BFD.  FIXME!!!  */
+		  elf_symbol_type *elf_sym = (elf_symbol_type *) sym;
+		  SET_MSYMBOL_SIZE (msym, elf_sym->internal_elf_sym.st_size);
+		}
 
 	      msym->filename = filesymname;
 	      gdbarch_elf_make_msymbol_special (gdbarch, sym, msym);
Index: gdb/ppc-linux-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ppc-linux-tdep.c,v
retrieving revision 1.143
diff -u -p -r1.143 ppc-linux-tdep.c
--- gdb/ppc-linux-tdep.c	4 Feb 2013 18:40:41 -0000	1.143
+++ gdb/ppc-linux-tdep.c	22 Feb 2013 05:42:53 -0000
@@ -1336,6 +1336,9 @@ ppc_linux_init_abi (struct gdbarch_info 
       set_gdbarch_convert_from_func_ptr_addr
 	(gdbarch, ppc64_convert_from_func_ptr_addr);
 
+      set_gdbarch_elf_make_msymbol_special (gdbarch,
+					    ppc64_elf_make_msymbol_special);
+
       /* Shared library handling.  */
       set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code);
       set_solib_svr4_fetch_link_map_offsets
Index: gdb/ppc64-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ppc64-tdep.c,v
retrieving revision 1.1
diff -u -p -r1.1 ppc64-tdep.c
--- gdb/ppc64-tdep.c	1 Feb 2013 20:59:08 -0000	1.1
+++ gdb/ppc64-tdep.c	22 Feb 2013 05:42:53 -0000
@@ -22,6 +22,7 @@
 #include "gdbcore.h"
 #include "ppc-tdep.h"
 #include "ppc64-tdep.h"
+#include "elf-bfd.h"
 
 /* Macros for matching instructions.  Note that, since all the
    operands are masked off before they're or-ed into the instruction,
@@ -361,3 +362,17 @@ ppc64_convert_from_func_ptr_addr (struct
 
   return addr;
 }
+
+/* A synthetic 'dot' symbols on ppc64 has the udata.p entry pointing
+   back to the original ELF symbol it was derived from.  Get the size
+   from that symbol.  */
+
+void
+ppc64_elf_make_msymbol_special (asymbol *sym, struct minimal_symbol *msym)
+{
+  if ((sym->flags & BSF_SYNTHETIC) != 0 && sym->udata.p != NULL)
+    {
+      elf_symbol_type *elf_sym = (elf_symbol_type *) sym->udata.p;
+      SET_MSYMBOL_SIZE (msym, elf_sym->internal_elf_sym.st_size);
+    }
+}
Index: gdb/ppc64-tdep.h
===================================================================
RCS file: /cvs/src/src/gdb/ppc64-tdep.h,v
retrieving revision 1.1
diff -u -p -r1.1 ppc64-tdep.h
--- gdb/ppc64-tdep.h	1 Feb 2013 20:59:08 -0000	1.1
+++ gdb/ppc64-tdep.h	22 Feb 2013 05:42:53 -0000
@@ -31,4 +31,6 @@ extern CORE_ADDR ppc64_convert_from_func
 						   CORE_ADDR addr,
 						   struct target_ops *targ);
 
+extern void ppc64_elf_make_msymbol_special (asymbol *,
+					    struct minimal_symbol *);
 #endif /* PPC64_TDEP_H  */
Index: gdb/ppcfbsd-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ppcfbsd-tdep.c,v
retrieving revision 1.1
diff -u -p -r1.1 ppcfbsd-tdep.c
--- gdb/ppcfbsd-tdep.c	4 Feb 2013 20:48:53 -0000	1.1
+++ gdb/ppcfbsd-tdep.c	22 Feb 2013 05:42:53 -0000
@@ -325,6 +325,9 @@ ppcfbsd_init_abi (struct gdbarch_info in
     {
       set_gdbarch_convert_from_func_ptr_addr
 	(gdbarch, ppc64_convert_from_func_ptr_addr);
+      set_gdbarch_elf_make_msymbol_special (gdbarch,
+					    ppc64_elf_make_msymbol_special);
+
       set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code);
       set_solib_svr4_fetch_link_map_offsets (gdbarch,
 					     svr4_lp64_fetch_link_map_offsets);

-- 
Alan Modra
Australia Development Lab, IBM


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-02-22  6:06     ` Alan Modra
@ 2013-02-22 20:09       ` Tom Tromey
  2013-03-09  4:06         ` Maciej W. Rozycki
  0 siblings, 1 reply; 28+ messages in thread
From: Tom Tromey @ 2013-02-22 20:09 UTC (permalink / raw)
  To: Alan Modra; +Cc: gdb-patches

>>>>> "Alan" == Alan Modra <amodra@gmail.com> writes:

Alan> Hmm, perhaps a cleaner change would be implement make_msymbol_special
Alan> for ppc64 and move the udata.p special case out of elf_symtab_read?

Alan> Like so.

Alan> 	* elfread.c (elf_symtab_read): Do not use udata.p here to find
Alan> 	symbol size.
Alan> 	* ppc64-tdep.c (ppc64_elf_make_msymbol_special): New function.
Alan> 	* ppc64-tdep.h (ppc64_elf_make_msymbol_special): Declare.
Alan> 	* ppc-linux-tdep.c (ppc_linux_init_abi): Set up to use the above.
Alan> 	* ppcfbsd-tdep.c (ppcfbsd_init_abi): Likewise.

I like this much better, thanks.
This is ok.

Tom


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-02-20 21:53 ` [PATCH 1/2] MIPS: Compressed PLT/stubs support Richard Sandiford
@ 2013-03-09  4:04   ` Maciej W. Rozycki
  2013-03-09  9:58     ` Richard Sandiford
  2013-03-11 13:53     ` Joel Brobecker
  0 siblings, 2 replies; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-03-09  4:04 UTC (permalink / raw)
  To: Joel Brobecker, Richard Sandiford; +Cc: Catherine Moore, binutils, gdb-patches

Joel,

 What's the current ETA for 7.6?  By now I have run out of time, I'll be 
off the next two weeks and won't be able to do anything about this change 
even if Richard accepts it right away (unlikely).

 The thing is microMIPS functionality in GCC relies on this piece and has 
been scheduled for inclusion in GCC 4.9.  For that to be reasonable we 
need to have this change in released binutils, which means binutils 2.24, 
currently expected this summer 
(http://sourceware.org/ml/binutils/2013-01/msg00328.html).  And given that 
any binary making use of the new functionality will break single-stepping 
over PLT in GDB I think it will make sense to have a proper GDB release 
with these bits *before* the corresponding binutils release.  Now 7.6 
being released now would be ideal and I hoped I would make it, but the 
number and substance of changes requested and my other commitments 
regrettably put it beyond my reach.

 Of course it's still possible that a fortnight away you'll still not have 
rolled 7.6 out for one reason or another, but assuming that all goes well, 
what is the prospect of having 7.7 released before binutils 2.24?

 Now as to the patch itself...

On Wed, 20 Feb 2013, Richard Sandiford wrote:

> Looks good.

 Ah, I see -- half of it is rubbish, but otherwise OK. ;)

> "Maciej W. Rozycki" <macro@codesourcery.com> writes:
> > @@ -3215,25 +3325,19 @@ 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 * 4);
> >  
> >    /* Calculate the value of _GLOBAL_OFFSET_TABLE_.  */
> >    got_value = (htab->root.hgot->root.u.def.section->output_section->vma
> 
> If we remove the is_vxworks assert, I think we should use MIPS_ELF_GOT_SIZE
> instead of 4.

 Not sure if this is related to this change, but I see no problem with 
that either.  I've updated all the references throughout.

> The patch updates most uses of plt.offset, but we still have:
> 
>       else if (htab->is_vxworks
> 	       && h->got_only_for_calls
> 	       && h->root.plt.offset != MINUS_ONE)
> 	/* On VxWorks, calls can refer directly to the .got.plt entry;
> 	   they don't need entries in the regular GOT.  .got.plt entries
> 	   will be allocated by _bfd_mips_elf_adjust_dynamic_symbol.  */
> 	h->global_got_area = GGA_NONE;
> 
> Shouldn't that be using plt.plist instead?

 An obvious oversight, fixed.

> > @@ -5124,13 +5231,63 @@ mips_elf_calculate_relocation (bfd *abfd
> >  		|| h->root.root.type == bfd_link_hash_defweak)
> >  	       && h->root.root.u.def.section)
> >  	{
> > -	  sec = h->root.root.u.def.section;
> > -	  if (sec->output_section)
> > -	    symbol = (h->root.root.u.def.value
> > -		      + sec->output_section->vma
> > -		      + sec->output_offset);
> > +	  if (h->has_plt_entry)
> > +	    {
> > +	      bfd_boolean micromips_p = MICROMIPS_P (abfd);
> > +	      unsigned int other;
> > +	      bfd_vma plt_offset;
> > +	      bfd_vma isa_bit;
> > +	      bfd_vma val;
> > +
> > +	      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);
> > +
> > +	      plt_offset = htab->plt_header_size;
> > +	      if (h->root.plt.plist->comp_offset == MINUS_ONE
> > +		  || (h->root.plt.plist->mips_offset != MINUS_ONE
> > +		      && r_type != R_MIPS16_26 && r_type != R_MICROMIPS_26_S1))
> > +		{
> > +		  isa_bit = 0;
> > +		  target_is_16_bit_code_p = FALSE;
> > +		  target_is_micromips_code_p = FALSE;
> > +		  plt_offset += h->root.plt.plist->mips_offset;
> > +		}
> > +	      else
> > +		{
> > +		  isa_bit = 1;
> > +		  target_is_16_bit_code_p = !micromips_p;
> > +		  target_is_micromips_code_p = micromips_p;
> > +		  plt_offset += (htab->plt_mips_offset
> > +				 + h->root.plt.plist->comp_offset);
> > +		}
> 
> When can we have a microMIPS call to a PLT without a microMIPS PLT for
> it to call?  I was expecting something like:
> 
>     if (r_type == R_MIPS16_26 || r_type == R_MICROMIPS_26_S1)
>       {
> 	BFD_ASSERT (h->root.plt.plist->comp_offset != MINUS_ONE);
> 	...
>       }
>     else
>       {
> 	BFD_ASSERT (h->root.plt.plist->mips_offset != MINUS_ONE);
> 	...
>       }

 I didn't (and still don't) want to hardcode decisions made across several 
places.  What kind of PLT entry to make is decided upon elsewhere, in a 
single place, and code here merely respects that decision.

 Besides both asserts you propose are bogus.  You'll get R_MIPS16_26 or 
R_MICROMIPS_26_S1 relocs referring to standard MIPS PLT entries with the 
new ABIs.  This invalidates the first assertion.  You'll get other relocs 
referring to compressed (microMIPS) PLT entries when the microMIPS ASE bit 
is set in the ELF header and there were no R_MIPS_26 relocs referring to 
the respective symbol.  That invalidates the second assertion.  So it's 
not like you can infer the type of the PLT entry available/to use from the 
reloc type alone, there are other factors.

> although see the comment about MIPS16 stubs below.
> 
> > +	      BFD_ASSERT (plt_offset <= htab->splt->size);
> > +
> > +	      sec = htab->splt;
> > +	      val = plt_offset + isa_bit;
> > +	      symbol = sec->output_section->vma + sec->output_offset + val;
> > +
> > +	      /* Set the symbol's value in the symbol table to the address
> > +	         of the stub too.  Prefer the standard MIPS one.  */
> > +	      other = 0;
> > +	      if (h->root.plt.plist->mips_offset != MINUS_ONE)
> > +		val = htab->plt_header_size + h->root.plt.plist->mips_offset;
> > +	      else
> > +		other = micromips_p ? STO_MICROMIPS : STO_MIPS16;
> > +	      h->root.root.u.def.section = sec;
> > +	      h->root.root.u.def.value = val;
> > +	      h->root.other = other;
> 
> calculate_relocation doesn't feel like the right place to do this.
> We should either do it in size_dynamic_sections (where we also set _P_L_T_)
> or finish_dynamic_symbol.  Probably the former.  E.g. we could do it in
> mips_elf_allocate_lazy_stub, renaming that and mips_elf_lay_out_lazy_stubs
> to something more general.

 Thanks for the hint -- I did not want to waste time in another iteration 
loop over the hash table and sort of did not find a better place to stick 
it.

 In the end I did create another loop -- we already have four in 
_bfd_mips_elf_size_dynamic_sections: allocate_dynrelocs, 
mips_elf_count_got_symbols and mips_elf_initialize_tls_index (both via 
mips_elf_lay_out_got), and mips_elf_allocate_lazy_stub (via 
mips_elf_lay_out_lazy_stubs), so I don't think there's any point in making 
an artificial binding of two unrelated actions like this.  It may make 
sense to go through the effort to combine all these pieces as a later step 
though -- not strictly related to this functionality though.

> > @@ -5242,7 +5393,7 @@ mips_elf_calculate_relocation (bfd *abfd
> >  	       || (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)
> > +	   && (!target_is_16_bit_code_p || (h != NULL && h->has_plt_entry)))
> >      {
> >        if (local_p)
> >  	sec = mips_elf_tdata (input_bfd)->local_call_stubs[r_symndx];
> 
> It'd be worth describing this in the comment (and getting rid of the weird
> "64-bit" thing that seems to be there now :-)).  Something like:
> 
>   /* If this is a MIPS16 call that has an associated call stub, we need to
>      use that stub when calling non-MIPS16 functions or PLTs (which themselves
>      are MIPS16, but the target might not be).  Note that we specifically
>      exclude R_MIPS16_CALL16 from this behavior; indirect calls should
>      use an indirect stub instead.  */
> 
> if that's accurate.  Although, if it is, why are we creating and choosing
> a MIPS16 PLT in the first place?  It looks like you rightly try to avoid
> that further down.

 No that's not accurate, the PLT entry is standard MIPS code in this case, 
because the stub the call is redirected to is also standard MIPS code.  
The change is to emphasize any calls through the PLT use the standard MIPS 
ABI no matter what the PLT entry looks like so technically these are two 
independent conditions; this in fact could be a separate change applied to 
our trunk as it is now, except that the h->has_plt_entry condition would 
have to be replaced with (!info->shared && !h->def_regular) or suchlike 
(probably with an additional condition making sure the symbol is a 
function too).

 The current code flow is a bit subtle, this piece works because as a side 
effect of the PLT being standard MIPS code the call is qualified as a 
cross-mode jump.  However this is not really the reason the call needs to 
be redirected for -- the redirection would have to be done regardless 
even if we did decide to emit the PLT entry as MIPS16 code for some 
reason.

 I have therefore decided to reverse the order of the conditions checked, 
which is functionally equivalent, but I think a bit friendlier to the 
reader.  I think this redundancy is worth having for the sake of code 
clarity; BFD is already hard to follow even without any such implicit 
dependencies.  I have also reworded the comment accordingly.  I hope this 
is OK with you -- both my point and the new description.

> > +/* Make a new PLT record to keep internal data.  */
> > +
> > +static void
> > +mips_elf_make_plt_record (bfd *abfd, struct plt_entry **plist)
> > +{
> > +  struct plt_entry *entry;
> > +
> > +  BFD_ASSERT (plist);
> > +  entry = bfd_alloc (abfd, sizeof (**plist));
> > +  if (entry == NULL)
> > +    return;
> > +  entry->need_mips = FALSE;
> > +  entry->need_comp = FALSE;
> 
> Please use zalloc and remove the FALSE stuff.
> 
> I think it would be cleaner to return a boolean success code.

 I decided to avoid the unnecessary pass-by-reference API complication and 
just pass the pointer to the newly allocated structure as the return value 
instead.  I saw you clean up some allocations to use bfd_zalloc recently, 
but somehow it did not trigger the conclusion I could do it here too; now 
done.

> > @@ -8201,6 +8349,40 @@ _bfd_mips_elf_check_relocs (bfd *abfd, s
> >  	  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
> > +	  && !info->shared
> > +	  && jal_reloc_p (r_type)
> > +	  && !SYMBOL_CALLS_LOCAL (info, h)
> > +	  && !(ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
> > +	       && h->root.type == bfd_link_hash_undefweak))
> 
> It feels strange to check the last condition in check_relocs.  I realise
> you added it because the same condition is used in adjust_dynamic_symbol
> (and is boilerplate elsewhere too, across targets), but I think it'd be
> better to abstract away:
> 
> 	   && htab->use_plts_and_copy_relocs
> 	   && !SYMBOL_CALLS_LOCAL (info, h)
> 	   && !(ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
> 		&& h->root.type == bfd_link_hash_undefweak))
> 
> (I think we should be using use_plts_and_copy_relocs here too.)

 I think jal_reloc_p already implies it, but I agree for the sake of code 
legibility it might make sense to make it explicit.  Unfortunately 
use_plts_and_copy_relocs has not been set this early yet (it's only 
initialised in mips_before_allocation, after all input has already been 
read in), so it cannot be relied on to be accurate here; adding such a 
check causes numerous regressions in the test suite.

 I agree the visibility check is premature here, and it is never going to 
matter here anyway, as it only determines whether a PLT entry will 
eventually be created at all and not its type, so I removed it altogether.

 Also I simplified the body of the conditional a bit as having looked into 
it again I realised it has suffered from some cruft accumulation (this 
code has been revised).

 Lastly, I have corrected an indentation mistake that has been there from 
the inception of this piece for no explicable reason.

> > -      /* Assign the next .plt entry to this symbol.  */
> > -      h->plt.offset = htab->splt->size;
> > -      htab->splt->size += htab->plt_entry_size;
> > +      /* See if we preallocated any PLT entries for this symbol.  If none,
> > +         then if microMIPS code is built, then make a compressed entry or
> > +         if no microMIPS code is built, then make a standard entry.  Also
> > +         if a call stub is used, then it is the call stub's standard MIPS
> > +         code that jumps to the PLT entry, so we may not be able to use a
> > +         MIPS16 entry in case the stub tail-jumps to it, and in any case
> > +         we would not benefit from using one, so revert to a standard one
> > +         in this case too.  Lastly, NewABI and VxWorks targets never use
> > +         compressed entries.  */
> > +      if (h->plt.plist == NULL)
> > +	mips_elf_make_plt_record (dynobj, &h->plt.plist);
> > +      if (h->plt.plist == NULL)
> > +	return FALSE;
> > +      if (h->plt.plist->need_comp && (hmips->call_stub || hmips->call_fp_stub))
> > +	h->plt.plist->need_comp = FALSE;
> > +      if (newabi_p || htab->is_vxworks)
> > +	h->plt.plist->need_mips = !(h->plt.plist->need_comp = FALSE);
> > +      if (!h->plt.plist->need_mips && !h->plt.plist->need_comp)
> > +	h->plt.plist->need_mips = !(h->plt.plist->need_comp = micromips_p);
> 
> Suggest:
> 
>       if (!h->plt.plist && !mips_elf_make_plt_record (dynobj, &h->plt.plist))
> 	return FALSE;
> 
>       /* There are no defined microMIPS PLT entries for VxWorks, n32 or n64,
> 	 so always use a standard entry there.
> 
> 	 If the symbol has a MIPS16 call stub, then all MIPS16 calls
> 	 should go via that stub, and there is no benefit to having a
> 	 MIPS16 entry.  */
>       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;
> 	}

 OK, this isn't too bad even though normally I'm opposed to nesting 
conditionals too much.  I slightly changed the wording of the comments you 
proposed though.

> > +      if (h->plt.plist->need_mips && h->plt.plist->mips_offset == MINUS_ONE)
> > +	{
> > +	  bfd_vma offset;
> >  
> > -      /* If the output file has no definition of the symbol, set the
> > -	 symbol's value to the address of the stub.  */
> > +	  h->plt.plist->mips_offset = offset = htab->plt_mips_offset;
> > +	  htab->plt_mips_offset = offset + htab->plt_mips_entry_size;
> > +	}
> 
> When is mips_offset not MINUS_ONE?

 This must have been some misunderstanding of mine, now fixed.

> The offset variable seems a bit obfuscating.  I'd prefer:
> 
> 	  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 == MINUS_ONE)
> > +	{
> > +	  bfd_vma offset;
> > +
> > +	  h->plt.plist->comp_offset = offset = htab->plt_comp_offset;
> > +	  htab->plt_comp_offset = offset + htab->plt_comp_entry_size;
> > +	}
> 
> Same comments here.
> 
> > +
> > +      /* Reserve the corresponding .got.plt entry now too.  */
> > +      if (h->plt.plist->gotplt_index == MINUS_ONE)
> > +	{
> > +	  bfd_vma gpindex;
> > +
> > +	  h->plt.plist->gotplt_index = gpindex = htab->plt_got_index;
> > +	  htab->plt_got_index = gpindex + 1;
> > +	}
> 
> Here too.

 I have adjusted the three pieces above.

> > +
> > +      /* If the output file has no definition of the symbol, we'll use
> > +         the address of the stub.
> > +
> > +         For VxWorks, point at the PLT load stub rather than the lazy
> > +         resolution stub; this stub will become the canonical function
> > +         address.
> > +
> > +         Otherwise we cannot determine the address of the stub yet, so
> > +         just record that we'll create a PLT entry for this symbol.  */
> 
> I'd prefer we set the address in the same place for VxWorks.  Also:

 Now moved across.

> >  	{
> > -	  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;
> > +	    {
> > +	      h->root.u.def.section = htab->splt;
> > +	      h->root.u.def.value = h->plt.offset + 8;
> > +	    }
> > +	  else
> > +	    hmips->has_plt_entry = TRUE;
> >  	}
> 
> shouldn't the plt.offset be plt.plist->...?

 Another oversight, thanks for catching.

> > @@ -8923,13 +9190,27 @@ static bfd_boolean
> >  mips_elf_allocate_lazy_stub (struct mips_elf_link_hash_entry *h, void **data)
> >  {
> >    struct mips_elf_link_hash_table *htab;
> > +  struct bfd_link_info *info;
> > +
> > +  info = (struct bfd_link_info *) data;
> > +  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 (info->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)
> > +	mips_elf_make_plt_record (htab->sstubs->owner, &h->root.plt.plist);
> > +      if (h->root.plt.plist == NULL)
> > +	return FALSE;
> 
> This won't propagate the error up.  Other callbacks typically handle
> this by having data be a pointer to a pointer that gets nullified
> on error.  Or you could simply allocate the PLT record when setting
> needs_lazy_stub in adjust_dynamic_symbol, I don't mind which.

 Moving over to adjust_dynamic_symbol would mean memory would be wasted in 
some cases (needs_lazy_stub is sometimes cleared having initially been 
set).  And we need to be careful about using resources -- I reckon I have 
a case to debug where a piece of trivial code brings GAS down to its knees 
due to inexplicable memory consumption.

> Also, preexisting problem, but it should be "void *data" rather than
> "void **data".

 Posted/committed separately.

> > @@ -8985,18 +9282,55 @@ _bfd_mips_elf_size_dynamic_sections (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.
> > +
> > +         Also create the _PROCEDURE_LINKAGE_TABLE_ symbol if we
> > +         haven't already in _bfd_elf_create_dynamic_sections.  */
> > +      if (htab->splt && htab->splt->size > 0)
> >  	{
> > +	  bfd_boolean micromips_p = MICROMIPS_P (output_bfd);
> > +	  bfd_boolean std_mips_p = !micromips_p || htab->plt_mips_offset;
> > +	  unsigned int other = std_mips_p ? 0 : STO_MICROMIPS;
> > +	  bfd_vma isa_bit = !std_mips_p;
> >  	  struct elf_link_hash_entry *h;
> > +	  bfd_vma size;
> 
> I'd prefer this as:
> 
> 	  bfd_boolean micromips_p = (MICROMIPS_P (output_bfd)
> 				     && htab->plt_mips_offset == 0);
> 	  unsigned int other = micromips_p ? STO_MICROMIPS : 0;
> 	  bfd_vma isa_bit = micromips_p;
> 
> which also matches what you did earlier.

 Well, this isn't an exact match, which was the reason to implement it 
like I did, but on second thoughts I see no strong reason to reject your 
preference, so I've changed this piece.

> > @@ -9835,68 +10169,135 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd
> >  
> >    BFD_ASSERT (!htab->is_vxworks);
> >  
> > -  if (h->plt.offset != MINUS_ONE && hmips->no_fn_stub)
> > +  if (h->plt.plist != NULL && hmips->no_fn_stub
> > +      && (h->plt.plist->mips_offset != MINUS_ONE
> > +	  || h->plt.plist->comp_offset != MINUS_ONE))
> 
> The old code was written that way because plt.offset was used for both PLTs
> and lazy stubs.  I don't think we need or want to test hmips->no_fn_stub now.
> By the same token, the "else if" should probably now be "if".

 Not a necessary change, I think, but it makes sense, I have made it now.  

 Wouldn't it make sense to change the (h->needs_plt && !hmips->no_fn_stub) 
condition consequently to (htab->is_vxworks && h->needs_plt) in 
_bfd_mips_elf_adjust_dynamic_symbol too (and adjust the comment to match 
reality)?  I think the condition would be clearer, I already scratched my 
head over it before I realised what it really is for, and VxWorks never 
sets hmips->no_fn_stub.

> > +      /* 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 = micromips_o32_exec_plt_entry;
> > +	      bfd_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)
> > +		return FALSE;
> 
> We shouldn't just return false without reporting an error.

 Hmm, I didn't actually realise the error reported I saw was actually a 
byproduct of a disregarded BFD error set by an earlier request.  Now 
corrected.  I guess this actually needs a test case too.

> > @@ -9905,21 +10306,39 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd
> >  	 binary where pointer equality matters.  */
> >        sym->st_shndx = SHN_UNDEF;
> >        if (h->pointer_equality_needed)
> > -	sym->st_other = STO_MIPS_PLT;
> > +	{
> > +	  if (ELF_ST_IS_MIPS16 (sym->st_other))
> > +	    sym->st_other
> > +	      = ELF_ST_SET_MIPS16 (ELF_ST_SET_MIPS_PLT (sym->st_other));
> > +	  else
> > +	    sym->st_other = ELF_ST_SET_MIPS_PLT (sym->st_other);
> > +	}
> 
> Please update the definition of ELF_ST_SET_MIPS_PLT instead, so that
> STO_MIPS16 gets preserved in the same way as STO_MICROMIPS.

 Posted separately.

> > +  else 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);
> > +      bfd_vma stub_size = htab->function_stub_size;
> >        bfd_byte stub[MIPS_FUNCTION_STUB_BIG_SIZE];
> > +      bfd_vma stub_big_size;
> > +      unsigned int other;
> > +      bfd_vma isa_bit;
> > +
> > +      if (micromips_p)
> > +	stub_big_size = MICROMIPS_FUNCTION_STUB_BIG_SIZE;
> > +      else
> > +	stub_big_size = MIPS_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
> > @@ -9928,35 +10347,80 @@ _bfd_mips_elf_finish_dynamic_symbol (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 (micromips_p)
> > +	{
> [...]
> > +	  isa_bit = 1;
> > +	  other = STO_MICROMIPS;
> 
> Minor consistency thing, but please use the same idiom as you used elsewhere.

 Sure.

> > @@ -10338,14 +10809,40 @@ mips_finish_exec_plt (bfd *output_bfd, s
> >  
> >    /* 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)
> > +	return FALSE;
> 
> Here too we shouldn't just return false without reporting an error.

 Fixed likewise.

> > @@ -10452,6 +10949,7 @@ _bfd_mips_elf_finish_dynamic_sections (b
> >    asection *sgot;
> >    struct mips_got_info *gg, *g;
> >    struct mips_elf_link_hash_table *htab;
> > +  bfd_boolean ok = TRUE;
> >  
> >    htab = mips_elf_hash_table (info);
> >    BFD_ASSERT (htab != NULL);
> > @@ -10867,10 +11365,10 @@ _bfd_mips_elf_finish_dynamic_sections (b
> >        else
> >  	{
> >  	  BFD_ASSERT (!info->shared);
> > -	  mips_finish_exec_plt (output_bfd, info);
> > +	  ok = mips_finish_exec_plt (output_bfd, info);
> >  	}
> >      }
> > -  return TRUE;
> > +  return ok;
> >  }
> 
> No need for the ok variable.  Just return early on error.

 Adjusted.

> > @@ -12860,6 +13358,10 @@ _bfd_mips_elf_link_hash_table_create (bf
> >        free (ret);
> >        return NULL;
> >      }
> > +  ret->root.init_plt_refcount.refcount = 0;
> > +  ret->root.init_plt_refcount.plist = NULL;
> > +  ret->root.init_plt_offset.offset = 0;
> > +  ret->root.init_plt_offset.plist = NULL;
> 
> These are unions, so we should only initialise one field each.

 Sigh, copy & paste silliness, fixed.

> > +      for (i = 0, p = relplt->relocation;
> > +	   i < count && p->address != gotplt_addr;
> > +	   i++, p += bed->s->int_rels_per_ext_rel);
> 
> This looks needlessly quadratic.  If we start the search from the previous
> match, won't a well-formed .plt only need two walks of the relocs?

 Good idea.  While the order of relocations in .rel.plt is certainly not a 
part of the ABI and we need to be liberal on what input we accept, we can 
treat the section like a circular buffer and that should reduce the number 
of passes considerably for the common case.

> > +      if (i < count)
> > +	{
> > +	  size_t namelen;
> > +	  size_t len;
> > +
> > +	  *s = **p->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->sym_ptr_ptr)->name);
> > +	  namelen = len + (p->addend != 0 ? addlen : 0) + suffixlen;
> > +	  if (names + namelen > nend)
> > +	    break;
> > +
> > +	  memcpy (names, (*p->sym_ptr_ptr)->name, len);
> > +	  names += len;
> > +	  if (p->addend != 0)
> > +	    {
> > +	      char buf[30], *a;
> > +
> > +	      memcpy (names, "+0x", sizeof ("+0x") - 1);
> > +	      names += sizeof ("+0x") - 1;
> > +	      bfd_sprintf_vma (abfd, buf, p->addend);
> > +	      for (a = buf; *a == '0'; ++a)
> > +		;
> > +	      len = strlen (a);
> > +	      memcpy (names, a, len);
> > +	      names += len;
> > +	    }
> 
> Maybe I'm showing my ignorance, but when do R_MIPS_JUMP_SLOTs have addends?
> If they shouldn't, lets just ignore the addend or skip entries with nonzero
> addends.

 I'm not sure why I included the addends, perhaps I was confused.  I have 
removed the relevant pieces now.

> Rather than making a general assumption that udata.i == st_other for
> BSF_SYNTHETIC, I think we should just do it for MIPS, and only (for now)
> when the defined symbol is in a section called .plt.  What do others think?

 I have now updated the GDB part accordingly.

 Here's the resulting new version.  Tested as previously.  I won't be able 
to look into your comments on the test suite update before I'm back in a 
two weeks' time.

2013-03-09  Maciej W. Rozycki  <macro@codesourcery.com>

	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 (plt_entry): New structure.
        (mips_elf_link_hash_entry): Add use_plt_entry member.
        (mips_elf_link_hash_table): Rename plt_entry_size member to
        plt_mips_entry_size.  Add plt_comp_entry_size, plt_mips_offset,
        plt_comp_offset, plt_got_index entries and plt_header_is_comp
	members.
	(STUB_LW_MICROMIPS, STUB_MOVE_MICROMIPS): New macros.
	(STUB_LUI_MICROMIPS, STUB_JALR_MICROMIPS): Likewise.
	(STUB_ORI_MICROMIPS, STUB_LI16U_MICROMIPS): Likewise.
	(STUB_LI16S_MICROMIPS): Likewise.
	(MICROMIPS_FUNCTION_STUB_NORMAL_SIZE): Likewise.
	(MICROMIPS_FUNCTION_STUB_BIG_SIZE): Likewise.
	(micromips_o32_exec_plt0_entry): New variable.
	(mips16_o32_exec_plt_entry): Likewise.
	(micromips_o32_exec_plt_entry): Likewise.
	(mips_elf_link_hash_newfunc): Initialize use_plt_entry.
        (mips_elf_output_extsym): Update to use gotplt_union's plist
        member rather than offset.
        (mips_elf_gotplt_index): Likewise.  Remove the VxWorks
        restriction.  Use MIPS_ELF_GOT_SIZE to calculate GOT address.
	(mips_elf_count_got_symbols): Update to use gotplt_union's plist
	member rather than offset.
        (mips_elf_calculate_relocation): Handle MIPS16/microMIPS PLT
        entries.
	(_bfd_mips_elf_create_dynamic_sections): Don't set PLT sizes
	here.
	(mips_elf_make_plt_record): New function.
        (_bfd_mips_elf_check_relocs): Update comment.  Record occurences
        of JAL relocations that might need a PLT entry.
        (_bfd_mips_elf_adjust_dynamic_symbol): Update to use
        gotplt_union's plist member rather than offset.  Set individual
	PLT entry sizes here.  Handle MIPS16/microMIPS PLT entries.
	Don't set the symbol's value in the symbol table for PLT
	references here.
        (mips_elf_estimate_stub_size): Handle microMIPS stubs.
        (mips_elf_allocate_lazy_stub): Likewise.
        (mips_elf_lay_out_lazy_stubs): Likewise.  Define a _MIPS_STUBS_
        magic symbol.
	(mips_elf_set_plt_sym_value): New function.
        (_bfd_mips_elf_size_dynamic_sections): Set PLT header size here.
	Set the symbol values in the symbol table for PLT references here.
        Handle microMIPS annotation of the _PROCEDURE_LINKAGE_TABLE_
        magic symbol.
        (_bfd_mips_elf_finish_dynamic_symbol): Update to use
        gotplt_union's plist member rather than offset.  Handle
        MIPS16/microMIPS PLT entries.  Handle microMIPS stubs.
	(_bfd_mips_vxworks_finish_dynamic_symbol): Update to use
	gotplt_union's plist member rather than offset.  Use
	MIPS_ELF_GOT_SIZE to calculate GOT address.
	(mips_finish_exec_plt): Handle microMIPS PLT.  Return status.
        (_bfd_mips_elf_finish_dynamic_sections): Handle result from
        mips_finish_exec_plt.
        (_bfd_mips_elf_link_hash_table_create): Update to use
        gotplt_union's plist member rather than offset.
        (_bfd_mips_elf_get_synthetic_symtab): New function.

	gdb/
	* mips-linux-tdep.c (mips_linux_in_dynsym_stub): Handle
	.MIPS.stubs section like .plt.  Remove unused `name' argument.
	Return 1 rather than the low 16-bit halfword of any instruction
	examined.
	(mips_linux_in_dynsym_resolve_code): Update accordingly.
        * mips-tdep.c (mips_elf_make_msymbol_special): Handle MIPS16 and
        microMIPS synthetic symbols.
	(mips_stub_frame_sniffer): Call in_plt_section in place of an
	equivalent hand-coded sequence.
	* objfiles.c (in_plt_section): Reuse the `name' argument as a
	trampoline section name override.

	ld/
	* emulparams/elf32btsmip.sh: Arrange for .got.plt to be placed
	as close to .plt as possible.
        * scripttempl/elf.sc: Handle $INITIAL_READWRITE_SECTIONS and
        $PLT_NEXT_DATA variables.

	ld/testsuite/
	* ld-mips-elf/jalx-2.dd: Update for microMIPS PLT support.
        * ld-mips-elf/pic-and-nonpic-3a.dd: Update for the _MIPS_STUBS_
        magic symbol.
        * ld-mips-elf/pic-and-nonpic-3b.dd: Likewise.
        * ld-mips-elf/pic-and-nonpic-6-n32.dd: Likewise.
        * ld-mips-elf/pic-and-nonpic-6-n64.dd: Likewise.
        * ld-mips-elf/pic-and-nonpic-6-o32.dd: Likewise.
        * ld-mips-elf/stub-dynsym-1-10000.d: Likewise.
        * ld-mips-elf/stub-dynsym-1-2fe80.d: Likewise.
        * ld-mips-elf/stub-dynsym-1-7fff.d: Likewise.
        * ld-mips-elf/stub-dynsym-1-8000.d: Likewise.
        * ld-mips-elf/stub-dynsym-1-fff0.d: Likewise.
        * ld-mips-elf/tlslib-o32.d: Likewise.

	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.

  Maciej

binutils-umips16-plt-stubs.diff
Index: binutils-fsf-trunk-quilt/bfd/elf32-mips.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/bfd/elf32-mips.c	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/bfd/elf32-mips.c	2013-03-08 11:10:48.395435819 +0000
@@ -2344,7 +2344,6 @@ static const struct ecoff_debug_swap mip
 #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 mip
 					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
 #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
 #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: binutils-fsf-trunk-quilt/bfd/elfxx-mips.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/bfd/elfxx-mips.c	2013-03-08 11:09:04.000000000 +0000
+++ binutils-fsf-trunk-quilt/bfd/elfxx-mips.c	2013-03-09 02:43:31.765430204 +0000
@@ -319,6 +319,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 of MIPS16 or microMIPS one.  We
+   keep the record of a stub if one is used instead separately, 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.  */
 
@@ -383,6 +409,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.  */
@@ -437,8 +466,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;
@@ -468,6 +509,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.  */
@@ -856,8 +900,28 @@ 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_LUI_MICROMIPS(VAL)						\
+   (0x41b80000 + (VAL))				/* lui t8,VAL */
+#define STUB_JALR_MICROMIPS 0x45d9		/* jalr 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
 
 /* The name of the dynamic interpreter.  This is put in the .interp
    section.  */
@@ -969,7 +1033,26 @@ static const bfd_vma mips_n64_exec_plt0_
   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 subsequent standard PLT entries.  */
 static const bfd_vma mips_exec_plt_entry[] =
 {
   0x3c0f0000,	/* lui $15, %hi(.got.plt entry)			*/
@@ -978,6 +1061,30 @@ 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 the first PLT entry in a VxWorks executable.  */
 static const bfd_vma mips_vxworks_exec_plt0_entry[] =
 {
@@ -1116,6 +1223,7 @@ mips_elf_link_hash_newfunc (struct bfd_h
       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;
@@ -2730,6 +2838,8 @@ mips_elf_output_extsym (struct mips_elf_
 
       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;
@@ -2739,7 +2849,7 @@ mips_elf_output_extsym (struct mips_elf_
 	    {
 	      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
@@ -3215,25 +3325,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
@@ -4200,7 +4305,7 @@ mips_elf_count_got_symbols (struct mips_
 	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.  */
@@ -5098,6 +5203,9 @@ mips_elf_calculate_relocation (bfd *abfd
       /* Record the name of this symbol, for our caller.  */
       *namep = h->root.root.root.string;
 
+      target_is_16_bit_code_p = ELF_ST_IS_MIPS16 (h->root.other);
+      target_is_micromips_code_p = ELF_ST_IS_MICROMIPS (h->root.other);
+
       /* See if this is the special _gp_disp symbol.  Note that such a
 	 symbol must always be a global symbol.  */
       if (strcmp (*namep, "_gp_disp") == 0
@@ -5124,13 +5232,55 @@ mips_elf_calculate_relocation (bfd *abfd
 		|| h->root.root.type == bfd_link_hash_defweak)
 	       && h->root.root.u.def.section)
 	{
-	  sec = h->root.root.u.def.section;
-	  if (sec->output_section)
-	    symbol = (h->root.root.u.def.value
-		      + sec->output_section->vma
-		      + sec->output_offset);
+	  if (h->use_plt_entry)
+	    {
+	      bfd_boolean micromips_p = MICROMIPS_P (abfd);
+	      bfd_vma plt_offset;
+	      bfd_vma isa_bit;
+	      bfd_vma val;
+
+	      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);
+
+	      plt_offset = htab->plt_header_size;
+	      if (h->root.plt.plist->comp_offset == MINUS_ONE
+		  || (h->root.plt.plist->mips_offset != MINUS_ONE
+		      && r_type != R_MIPS16_26 && r_type != R_MICROMIPS_26_S1))
+		{
+		  isa_bit = 0;
+		  target_is_16_bit_code_p = FALSE;
+		  target_is_micromips_code_p = FALSE;
+		  plt_offset += h->root.plt.plist->mips_offset;
+		}
+	      else
+		{
+		  isa_bit = 1;
+		  target_is_16_bit_code_p = !micromips_p;
+		  target_is_micromips_code_p = micromips_p;
+		  plt_offset += (htab->plt_mips_offset
+				 + h->root.plt.plist->comp_offset);
+		}
+	      BFD_ASSERT (plt_offset <= htab->splt->size);
+
+	      sec = htab->splt;
+	      val = plt_offset + isa_bit;
+	      /* For VxWorks, point at the PLT load stub rather than the
+	         lazy resolution stub.  */
+	      if (htab->is_vxworks)
+		val += 8;
+	      symbol = sec->output_section->vma + sec->output_offset + val;
+	    }
 	  else
-	    symbol = h->root.root.u.def.value;
+	    {
+	      sec = h->root.root.u.def.section;
+	      if (sec->output_section)
+		symbol = (h->root.root.u.def.value
+			  + sec->output_section->vma
+			  + sec->output_offset);
+	      else
+		symbol = h->root.root.u.def.value;
+	    }
 	}
       else if (h->root.root.type == bfd_link_hash_undefweak)
 	/* We allow relocations against undefined weak symbols, giving
@@ -5177,12 +5327,6 @@ mips_elf_calculate_relocation (bfd *abfd
 	{
 	  return bfd_reloc_notsupported;
 	}
-
-      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));
     }
 
   /* If this is a reference to a 16-bit function with a stub, we need
@@ -5233,16 +5377,16 @@ mips_elf_calculate_relocation (bfd *abfd
       /* 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];
@@ -7348,34 +7492,10 @@ _bfd_mips_elf_create_dynamic_sections (b
       || !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;
 }
@@ -7503,8 +7623,27 @@ mips_elf_get_section_contents (bfd *abfd
   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,
@@ -8201,6 +8340,28 @@ _bfd_mips_elf_check_relocs (bfd *abfd, s
 	  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;
+	}
+
       /* 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
@@ -8603,11 +8764,16 @@ _bfd_mips_elf_adjust_dynamic_symbol (str
 	   && !(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.  */
+      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->splt->size == 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.
@@ -8623,40 +8789,103 @@ _bfd_mips_elf_adjust_dynamic_symbol (str
 					  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 (micromips_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 (mips16_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);
+      htab->splt->size = htab->plt_mips_offset + htab->plt_comp_offset;
+      htab->sgotplt->size = htab->plt_got_index * 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));
@@ -8907,29 +9136,58 @@ mips_elf_estimate_stub_size (bfd *output
   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.  */
+  if (MICROMIPS_P (output_bfd))
+    htab->function_stub_size = (dynsymcount > 0x10000
+				? MICROMIPS_FUNCTION_STUB_BIG_SIZE
+				: MICROMIPS_FUNCTION_STUB_NORMAL_SIZE);
+  else
+    htab->function_stub_size = (dynsymcount > 0x10000
+				? MIPS_FUNCTION_STUB_BIG_SIZE
+				: MIPS_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;
@@ -8938,22 +9196,97 @@ mips_elf_allocate_lazy_stub (struct mips
 /* 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.  */
@@ -8985,18 +9318,60 @@ _bfd_mips_elf_size_dynamic_sections (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->splt->size > 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);
 
-	  h = _bfd_elf_define_linkage_sym (dynobj, info, htab->splt,
-					   "_PROCEDURE_LINKAGE_TABLE_");
-	  htab->root.hplt = h;
-	  if (h == NULL)
-	    return FALSE;
+	  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
+	    size = 2 * ARRAY_SIZE (micromips_o32_exec_plt0_entry);
+
+	  htab->plt_header_is_comp = micromips_p;
+	  htab->plt_header_size = size;
+	  htab->splt->size += size;
+
+	  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;
 	}
     }
@@ -9835,68 +10210,145 @@ _bfd_mips_elf_finish_dynamic_symbol (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 = 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);
+	    }
+	  else
+	    {
+	      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);
+	    }
 	}
 
       /* 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
@@ -9905,21 +10357,34 @@ _bfd_mips_elf_finish_dynamic_symbol (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 = MICROMIPS_FUNCTION_STUB_BIG_SIZE;
+      else
+	stub_big_size = MIPS_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
@@ -9928,35 +10393,76 @@ _bfd_mips_elf_finish_dynamic_symbol (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 (micromips_p)
+	{
+	  idx = 0;
+	  bfd_put_micromips_32 (output_bfd, STUB_LW_MICROMIPS (output_bfd),
+				stub + idx);
+	  idx += 4;
+	  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;
 
-      /* 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);
+	      bfd_put_micromips_32 (output_bfd,
+				    STUB_LUI_MICROMIPS (dynindx_hi),
+				    stub + idx);
+	      idx += 4;
+	    }
+	  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;
 
-      BFD_ASSERT (h->plt.offset <= htab->sstubs->size);
-      memcpy (htab->sstubs->contents + h->plt.offset,
-	      stub, htab->function_stub_size);
+	  /* 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);
+	}
 
-      /* Mark the symbol as undefined.  plt.offset != -1 occurs
+      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.  stub_offset != -1 occurs
 	 only for the referenced symbol.  */
       sym->st_shndx = SHN_UNDEF;
 
@@ -9965,7 +10471,9 @@ _bfd_mips_elf_finish_dynamic_symbol (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
@@ -10153,30 +10661,32 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
   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_.  */
@@ -10184,20 +10694,21 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 
       /* 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
 	{
@@ -10208,7 +10719,7 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 	  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);
@@ -10217,12 +10728,12 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 	  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>).  */
@@ -10240,7 +10751,8 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 	}
 
       /* 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;
@@ -10307,7 +10819,7 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 
 /* 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;
@@ -10322,6 +10834,8 @@ mips_finish_exec_plt (bfd *output_bfd, s
     plt_entry = mips_n64_exec_plt0_entry;
   else if (ABI_N32_P (output_bfd))
     plt_entry = mips_n32_exec_plt0_entry;
+  else if (htab->plt_header_is_comp)
+    plt_entry = micromips_o32_exec_plt0_entry;
   else
     plt_entry = mips_o32_exec_plt0_entry;
 
@@ -10338,14 +10852,49 @@ mips_finish_exec_plt (bfd *output_bfd, s
 
   /* 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
+    {
+      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
@@ -10867,7 +11416,8 @@ _bfd_mips_elf_finish_dynamic_sections (b
       else
 	{
 	  BFD_ASSERT (!info->shared);
-	  mips_finish_exec_plt (output_bfd, info);
+	  if (!mips_finish_exec_plt (output_bfd, info))
+	    return FALSE;
 	}
     }
   return TRUE;
@@ -12860,6 +13410,8 @@ _bfd_mips_elf_link_hash_table_create (bf
       free (ret);
       return NULL;
     }
+  ret->root.init_plt_refcount.plist = NULL;
+  ret->root.init_plt_offset.plist = NULL;
 
   return &ret->root.root;
 }
@@ -14347,6 +14899,231 @@ _bfd_mips_elf_plt_sym_val (bfd_vma i, co
 	  + 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 addlen;
+  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)));
+  addlen = 2 * (sizeof ("+0x") - 1 + 8);
+#ifdef BFD64
+  addlen += 2 * 8 * (bed->s->elfclass == ELFCLASS64);
+#endif
+  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
+    {
+      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.  */
+      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;
+	}
+      /* 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: binutils-fsf-trunk-quilt/bfd/elfxx-mips.h
===================================================================
--- binutils-fsf-trunk-quilt.orig/bfd/elfxx-mips.h	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/bfd/elfxx-mips.h	2013-03-08 11:28:58.154310764 +0000
@@ -152,6 +152,8 @@ extern bfd_boolean _bfd_mips_elf_init_st
    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: binutils-fsf-trunk-quilt/gdb/mips-linux-tdep.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/gdb/mips-linux-tdep.c	2013-03-08 11:02:50.000000000 +0000
+++ binutils-fsf-trunk-quilt/gdb/mips-linux-tdep.c	2013-03-08 11:10:48.395435819 +0000
@@ -30,6 +30,7 @@
 #include "trad-frame.h"
 #include "tramp-frame.h"
 #include "gdbtypes.h"
+#include "objfiles.h"
 #include "solib.h"
 #include "solib-svr4.h"
 #include "solist.h"
@@ -666,25 +667,34 @@ mips_linux_core_read_description (struct
 
 
 /* Check the code at PC for a dynamic linker lazy resolution stub.
-   Because they aren't in the .plt section, we pattern-match on the
-   code generated by GNU ld.  They look like this:
+   GNU ld for MIPS has put lazy resolution stubs into a ".MIPS.stubs"
+   section uniformly since version 2.15.  If the pc is in that section,
+   then we are in such a stub.  Before that ".stub" was used in 32-bit
+   ELF binaries, however we do not bother checking for that since we
+   have never had and that case should be extremely rare these days.
+   Instead we pattern-match on the code generated by GNU ld.  They look
+   like this:
 
    lw t9,0x8010(gp)
    addu t7,ra
    jalr t9,ra
    addiu t8,zero,INDEX
 
-   (with the appropriate doubleword instructions for N64).  Also
-   return the dynamic symbol index used in the last instruction.  */
+   (with the appropriate doubleword instructions for N64).  As any lazy
+   resolution stubs in microMIPS binaries will always be in a
+   ".MIPS.stubs" section we only ever verify standard MIPS patterns. */
 
 static int
-mips_linux_in_dynsym_stub (CORE_ADDR pc, char *name)
+mips_linux_in_dynsym_stub (CORE_ADDR pc)
 {
   gdb_byte buf[28], *p;
   ULONGEST insn, insn1;
   int n64 = (mips_abi (target_gdbarch ()) == MIPS_ABI_N64);
   enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
 
+  if (in_plt_section (pc, ".MIPS.stubs"))
+    return 1;
+
   read_memory (pc - 12, buf, 28);
 
   if (n64)
@@ -742,7 +752,7 @@ mips_linux_in_dynsym_stub (CORE_ADDR pc,
 	return 0;
     }
 
-  return (insn & 0xffff);
+  return 1;
 }
 
 /* Return non-zero iff PC belongs to the dynamic linker resolution
@@ -756,9 +766,10 @@ mips_linux_in_dynsym_resolve_code (CORE_
   if (svr4_in_dynsym_resolve_code (pc))
     return 1;
 
-  /* Pattern match for the stub.  It would be nice if there were a
-     more efficient way to avoid this check.  */
-  if (mips_linux_in_dynsym_stub (pc, NULL))
+  /* Likewise for the stubs.  They live in the .MIPS.stubs section these
+     days, so we check if the PC is within, than fall back to a pattern
+     match.  */
+  if (mips_linux_in_dynsym_stub (pc))
     return 1;
 
   return 0;
Index: binutils-fsf-trunk-quilt/gdb/mips-tdep.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/gdb/mips-tdep.c	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/gdb/mips-tdep.c	2013-03-08 11:10:48.405435415 +0000
@@ -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;
 }
 
@@ -3591,12 +3597,7 @@ mips_stub_frame_sniffer (const struct fr
   if (in_plt_section (pc, NULL))
     return 1;
 
-  /* Binutils for MIPS puts lazy resolution stubs into .MIPS.stubs.  */
-  s = find_pc_section (pc);
-
-  if (s != NULL
-      && strcmp (bfd_get_section_name (s->objfile->obfd, s->the_bfd_section),
-		 ".MIPS.stubs") == 0)
+  if (in_plt_section (pc, ".MIPS.stubs"))
     return 1;
 
   /* Calling a PIC function from a non-PIC function passes through a
Index: binutils-fsf-trunk-quilt/gdb/objfiles.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/gdb/objfiles.c	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/gdb/objfiles.c	2013-03-08 11:10:48.405435415 +0000
@@ -1383,9 +1383,11 @@ find_pc_section (CORE_ADDR pc)
 }
 
 
-/* In SVR4, we recognize a trampoline by it's section name. 
-   That is, if the pc is in a section named ".plt" then we are in
-   a trampoline.  */
+/* In SVR4, we recognize a trampoline by it's section name.  That is,
+   if the pc is in a section named ".plt" then we are in a trampoline.
+   We let targets request an alternative name, this is currently used
+   by the MIPS backend to handle the SVR4 lazy resolution stubs that
+   binutils put into ".MIPS.stubs" instead.  */
 
 int
 in_plt_section (CORE_ADDR pc, char *name)
@@ -1397,7 +1399,7 @@ in_plt_section (CORE_ADDR pc, char *name
 
   retval = (s != NULL
 	    && s->the_bfd_section->name != NULL
-	    && strcmp (s->the_bfd_section->name, ".plt") == 0);
+	    && strcmp (s->the_bfd_section->name, name ? name : ".plt") == 0);
   return (retval);
 }
 \f
Index: binutils-fsf-trunk-quilt/ld/emulparams/elf32btsmip.sh
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/emulparams/elf32btsmip.sh	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/ld/emulparams/elf32btsmip.sh	2013-03-08 11:10:48.405435415 +0000
@@ -8,3 +8,10 @@ LITTLE_OUTPUT_FORMAT="elf32-tradlittlemi
 unset DATA_ADDR
 SHLIB_TEXT_START_ADDR=0
 ENTRY=__start
+
+# Place .got.plt as close to .plt as possible so that the former can be
+# referred to from the latter with the microMIPS ADDIUPC instruction
+# that only has a span of +/-16MB.
+PLT_NEXT_DATA=
+INITIAL_READWRITE_SECTIONS=$OTHER_READWRITE_SECTIONS
+unset OTHER_READWRITE_SECTIONS
Index: binutils-fsf-trunk-quilt/ld/scripttempl/elf.sc
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/scripttempl/elf.sc	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/ld/scripttempl/elf.sc	2013-03-08 11:10:48.405435415 +0000
@@ -10,6 +10,7 @@
 #	OTHER_READONLY_SECTIONS - other than .text .init .rodata ...
 #		(e.g., .PARISC.milli)
 #	OTHER_TEXT_SECTIONS - these get put in .text when relocating
+#	INITIAL_READWRITE_SECTIONS - at start of data segment (after relro)
 #	OTHER_READWRITE_SECTIONS - other than .data .bss .ctors .sdata ...
 #		(e.g., .PARISC.global)
 #	OTHER_RELRO_SECTIONS - other than .data.rel.ro ...
@@ -33,6 +34,7 @@
 #	OTHER_SDATA_SECTIONS - sections just after .sdata.
 #	OTHER_BSS_SYMBOLS - symbols that appear at the start of the
 #		.bss section besides __bss_start.
+#	PLT_NEXT_DATA - .plt next to data segment when .plt is in text segment.
 #	DATA_PLT - .plt should be in data segment, not text segment.
 #	PLT_BEFORE_GOT - .plt just before .got when .plt is in data segement.
 #	BSS_PLT - .plt should be in bss segment
@@ -132,7 +134,7 @@ if test -z "$PLT"; then
   PLT=".plt          ${RELOCATING-0} : { *(.plt)${IREL_IN_PLT+ *(.iplt)} }
   ${IREL_IN_PLT-$IPLT}"
 fi
-test -n "${DATA_PLT-${BSS_PLT-text}}" && TEXT_PLT=yes
+test -n "${DATA_PLT-${BSS_PLT-text}}" && TEXT_PLT=
 if test -z "$GOT"; then
   if test -z "$SEPARATE_GOTPLT"; then
     GOT=".got          ${RELOCATING-0} : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }"
@@ -463,7 +465,7 @@ cat <<EOF
     ${RELOCATING+${INIT_END}}
   } ${FILL}
 
-  ${TEXT_PLT+${PLT}}
+  ${TEXT_PLT+${PLT_NEXT_DATA-${PLT}}}
   ${TINY_READONLY_SECTION}
   .text         ${RELOCATING-0} :
   {
@@ -527,6 +529,7 @@ cat <<EOF
   /* These sections are generated by the Sun/Oracle C++ compiler.  */
   .exception_ranges ${RELOCATING-0} : ONLY_IF_RO { *(.exception_ranges
   .exception_ranges*) }
+  ${TEXT_PLT+${PLT_NEXT_DATA+${PLT}}}
 
   /* Adjust the address for the data segment.  We want to adjust up to
      the same address within the page on the next page up.  */
@@ -562,6 +565,7 @@ cat <<EOF
   ${DATA_GOT+${RELRO_NOW+${GOTPLT}}}
   ${DATA_GOT+${RELRO_NOW-${SEPARATE_GOTPLT+${GOT}}}}
   ${RELOCATING+${DATA_SEGMENT_RELRO_END}}
+  ${INITIAL_READWRITE_SECTIONS}
   ${DATA_GOT+${RELRO_NOW-${SEPARATE_GOTPLT-${GOT}}}}
   ${DATA_GOT+${RELRO_NOW-${GOTPLT}}}
 
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/jalx-2.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/jalx-2.dd	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/jalx-2.dd	2013-03-08 11:10:48.405435415 +0000
@@ -28,8 +28,8 @@
  4400034:	f89e 0020 	sw	a0,32\(s8\)
  4400038:	f8be 0024 	sw	a1,36\(s8\)
  440003c:	41a2 0440 	lui	v0,0x440
- 4400040:	3082 02a0 	addiu	a0,v0,672
- 4400044:	f110 0028 	jalx	44000a0 <printf@plt>
+ 4400040:	3082 0290 	addiu	a0,v0,656
+ 4400044:	f620 004c 	jal	4400098 <printf@micromipsplt>
  4400048:	0000 0000 	nop
  440004c:	f620 0010 	jal	4400020 <internal_function>
  4400050:	0000 0000 	nop
@@ -44,17 +44,18 @@
 Disassembly of section \.plt:
 
 04400080 <_PROCEDURE_LINKAGE_TABLE_>:
- 4400080:	3c1c0440 	lui	gp,0x440
- 4400084:	8f9900d8 	lw	t9,216\(gp\)
- 4400088:	279c00d8 	addiu	gp,gp,216
- 440008c:	031cc023 	subu	t8,t8,gp
- 4400090:	03e07821 	move	t7,ra
- 4400094:	0018c082 	srl	t8,t8,0x2
- 4400098:	0320f809 	jalr	t9
- 440009c:	2718fffe 	addiu	t8,t8,-2
+ 4400080:	7980 0012 	addiu	v1,\$pc,72
+ 4400084:	ff23 0000 	lw	t9,0\(v1\)
+ 4400088:	0535      	subu	v0,v0,v1
+ 440008a:	2525      	srl	v0,v0,2
+ 440008c:	3302 fffe 	addiu	t8,v0,-2
+ 4400090:	0dff      	move	t7,ra
+ 4400092:	45f9      	jalrs	t9
+ 4400094:	0f83      	move	gp,v1
+ 4400096:	0c00      	nop
 
-044000a0 <printf@plt>:
- 44000a0:	3c0f0440 	lui	t7,0x440
- 44000a4:	8df900e0 	lw	t9,224\(t7\)
- 44000a8:	03200008 	jr	t9
- 44000ac:	25f800e0 	addiu	t8,t7,224
+04400098 <printf@micromipsplt>:
+ 4400098:	7900 000e 	addiu	v0,\$pc,56
+ 440009c:	ff22 0000 	lw	t9,0\(v0\)
+ 44000a0:	4599      	jr	t9
+ 44000a2:	0f02      	move	t8,v0
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-3a.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-3a.dd	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-3a.dd	2013-03-08 11:10:48.405435415 +0000
@@ -31,7 +31,7 @@
 #...
 Disassembly of section \.MIPS\.stubs:
 
-00000c00 <.MIPS.stubs>:
+00000c00 <_MIPS_STUBS_>:
  c00:	8f998010 	lw	t9,-32752\(gp\)
  c04:	03e07821 	move	t7,ra
  c08:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-3b.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-3b.dd	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-3b.dd	2013-03-08 11:10:48.405435415 +0000
@@ -42,9 +42,10 @@
 .*:	03200008 	jr	t9
 .*:	00000000 	nop
 .*:	00000000 	nop
-Disassembly of section .MIPS.stubs:
 
-00044030 <\.MIPS\.stubs>:
+Disassembly of section \.MIPS\.stubs:
+
+00044030 <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n32.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n32.dd	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n32.dd	2013-03-08 11:10:48.405435415 +0000
@@ -91,9 +91,10 @@
    44090:	3c02000a 	lui	v0,0xa
    44094:	24422018 	addiu	v0,v0,8216
 	\.\.\.
+
 Disassembly of section \.MIPS\.stubs:
 
-000440a0 <\.MIPS\.stubs>:
+000440a0 <_MIPS_STUBS_>:
    440a0:	8f998010 	lw	t9,-32752\(gp\)
    440a4:	03e07821 	move	t3,ra
    440a8:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n64.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n64.dd	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n64.dd	2013-03-08 11:10:48.405435415 +0000
@@ -91,9 +91,10 @@
    44090:	3c02000a 	lui	v0,0xa
    44094:	24422018 	addiu	v0,v0,8216
 	\.\.\.
+
 Disassembly of section \.MIPS\.stubs:
 
-0+440a0 <\.MIPS\.stubs>:
+0+440a0 <_MIPS_STUBS_>:
    440a0:	df998010 	ld	t9,-32752\(gp\)
    440a4:	03e0782d 	move	t3,ra
    440a8:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-o32.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-o32.dd	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-o32.dd	2013-03-08 11:10:48.405435415 +0000
@@ -91,9 +91,10 @@
    44090:	3c02000a 	lui	v0,0xa
    44094:	24422018 	addiu	v0,v0,8216
 	\.\.\.
+
 Disassembly of section \.MIPS\.stubs:
 
-000440a0 <\.MIPS\.stubs>:
+000440a0 <_MIPS_STUBS_>:
    440a0:	8f998010 	lw	t9,-32752\(gp\)
    440a4:	03e07821 	move	t7,ra
    440a8:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-10000.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-10000.d	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-10000.d	2013-03-08 11:10:48.405435415 +0000
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	3c180001 	lui	t8,0x1
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-2fe80.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-2fe80.d	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-2fe80.d	2013-03-08 11:10:48.405435415 +0000
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	3c180002 	lui	t8,0x2
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-7fff.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-7fff.d	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-7fff.d	2013-03-08 11:10:48.405435415 +0000
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-8000.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-8000.d	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-8000.d	2013-03-08 11:10:48.405435415 +0000
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-fff0.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-fff0.d	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-fff0.d	2013-03-08 11:10:48.405435415 +0000
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/tlslib-o32.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/tlslib-o32.d	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/tlslib-o32.d	2013-03-08 11:10:48.405435415 +0000
@@ -35,9 +35,10 @@
  .*:	03e00008 	jr	ra
  .*:	27bd0010 	addiu	sp,sp,16
 	...
+
 Disassembly of section .MIPS.stubs:
 
-.* <.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
  .*:	8f998010 	lw	t9,-32752\(gp\)
  .*:	03e07821 	move	t7,ra
  .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/opcodes/mips-dis.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/opcodes/mips-dis.c	2013-03-08 10:59:15.000000000 +0000
+++ binutils-fsf-trunk-quilt/opcodes/mips-dis.c	2013-03-08 11:10:48.415435627 +0000
@@ -2031,6 +2031,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
@@ -2038,7 +2055,7 @@ print_insn_mips16 (bfd_vma memaddr, stru
 {
   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;
@@ -2051,11 +2068,32 @@ print_insn_mips16 (bfd_vma memaddr, stru
   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);
@@ -2929,27 +2967,26 @@ print_insn_micromips (bfd_vma memaddr, s
 static bfd_boolean
 is_compressed_mode_p (struct disassemble_info *info)
 {
-  elf_symbol_type *symbol;
-  int pos;
   int i;
+  int l;
 
-  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;
-    }
+  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;
 }


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-02-22 20:09       ` Tom Tromey
@ 2013-03-09  4:06         ` Maciej W. Rozycki
  0 siblings, 0 replies; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-03-09  4:06 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Alan Modra, gdb-patches

On Fri, 22 Feb 2013, Tom Tromey wrote:

> Alan> Hmm, perhaps a cleaner change would be implement make_msymbol_special
> Alan> for ppc64 and move the udata.p special case out of elf_symtab_read?
> 
> Alan> Like so.
> 
> Alan> 	* elfread.c (elf_symtab_read): Do not use udata.p here to find
> Alan> 	symbol size.
> Alan> 	* ppc64-tdep.c (ppc64_elf_make_msymbol_special): New function.
> Alan> 	* ppc64-tdep.h (ppc64_elf_make_msymbol_special): Declare.
> Alan> 	* ppc-linux-tdep.c (ppc_linux_init_abi): Set up to use the above.
> Alan> 	* ppcfbsd-tdep.c (ppcfbsd_init_abi): Likewise.
> 
> I like this much better, thanks.
> This is ok.

 Thanks for sorting this out -- now the MIPS change does not have to touch 
elfread.c anymore.

  Maciej


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-03-09  4:04   ` Maciej W. Rozycki
@ 2013-03-09  9:58     ` Richard Sandiford
  2013-06-08  0:22       ` Maciej W. Rozycki
  2013-03-11 13:53     ` Joel Brobecker
  1 sibling, 1 reply; 28+ messages in thread
From: Richard Sandiford @ 2013-03-09  9:58 UTC (permalink / raw)
  To: Maciej W. Rozycki; +Cc: Joel Brobecker, Catherine Moore, binutils, gdb-patches

Thanks for the updates.

"Maciej W. Rozycki" <macro@codesourcery.com> writes:
>> "Maciej W. Rozycki" <macro@codesourcery.com> writes:
>> > @@ -3215,25 +3325,19 @@ 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 * 4);
>> >  
>> >    /* Calculate the value of _GLOBAL_OFFSET_TABLE_.  */
>> >    got_value = (htab->root.hgot->root.u.def.section->output_section->vma
>> 
>> If we remove the is_vxworks assert, I think we should use MIPS_ELF_GOT_SIZE
>> instead of 4.
>
>  Not sure if this is related to this change, but I see no problem with 
> that either.  I've updated all the references throughout.

It was related because the patch kept a VxWorks assumption while removing
the assert for VxWorks.

> Index: binutils-fsf-trunk-quilt/bfd/elfxx-mips.c
> ===================================================================
> --- binutils-fsf-trunk-quilt.orig/bfd/elfxx-mips.c	2013-03-08 11:09:04.000000000 +0000
> +++ binutils-fsf-trunk-quilt/bfd/elfxx-mips.c	2013-03-09 02:43:31.765430204 +0000
> @@ -319,6 +319,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 of MIPS16 or microMIPS one.  We
> +   keep the record of a stub if one is used instead separately, for
> +   easier processing.  */

s/either of MIPS16 or microMIPS one/either a MIPS16 or microMIPS one/.
Suggest "We keep a separate record of traditional lazy-binding stubs,
for easier processing."

> @@ -5124,13 +5232,55 @@ mips_elf_calculate_relocation (bfd *abfd
>  		|| h->root.root.type == bfd_link_hash_defweak)
>  	       && h->root.root.u.def.section)
>  	{
> -	  sec = h->root.root.u.def.section;
> -	  if (sec->output_section)
> -	    symbol = (h->root.root.u.def.value
> -		      + sec->output_section->vma
> -		      + sec->output_offset);
> +	  if (h->use_plt_entry)
> +	    {
> +	      bfd_boolean micromips_p = MICROMIPS_P (abfd);
> +	      bfd_vma plt_offset;
> +	      bfd_vma isa_bit;
> +	      bfd_vma val;
> +
> +	      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);
> +
> +	      plt_offset = htab->plt_header_size;
> +	      if (h->root.plt.plist->comp_offset == MINUS_ONE
> +		  || (h->root.plt.plist->mips_offset != MINUS_ONE
> +		      && r_type != R_MIPS16_26 && r_type != R_MICROMIPS_26_S1))
> +		{
> +		  isa_bit = 0;
> +		  target_is_16_bit_code_p = FALSE;
> +		  target_is_micromips_code_p = FALSE;
> +		  plt_offset += h->root.plt.plist->mips_offset;
> +		}
> +	      else
> +		{
> +		  isa_bit = 1;
> +		  target_is_16_bit_code_p = !micromips_p;
> +		  target_is_micromips_code_p = micromips_p;
> +		  plt_offset += (htab->plt_mips_offset
> +				 + h->root.plt.plist->comp_offset);
> +		}
> +	      BFD_ASSERT (plt_offset <= htab->splt->size);
> +
> +	      sec = htab->splt;
> +	      val = plt_offset + isa_bit;
> +	      /* For VxWorks, point at the PLT load stub rather than the
> +	         lazy resolution stub.  */
> +	      if (htab->is_vxworks)
> +		val += 8;
> +	      symbol = sec->output_section->vma + sec->output_offset + val;
> +	    }
>  	  else
> -	    symbol = h->root.root.u.def.value;
> +	    {
> +	      sec = h->root.root.u.def.section;
> +	      if (sec->output_section)
> +		symbol = (h->root.root.u.def.value
> +			  + sec->output_section->vma
> +			  + sec->output_offset);
> +	      else
> +		symbol = h->root.root.u.def.value;
> +	    }
>  	}
>        else if (h->root.root.type == bfd_link_hash_undefweak)
>  	/* We allow relocations against undefined weak symbols, giving
> @@ -5177,12 +5327,6 @@ mips_elf_calculate_relocation (bfd *abfd
>  	{
>  	  return bfd_reloc_notsupported;
>  	}
> -
> -      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));
>      }
>  
>    /* If this is a reference to a 16-bit function with a stub, we need

The block of code that you're changing here is simply calculating the
symbol value.  Now that size_dynamic_sections has set that up for us,
these two hunks should be dropped, and the test for whether to use a
compressed PLT entry instead of the symbol value should be made
afterwards, as it is for things like hard-float and LA25 stubs.
Maybe something like:

  /* If compressed PLT entries are available, make sure that we use them
     for MIPS16 and microMIPS calls.  */
  else if ((r_type == R_MIPS16_26 || r_type == R_MICROMIPS_26_S1)
	   && h != NULL
	   && h->use_plt
	   && h->root.plt.plist->comp_offset != MINUS_ONE)
    {
      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 (abfd);
      target_is_micromips_code_p = MICROMIPS_P (abfd);
    }

at the end of the:

  /* If this is a reference to a 16-bit function with a stub, we need
     to redirect the relocation to the stub unless:

chain of ifs.

> -      /* Make room for the .got.plt entry and the R_MIPS_JUMP_SLOT
> -	 relocation.  */
> -      htab->sgotplt->size += MIPS_ELF_GOT_SIZE (dynobj);
> +      htab->splt->size = htab->plt_mips_offset + htab->plt_comp_offset;
> +      htab->sgotplt->size = htab->plt_got_index * MIPS_ELF_GOT_SIZE (dynobj);

These last two lines should be done in size_dynamic_sections rather
than once for each symbol.  I.e.:

> @@ -8985,18 +9318,60 @@ _bfd_mips_elf_size_dynamic_sections (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->splt->size > 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);
>  
> -	  h = _bfd_elf_define_linkage_sym (dynobj, info, htab->splt,
> -					   "_PROCEDURE_LINKAGE_TABLE_");
> -	  htab->root.hplt = h;
> -	  if (h == NULL)
> -	    return FALSE;
> +	  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
> +	    size = 2 * ARRAY_SIZE (micromips_o32_exec_plt0_entry);
> +
> +	  htab->plt_header_is_comp = micromips_p;
> +	  htab->plt_header_size = size;
> +	  htab->splt->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));

> +	      /* 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);

The last two arguments should be swapped.

> +      /* 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);

Same here.

> +  /* 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)));
> +  addlen = 2 * (sizeof ("+0x") - 1 + 8);
> +#ifdef BFD64
> +  addlen += 2 * 8 * (bed->s->elfclass == ELFCLASS64);
> +#endif

Now that there are no addends (thanks), the last four lines should be dropped.

OK with those changes if they work, otherwise let me know.

Richard


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-03-09  4:04   ` Maciej W. Rozycki
  2013-03-09  9:58     ` Richard Sandiford
@ 2013-03-11 13:53     ` Joel Brobecker
  2013-06-26 15:02       ` Maciej W. Rozycki
  1 sibling, 1 reply; 28+ messages in thread
From: Joel Brobecker @ 2013-03-11 13:53 UTC (permalink / raw)
  To: Maciej W. Rozycki
  Cc: Richard Sandiford, Catherine Moore, binutils, gdb-patches

>  What's the current ETA for 7.6?  By now I have run out of time, I'll
>  be off the next two weeks and won't be able to do anything about this
>  change even if Richard accepts it right away (unlikely).

Branch is scheduled to be tomorrow, and release 2 weeks after
assuming no last-minute glitch.

>  Of course it's still possible that a fortnight away you'll still not
>  have rolled 7.6 out for one reason or another, but assuming that all
>  goes well, what is the prospect of having 7.7 released before
>  binutils 2.24?

We have several options if you miss 7.6. We can look at a 7.6.1,
for instance. And if binutils keeps to its current typical pace
of 1 major release a year, with the last one being around Oct,
GDB 7.7 should also be out by then.

-- 
Joel


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH] in_plt_section: support alternate stub section names (was: [PATCH 1/2] MIPS: Compressed PLT/stubs support)
  2013-02-19 20:44 [PATCH 1/2] MIPS: Compressed PLT/stubs support Maciej W. Rozycki
                   ` (2 preceding siblings ...)
  2013-02-21 21:06 ` Tom Tromey
@ 2013-06-07 13:25 ` Maciej W. Rozycki
  2013-06-13 12:43   ` [PING][PATCH] " Maciej W. Rozycki
  3 siblings, 1 reply; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-06-07 13:25 UTC (permalink / raw)
  To: gdb-patches; +Cc: Richard Sandiford, Catherine Moore, binutils

Hi,

 I have realised the change to support alternate stub section names in 
in_plt_section is really self-contained and while a prerequisite for 
microMIPS/MIPS16 PLT support it can be applied separately.  I have 
therefore split it off from the PLT change, hopefully making a review 
easier.

 For a reference, here are the relevant original observations I made when 
posting the combined change:

>  As to the semantics change of the in_plt_section GDB helper -- the `name' 
> argument is unused and all the callers pass it as NULL.  I've tracked down 
> the history of this function, and it was introduced with GDB 4.13:
> 
> Fri Apr  1 00:44:00 1994  Peter Schauer  (pes@regent.e-technik.tu-muenchen.de)
> 
> 	* sparc-tdep.c (in_solib_trampoline):  Renamed to in_plt_section
> 	and moved to objfiles.c.
> 	* objfiles.c (in_plt_section):  Moved to here from sparc-tdep.
> 	* config/tm-sysv4.h (IN_SOLIB_TRAMPOLINE):  Use new in_plt_section.
> 	* config/sparc/tm-sun4sol2.h (IN_SOLIB_TRAMPOLINE):  Removed,
> 	the new generic definition from tm-sysv4.h works for Solaris.
> 
> -- with this argument already unused.  Furthermore, `in_solib_trampoline' 
> was introduced in GDB 4.9:
> 
> Tue Mar 30 15:46:14 1993  K. Richard Pixley  (rich@rtl.cygnus.com)
> 
> 	* sparc-tdep.c (in_solib_trampoline): new function.
> 	* config/sparc/tm-sun4sol2.h (IN_SOLIB_TRAMPOLINE): redefine to
> 	  in_solib_trampoline.
> 
> with this argument also unused.  I was unable to track down the pre-4.9
> tm-sun4sol2.h version of IN_SOLIB_TRAMPOLINE as GDB 4.8 didn't have the 
> macro there yet, so no GDB version was ever released that provided it.  
> 
>  However, the tm-sysv4.h version was defined like this:
> 
> #define IN_SOLIB_TRAMPOLINE(pc,name) ((name) && (STREQ ("_init", name)))
> 
> -- and then redefined in terms of in_plt_section as recorded in the 
> ChangeLog entry quoted above like this:
> 
> #define IN_SOLIB_TRAMPOLINE(pc, name) in_plt_section((pc), (name))
> 
> at which point the `name' argument became unused as well.
> 
>  HP-PA had its own version:
> 
> #define IN_SOLIB_TRAMPOLINE(pc, name) skip_trampoline_code (pc, name)
> 
> -- but skip_trampoline_code didn't make any use of its `name' argument 
> either -- just as does't current code in hppa_in_solib_call_trampoline the 
> former has evolved to (and neither does code in 
> hppa32_hpux_in_solib_call_trampoline, hppa64_hpux_in_solib_call_trampoline 
> or hppa_hpux_in_solib_return_trampoline).
> 
>  With the above consideration in mind, I think it is safe to redefine 
> in_plt_section's API as proposed in this change -- remembering that MIPS 
> stubs are the functional equivalent of PLT entries -- for the sake of code 
> duplication avoidance.

 With in_plt_section such redefined, all the handcoded conditions 
throughout the MIPS backend can be unified, and also the helper can now be 
used in mips_linux_in_dynsym_stub to avoid the heuristic there if 
possible.

 This change was regression-tested for the mips-sde-elf and mips-linux-gnu 
targets using the following configurations (multilibs), both endiannesses 
each:

* o32 (-mabi=32),

* n64 (-mabi=64) (mips-linux-gnu only),

* n32 (-mabi=n32) (mips-linux-gnu only),

* MIPS16 o32 (-mips16 -mabi=32),

* microMIPS o32 (-mmicromips -mabi=32).

with no regressions (as previously, with the outstanding ISA bit fix 
applied).  OK to apply?

2013-06-07  Maciej W. Rozycki  <macro@codesourcery.com>

	gdb/
	* mips-linux-tdep.c (mips_linux_in_dynsym_stub): Handle
	.MIPS.stubs section like .plt.  Remove unused `name' argument.
	Return 1 rather than the low 16-bit halfword of any instruction
	examined.
	(mips_linux_in_dynsym_resolve_code): Update accordingly.
        * mips-tdep.c (mips_stub_frame_sniffer): Call in_plt_section in 
	place of an equivalent hand-coded sequence.
	* objfiles.c (in_plt_section): Reuse the `name' argument as an
	trampoline section name override.
  Maciej

gdb-mips-in-stubs-section.diff
Index: gdb-fsf-trunk-quilt/gdb/mips-linux-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/mips-linux-tdep.c	2013-06-06 20:48:30.243223201 +0100
+++ gdb-fsf-trunk-quilt/gdb/mips-linux-tdep.c	2013-06-06 20:52:00.273227140 +0100
@@ -30,6 +30,7 @@
 #include "trad-frame.h"
 #include "tramp-frame.h"
 #include "gdbtypes.h"
+#include "objfiles.h"
 #include "solib.h"
 #include "solib-svr4.h"
 #include "solist.h"
@@ -666,25 +667,34 @@ mips_linux_core_read_description (struct
 
 
 /* Check the code at PC for a dynamic linker lazy resolution stub.
-   Because they aren't in the .plt section, we pattern-match on the
-   code generated by GNU ld.  They look like this:
+   GNU ld for MIPS has put lazy resolution stubs into a ".MIPS.stubs"
+   section uniformly since version 2.15.  If the pc is in that section,
+   then we are in such a stub.  Before that ".stub" was used in 32-bit
+   ELF binaries, however we do not bother checking for that since we
+   have never had and that case should be extremely rare these days.
+   Instead we pattern-match on the code generated by GNU ld.  They look
+   like this:
 
    lw t9,0x8010(gp)
    addu t7,ra
    jalr t9,ra
    addiu t8,zero,INDEX
 
-   (with the appropriate doubleword instructions for N64).  Also
-   return the dynamic symbol index used in the last instruction.  */
+   (with the appropriate doubleword instructions for N64).  As any lazy
+   resolution stubs in microMIPS binaries will always be in a
+   ".MIPS.stubs" section we only ever verify standard MIPS patterns. */
 
 static int
-mips_linux_in_dynsym_stub (CORE_ADDR pc, char *name)
+mips_linux_in_dynsym_stub (CORE_ADDR pc)
 {
   gdb_byte buf[28], *p;
   ULONGEST insn, insn1;
   int n64 = (mips_abi (target_gdbarch ()) == MIPS_ABI_N64);
   enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
 
+  if (in_plt_section (pc, ".MIPS.stubs"))
+    return 1;
+
   read_memory (pc - 12, buf, 28);
 
   if (n64)
@@ -742,7 +752,7 @@ mips_linux_in_dynsym_stub (CORE_ADDR pc,
 	return 0;
     }
 
-  return (insn & 0xffff);
+  return 1;
 }
 
 /* Return non-zero iff PC belongs to the dynamic linker resolution
@@ -756,9 +766,10 @@ mips_linux_in_dynsym_resolve_code (CORE_
   if (svr4_in_dynsym_resolve_code (pc))
     return 1;
 
-  /* Pattern match for the stub.  It would be nice if there were a
-     more efficient way to avoid this check.  */
-  if (mips_linux_in_dynsym_stub (pc, NULL))
+  /* Likewise for the stubs.  They live in the .MIPS.stubs section these
+     days, so we check if the PC is within, than fall back to a pattern
+     match.  */
+  if (mips_linux_in_dynsym_stub (pc))
     return 1;
 
   return 0;
Index: gdb-fsf-trunk-quilt/gdb/mips-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/mips-tdep.c	2013-06-06 20:48:30.243223201 +0100
+++ gdb-fsf-trunk-quilt/gdb/mips-tdep.c	2013-06-06 20:52:00.273227140 +0100
@@ -3591,12 +3591,7 @@ mips_stub_frame_sniffer (const struct fr
   if (in_plt_section (pc, NULL))
     return 1;
 
-  /* Binutils for MIPS puts lazy resolution stubs into .MIPS.stubs.  */
-  s = find_pc_section (pc);
-
-  if (s != NULL
-      && strcmp (bfd_get_section_name (s->objfile->obfd, s->the_bfd_section),
-		 ".MIPS.stubs") == 0)
+  if (in_plt_section (pc, ".MIPS.stubs"))
     return 1;
 
   /* Calling a PIC function from a non-PIC function passes through a
Index: gdb-fsf-trunk-quilt/gdb/objfiles.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/objfiles.c	2013-06-06 20:48:30.243223201 +0100
+++ gdb-fsf-trunk-quilt/gdb/objfiles.c	2013-06-06 20:52:00.273227140 +0100
@@ -1410,9 +1410,11 @@ find_pc_section (CORE_ADDR pc)
 }
 
 
-/* In SVR4, we recognize a trampoline by it's section name. 
-   That is, if the pc is in a section named ".plt" then we are in
-   a trampoline.  */
+/* In SVR4, we recognize a trampoline by it's section name.  That is,
+   if the pc is in a section named ".plt" then we are in a trampoline.
+   We let targets request an alternative name, this is currently used
+   by the MIPS backend to handle the SVR4 lazy resolution stubs that
+   binutils put into ".MIPS.stubs" instead.  */
 
 int
 in_plt_section (CORE_ADDR pc, char *name)
@@ -1424,7 +1426,7 @@ in_plt_section (CORE_ADDR pc, char *name
 
   retval = (s != NULL
 	    && s->the_bfd_section->name != NULL
-	    && strcmp (s->the_bfd_section->name, ".plt") == 0);
+	    && strcmp (s->the_bfd_section->name, name ? name : ".plt") == 0);
   return (retval);
 }
 \f


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-03-09  9:58     ` Richard Sandiford
@ 2013-06-08  0:22       ` Maciej W. Rozycki
  2013-06-08 16:04         ` Richard Sandiford
  0 siblings, 1 reply; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-06-08  0:22 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Joel Brobecker, Catherine Moore, binutils, gdb-patches

On Sat, 9 Mar 2013, Richard Sandiford wrote:

> > Index: binutils-fsf-trunk-quilt/bfd/elfxx-mips.c
> > ===================================================================
> > --- binutils-fsf-trunk-quilt.orig/bfd/elfxx-mips.c	2013-03-08 11:09:04.000000000 +0000
> > +++ binutils-fsf-trunk-quilt/bfd/elfxx-mips.c	2013-03-09 02:43:31.765430204 +0000
> > @@ -319,6 +319,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 of MIPS16 or microMIPS one.  We
> > +   keep the record of a stub if one is used instead separately, for
> > +   easier processing.  */
> 
> s/either of MIPS16 or microMIPS one/either a MIPS16 or microMIPS one/.
> Suggest "We keep a separate record of traditional lazy-binding stubs,
> for easier processing."

 Done.  I wasn't sure if that would sound better as "either MIPS16 or 
microMIPS one," -- referring to "code" (uncountable) rather than "a PLT 
entry" -- but eventually I decided it sounded better with the article. ;)

> > @@ -5124,13 +5232,55 @@ mips_elf_calculate_relocation (bfd *abfd
> >  		|| h->root.root.type == bfd_link_hash_defweak)
> >  	       && h->root.root.u.def.section)
> >  	{
> > -	  sec = h->root.root.u.def.section;
> > -	  if (sec->output_section)
> > -	    symbol = (h->root.root.u.def.value
> > -		      + sec->output_section->vma
> > -		      + sec->output_offset);
> > +	  if (h->use_plt_entry)
> > +	    {
> > +	      bfd_boolean micromips_p = MICROMIPS_P (abfd);
> > +	      bfd_vma plt_offset;
> > +	      bfd_vma isa_bit;
> > +	      bfd_vma val;
> > +
> > +	      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);
> > +
> > +	      plt_offset = htab->plt_header_size;
> > +	      if (h->root.plt.plist->comp_offset == MINUS_ONE
> > +		  || (h->root.plt.plist->mips_offset != MINUS_ONE
> > +		      && r_type != R_MIPS16_26 && r_type != R_MICROMIPS_26_S1))
> > +		{
> > +		  isa_bit = 0;
> > +		  target_is_16_bit_code_p = FALSE;
> > +		  target_is_micromips_code_p = FALSE;
> > +		  plt_offset += h->root.plt.plist->mips_offset;
> > +		}
> > +	      else
> > +		{
> > +		  isa_bit = 1;
> > +		  target_is_16_bit_code_p = !micromips_p;
> > +		  target_is_micromips_code_p = micromips_p;
> > +		  plt_offset += (htab->plt_mips_offset
> > +				 + h->root.plt.plist->comp_offset);
> > +		}
> > +	      BFD_ASSERT (plt_offset <= htab->splt->size);
> > +
> > +	      sec = htab->splt;
> > +	      val = plt_offset + isa_bit;
> > +	      /* For VxWorks, point at the PLT load stub rather than the
> > +	         lazy resolution stub.  */
> > +	      if (htab->is_vxworks)
> > +		val += 8;
> > +	      symbol = sec->output_section->vma + sec->output_offset + val;
> > +	    }
> >  	  else
> > -	    symbol = h->root.root.u.def.value;
> > +	    {
> > +	      sec = h->root.root.u.def.section;
> > +	      if (sec->output_section)
> > +		symbol = (h->root.root.u.def.value
> > +			  + sec->output_section->vma
> > +			  + sec->output_offset);
> > +	      else
> > +		symbol = h->root.root.u.def.value;
> > +	    }
> >  	}
> >        else if (h->root.root.type == bfd_link_hash_undefweak)
> >  	/* We allow relocations against undefined weak symbols, giving
> > @@ -5177,12 +5327,6 @@ mips_elf_calculate_relocation (bfd *abfd
> >  	{
> >  	  return bfd_reloc_notsupported;
> >  	}
> > -
> > -      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));
> >      }
> >  
> >    /* If this is a reference to a 16-bit function with a stub, we need
> 
> The block of code that you're changing here is simply calculating the
> symbol value.  Now that size_dynamic_sections has set that up for us,
> these two hunks should be dropped, and the test for whether to use a
> compressed PLT entry instead of the symbol value should be made
> afterwards, as it is for things like hard-float and LA25 stubs.

 D'oh, it must have escaped me as the obvious consequence of the 
introduction of mips_elf_set_plt_sym_value.  I haven't merely reverted the 
latter hunk though -- given that we're going to handle the PLT explicitly 
later on now, we want to preset target_is_micromips_code_p according to 
the symbol table here, just as we already do for target_is_16_bit_code_p.

> Maybe something like:
> 
>   /* If compressed PLT entries are available, make sure that we use them
>      for MIPS16 and microMIPS calls.  */
>   else if ((r_type == R_MIPS16_26 || r_type == R_MICROMIPS_26_S1)
> 	   && h != NULL
> 	   && h->use_plt
> 	   && h->root.plt.plist->comp_offset != MINUS_ONE)
>     {
>       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 (abfd);
>       target_is_micromips_code_p = MICROMIPS_P (abfd);
>     }
> 
> at the end of the:
> 
>   /* If this is a reference to a 16-bit function with a stub, we need
>      to redirect the relocation to the stub unless:
> 
> chain of ifs.

 More or less, though I've decided to push it ahead of the chain so that 
it sees the state consistent regardless of whether any PLT entry processed 
has been duplicated for dual-mode support.  This probably does not really 
matter right now (fn_stub/need_fn_stub cases will override any symbol 
value to use for the relocation and likewise the mode setting anyway, 
call_stub/call_fp_stub cases will only ever have a standard MIPS PLT entry 
due to the arrangement in _bfd_mips_elf_adjust_dynamic_symbol and 
la25_stub cases will never have a PLT entry as these must resolve 
locally), but I feel a bit uneasy about this half-cooked state.  Let me 
know if you disagree (and why).

 Also I've decided not to recalculate the symbol's value if what 
mips_elf_set_plt_sym_value worked out is the right thing for compressed 
direct calls too (i.e. there is no standard MIPS PLT entry alternative).

> > -      /* Make room for the .got.plt entry and the R_MIPS_JUMP_SLOT
> > -	 relocation.  */
> > -      htab->sgotplt->size += MIPS_ELF_GOT_SIZE (dynobj);
> > +      htab->splt->size = htab->plt_mips_offset + htab->plt_comp_offset;
> > +      htab->sgotplt->size = htab->plt_got_index * MIPS_ELF_GOT_SIZE (dynobj);
> 
> These last two lines should be done in size_dynamic_sections rather
> than once for each symbol.  I.e.:
> 
> > @@ -8985,18 +9318,60 @@ _bfd_mips_elf_size_dynamic_sections (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->splt->size > 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);
> >  
> > -	  h = _bfd_elf_define_linkage_sym (dynobj, info, htab->splt,
> > -					   "_PROCEDURE_LINKAGE_TABLE_");
> > -	  htab->root.hplt = h;
> > -	  if (h == NULL)
> > -	    return FALSE;
> > +	  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
> > +	    size = 2 * ARRAY_SIZE (micromips_o32_exec_plt0_entry);
> > +
> > +	  htab->plt_header_is_comp = micromips_p;
> > +	  htab->plt_header_size = size;
> > +	  htab->splt->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));

 Sure, I had to adjust the condition around this block:

      if (htab->splt && htab->splt->size > 0)

and one in _bfd_mips_elf_adjust_dynamic_symbol accordingly though.

> > +	      /* 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);
> 
> The last two arguments should be swapped.

 Nope, %A and %B are handled specially (ahead of any other format 
specifiers).  See the comment at _bfd_default_error_handler in bfd/bfd.c. 
I actually verified this range checking triggers correctly (hmm, it might 
be worth making a dedicated test case for) and it would be outright silly 
to make such a mistake, wouldn't it (hint, hint!)?

> > +      /* 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);
> 
> Same here.

 Ditto. ;)

> > +  /* 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)));
> > +  addlen = 2 * (sizeof ("+0x") - 1 + 8);
> > +#ifdef BFD64
> > +  addlen += 2 * 8 * (bed->s->elfclass == ELFCLASS64);
> > +#endif
> 
> Now that there are no addends (thanks), the last four lines should be dropped.

 Missed that, thanks for catching; I guess a newer GCC version would 
complain about this set-but-unused addlen variable.  Dropped that now as 
well as the variable itself.

> OK with those changes if they work, otherwise let me know.

 Given that (as noted above) the change is more than just what you 
suggested, here's a new version for you to review, in the form of an 
incremental change on top of the previous one (let me know if you'd like 
to see a combined version as well; I've included the intended set of 
ChangeLog entries only).  Please double-check you're all right with it.  
No regressions across the usual set of MIPS targets (let me know if you 
want me to remind you of the actual list).  Thanks.

2013-06-07  Maciej W. Rozycki  <macro@codesourcery.com>

	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 (plt_entry): New structure.
	(mips_elf_link_hash_entry): Add use_plt_entry member.
	(mips_elf_link_hash_table): Rename plt_entry_size member to
	plt_mips_entry_size.  Add plt_comp_entry_size, plt_mips_offset,
	plt_comp_offset, plt_got_index entries and plt_header_is_comp
	members.
	(STUB_LW_MICROMIPS, STUB_MOVE_MICROMIPS): New macros.
	(STUB_LUI_MICROMIPS, STUB_JALR_MICROMIPS): Likewise.
	(STUB_ORI_MICROMIPS, STUB_LI16U_MICROMIPS): Likewise.
	(STUB_LI16S_MICROMIPS): Likewise.
	(MICROMIPS_FUNCTION_STUB_NORMAL_SIZE): Likewise.
	(MICROMIPS_FUNCTION_STUB_BIG_SIZE): Likewise.
	(micromips_o32_exec_plt0_entry): New variable.
	(mips16_o32_exec_plt_entry): Likewise.
	(micromips_o32_exec_plt_entry): Likewise.
	(mips_elf_link_hash_newfunc): Initialize use_plt_entry.
	(mips_elf_output_extsym): Update to use gotplt_union's plist
	member rather than offset.
	(mips_elf_gotplt_index): Likewise.  Remove the VxWorks
	restriction.  Use MIPS_ELF_GOT_SIZE to calculate GOT address.
	(mips_elf_count_got_symbols): Update to use gotplt_union's plist
	member rather than offset.
	(mips_elf_calculate_relocation): Handle MIPS16/microMIPS PLT
	entries.
	(_bfd_mips_elf_create_dynamic_sections): Don't set PLT sizes
	here.
	(mips_elf_make_plt_record): New function.
	(_bfd_mips_elf_check_relocs): Update comment.  Record occurences
	of JAL relocations that might need a PLT entry.
	(_bfd_mips_elf_adjust_dynamic_symbol): Update to use
	gotplt_union's plist member rather than offset.  Set individual
	PLT entry sizes here.  Handle MIPS16/microMIPS PLT entries.
	Don't set the symbol's value in the symbol table for PLT
	references here.  Don't set the PLT or PLT GOT section sizes
	here.
	(mips_elf_estimate_stub_size): Handle microMIPS stubs.
	(mips_elf_allocate_lazy_stub): Likewise.
	(mips_elf_lay_out_lazy_stubs): Likewise.  Define a _MIPS_STUBS_
	magic symbol.
	(mips_elf_set_plt_sym_value): New function.
	(_bfd_mips_elf_size_dynamic_sections): Set PLT header size and
	PLT and PLT GOT section sizes here.  Set the symbol values in
	the symbol table for PLT references here.  Handle microMIPS
	annotation of the _PROCEDURE_LINKAGE_TABLE_ magic symbol.
	(_bfd_mips_elf_finish_dynamic_symbol): Update to use
	gotplt_union's plist member rather than offset.  Handle
	MIPS16/microMIPS PLT entries.  Handle microMIPS stubs.
	(_bfd_mips_vxworks_finish_dynamic_symbol): Update to use
	gotplt_union's plist member rather than offset.  Use
	MIPS_ELF_GOT_SIZE to calculate GOT address.
	(mips_finish_exec_plt): Handle microMIPS PLT.  Return status.
	(_bfd_mips_elf_finish_dynamic_sections): Handle result from
	mips_finish_exec_plt.
	(_bfd_mips_elf_link_hash_table_create): Update to use
	gotplt_union's plist member rather than offset.
	(_bfd_mips_elf_get_synthetic_symtab): New function.

	include/elf/
	* mips.h (ELF_ST_IS_MIPS_PLT): Respect STO_MIPS16 setting.
	(ELF_ST_SET_MIPS_PLT): Likewise.

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

	ld/
	* emulparams/elf32btsmip.sh: Arrange for .got.plt to be placed
	as close to .plt as possible.
	* scripttempl/elf.sc: Handle $INITIAL_READWRITE_SECTIONS and
	$PLT_NEXT_DATA variables.

	ld/testsuite/
	* ld-mips-elf/jalx-2.dd: Update for microMIPS PLT support.
	* ld-mips-elf/pic-and-nonpic-3a.dd: Update for the _MIPS_STUBS_
	magic symbol.
	* ld-mips-elf/pic-and-nonpic-3b.dd: Likewise.
	* ld-mips-elf/pic-and-nonpic-6-n32.dd: Likewise.
	* ld-mips-elf/pic-and-nonpic-6-n64.dd: Likewise.
	* ld-mips-elf/pic-and-nonpic-6-o32.dd: Likewise.
	* ld-mips-elf/stub-dynsym-1-10000.d: Likewise.
	* ld-mips-elf/stub-dynsym-1-2fe80.d: Likewise.
	* ld-mips-elf/stub-dynsym-1-7fff.d: Likewise.
	* ld-mips-elf/stub-dynsym-1-8000.d: Likewise.
	* ld-mips-elf/stub-dynsym-1-fff0.d: Likewise.
	* ld-mips-elf/tlslib-o32.d: Likewise.

	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.

  Maciej

binutils-umips16-plt-stubs-update.diff
Index: binutils-fsf-trunk-quilt/bfd/elfxx-mips.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/bfd/elfxx-mips.c	2013-06-07 19:07:47.000000000 +0100
+++ binutils-fsf-trunk-quilt/bfd/elfxx-mips.c	2013-06-07 21:02:39.942586414 +0100
@@ -318,9 +318,9 @@ struct mips_elf_hash_sort_data
 };
 
 /* We make up to two PLT entries if needed, one for standard MIPS code
-   and one for compressed code, either of MIPS16 or microMIPS one.  We
-   keep the record of a stub if one is used instead separately, for
-   easier processing.  */
+   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
 {
@@ -5201,9 +5201,6 @@ mips_elf_calculate_relocation (bfd *abfd
       /* Record the name of this symbol, for our caller.  */
       *namep = h->root.root.root.string;
 
-      target_is_16_bit_code_p = ELF_ST_IS_MIPS16 (h->root.other);
-      target_is_micromips_code_p = ELF_ST_IS_MICROMIPS (h->root.other);
-
       /* See if this is the special _gp_disp symbol.  Note that such a
 	 symbol must always be a global symbol.  */
       if (strcmp (*namep, "_gp_disp") == 0
@@ -5230,55 +5227,13 @@ mips_elf_calculate_relocation (bfd *abfd
 		|| h->root.root.type == bfd_link_hash_defweak)
 	       && h->root.root.u.def.section)
 	{
-	  if (h->use_plt_entry)
-	    {
-	      bfd_boolean micromips_p = MICROMIPS_P (abfd);
-	      bfd_vma plt_offset;
-	      bfd_vma isa_bit;
-	      bfd_vma val;
-
-	      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);
-
-	      plt_offset = htab->plt_header_size;
-	      if (h->root.plt.plist->comp_offset == MINUS_ONE
-		  || (h->root.plt.plist->mips_offset != MINUS_ONE
-		      && r_type != R_MIPS16_26 && r_type != R_MICROMIPS_26_S1))
-		{
-		  isa_bit = 0;
-		  target_is_16_bit_code_p = FALSE;
-		  target_is_micromips_code_p = FALSE;
-		  plt_offset += h->root.plt.plist->mips_offset;
-		}
-	      else
-		{
-		  isa_bit = 1;
-		  target_is_16_bit_code_p = !micromips_p;
-		  target_is_micromips_code_p = micromips_p;
-		  plt_offset += (htab->plt_mips_offset
-				 + h->root.plt.plist->comp_offset);
-		}
-	      BFD_ASSERT (plt_offset <= htab->splt->size);
-
-	      sec = htab->splt;
-	      val = plt_offset + isa_bit;
-	      /* For VxWorks, point at the PLT load stub rather than the
-	         lazy resolution stub.  */
-	      if (htab->is_vxworks)
-		val += 8;
-	      symbol = sec->output_section->vma + sec->output_offset + val;
-	    }
+	  sec = h->root.root.u.def.section;
+	  if (sec->output_section)
+	    symbol = (h->root.root.u.def.value
+		      + sec->output_section->vma
+		      + sec->output_offset);
 	  else
-	    {
-	      sec = h->root.root.u.def.section;
-	      if (sec->output_section)
-		symbol = (h->root.root.u.def.value
-			  + sec->output_section->vma
-			  + sec->output_offset);
-	      else
-		symbol = h->root.root.u.def.value;
-	    }
+	    symbol = h->root.root.u.def.value;
 	}
       else if (h->root.root.type == bfd_link_hash_undefweak)
 	/* We allow relocations against undefined weak symbols, giving
@@ -5325,6 +5280,35 @@ mips_elf_calculate_relocation (bfd *abfd
 	{
 	  return bfd_reloc_notsupported;
 	}
+
+      target_is_16_bit_code_p = ELF_ST_IS_MIPS16 (h->root.other);
+      target_is_micromips_code_p = ELF_ST_IS_MICROMIPS (h->root.other);
+    }
+
+  /* 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.  Adjust the mode settings accordingly.  */
+  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;
     }
 
   /* If this is a reference to a 16-bit function with a stub, we need
@@ -8778,7 +8762,7 @@ _bfd_mips_elf_adjust_dynamic_symbol (str
       /* 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->splt->size == 0)
+      if (htab->plt_mips_offset + htab->plt_comp_offset == 0)
 	{
 	  BFD_ASSERT (htab->sgotplt->size == 0);
 	  BFD_ASSERT (htab->plt_got_index == 0);
@@ -8890,9 +8874,6 @@ _bfd_mips_elf_adjust_dynamic_symbol (str
       if (!info->shared && !h->def_regular)
 	hmips->use_plt_entry = TRUE;
 
-      htab->splt->size = htab->plt_mips_offset + htab->plt_comp_offset;
-      htab->sgotplt->size = htab->plt_got_index * 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)
@@ -9338,7 +9319,7 @@ _bfd_mips_elf_size_dynamic_sections (bfd
 
          Also create the _PROCEDURE_LINKAGE_TABLE_ symbol if we
          haven't already in _bfd_elf_create_dynamic_sections.  */
-      if (htab->splt && htab->splt->size > 0)
+      if (htab->splt && htab->plt_mips_offset + htab->plt_comp_offset != 0)
 	{
 	  bfd_boolean micromips_p = (MICROMIPS_P (output_bfd)
 				     && !htab->plt_mips_offset);
@@ -9348,6 +9329,8 @@ _bfd_mips_elf_size_dynamic_sections (bfd
 	  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);
@@ -9364,7 +9347,11 @@ _bfd_mips_elf_size_dynamic_sections (bfd
 
 	  htab->plt_header_is_comp = micromips_p;
 	  htab->plt_header_size = size;
-	  htab->splt->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);
 
@@ -14943,7 +14930,6 @@ _bfd_mips_elf_get_synthetic_symtab (bfd 
   bfd_vma opcode;
   asection *plt;
   asymbol *send;
-  size_t addlen;
   size_t size;
   char *names;
   long counti;
@@ -14985,10 +14971,6 @@ _bfd_mips_elf_get_synthetic_symtab (bfd 
   size = 2 * count * sizeof (asymbol);
   size += count * (sizeof (mipssuffix) +
 		   (micromips_p ? sizeof (microsuffix) : sizeof (m16suffix)));
-  addlen = 2 * (sizeof ("+0x") - 1 + 8);
-#ifdef BFD64
-  addlen += 2 * 8 * (bed->s->elfclass == ELFCLASS64);
-#endif
   for (pi = 0; pi < counti; pi += bed->s->int_rels_per_ext_rel)
     size += 2 * strlen ((*p[pi].sym_ptr_ptr)->name);
 
Index: binutils-fsf-trunk-quilt/include/elf/mips.h
===================================================================
--- binutils-fsf-trunk-quilt.orig/include/elf/mips.h	2013-06-07 19:06:39.000000000 +0100
+++ binutils-fsf-trunk-quilt/include/elf/mips.h	2013-06-07 19:07:49.052856391 +0100
@@ -803,8 +803,14 @@ extern void bfd_mips_elf32_swap_reginfo_
    PLT entries and traditional MIPS lazy binding stubs.  We mark the former
    with STO_MIPS_PLT to distinguish them from the latter.  */
 #define STO_MIPS_PLT		0x8
-#define ELF_ST_IS_MIPS_PLT(other) (((other) & STO_MIPS_FLAGS) == STO_MIPS_PLT)
-#define ELF_ST_SET_MIPS_PLT(other) (((other) & ~STO_MIPS_FLAGS) | STO_MIPS_PLT)
+#define ELF_ST_IS_MIPS_PLT(other)					\
+  ((ELF_ST_IS_MIPS16 (other)						\
+    ? ((other) & (~STO_MIPS16 & STO_MIPS_FLAGS))			\
+    : ((other) & STO_MIPS_FLAGS)) == STO_MIPS_PLT)
+#define ELF_ST_SET_MIPS_PLT(other)					\
+  ((ELF_ST_IS_MIPS16 (other)						\
+    ? ((other) & (STO_MIPS16 | ~STO_MIPS_FLAGS))			\
+    : ((other) & ~STO_MIPS_FLAGS)) | STO_MIPS_PLT)
 
 /* This value is used to mark PIC functions in an object that mixes
    PIC and non-PIC.  Note that this bit overlaps with STO_MIPS16,


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-06-08  0:22       ` Maciej W. Rozycki
@ 2013-06-08 16:04         ` Richard Sandiford
  2013-06-10 17:13           ` Maciej W. Rozycki
  0 siblings, 1 reply; 28+ messages in thread
From: Richard Sandiford @ 2013-06-08 16:04 UTC (permalink / raw)
  To: Maciej W. Rozycki; +Cc: Joel Brobecker, Catherine Moore, binutils, gdb-patches

"Maciej W. Rozycki" <macro@codesourcery.com> writes:
>> Maybe something like:
>> 
>>   /* If compressed PLT entries are available, make sure that we use them
>>      for MIPS16 and microMIPS calls.  */
>>   else if ((r_type == R_MIPS16_26 || r_type == R_MICROMIPS_26_S1)
>> 	   && h != NULL
>> 	   && h->use_plt
>> 	   && h->root.plt.plist->comp_offset != MINUS_ONE)
>>     {
>>       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 (abfd);
>>       target_is_micromips_code_p = MICROMIPS_P (abfd);
>>     }
>> 
>> at the end of the:
>> 
>>   /* If this is a reference to a 16-bit function with a stub, we need
>>      to redirect the relocation to the stub unless:
>> 
>> chain of ifs.
>
>  More or less, though I've decided to push it ahead of the chain so that 
> it sees the state consistent regardless of whether any PLT entry processed 
> has been duplicated for dual-mode support.  This probably does not really 
> matter right now (fn_stub/need_fn_stub cases will override any symbol 
> value to use for the relocation and likewise the mode setting anyway, 
> call_stub/call_fp_stub cases will only ever have a standard MIPS PLT entry 
> due to the arrangement in _bfd_mips_elf_adjust_dynamic_symbol and 
> la25_stub cases will never have a PLT entry as these must resolve 
> locally), but I feel a bit uneasy about this half-cooked state.  Let me 
> know if you disagree (and why).

The changes made by the block beginning:

   /* If this is a reference to a 16-bit function with a stub, we need
      to redirect the relocation to the stub unless:

are mutually-exclusive with each other and with the new PLT transformation.
We should only ever perform one.  And the transformation:

  /* 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))
	   && ((h != NULL && h->use_plt_entry) || !target_is_16_bit_code_p))

logically trumps the PLT one.

That's why I think all four transformations should be in a single if chain,
and why the PLT one should come last.

You said yourself about the addition of (h != NULL && h->use_plt_entry):

  The current code flow is a bit subtle, this piece works because as a side 
  effect of the PLT being standard MIPS code the call is qualified as a 
  cross-mode jump.  However this is not really the reason the call needs to 
  be redirected for -- the redirection would have to be done regardless 
  even if we did decide to emit the PLT entry as MIPS16 code for some 
  reason.

That is, if we (redundantly) created both standard and MIPS16 PLT
entries for functions with stubs, and if the "if" statement as you had
it redirected the call from a standard PLT entry to a MIPS16 PLT entry,
this code must explicitly ignore that transformation.  So why do it?
Making a point of doing it ahead of the if-else block, only to explicitly
ignore it in the if-else block, just makes the code more confusing IMO.

Richard


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-06-08 16:04         ` Richard Sandiford
@ 2013-06-10 17:13           ` Maciej W. Rozycki
  2013-06-10 18:08             ` Richard Sandiford
  0 siblings, 1 reply; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-06-10 17:13 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Joel Brobecker, Catherine Moore, binutils, gdb-patches

On Sat, 8 Jun 2013, Richard Sandiford wrote:

> The changes made by the block beginning:
> 
>    /* If this is a reference to a 16-bit function with a stub, we need
>       to redirect the relocation to the stub unless:
> 
> are mutually-exclusive with each other and with the new PLT transformation.
> We should only ever perform one.  And the transformation:
> 
>   /* 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))
> 	   && ((h != NULL && h->use_plt_entry) || !target_is_16_bit_code_p))
> 
> logically trumps the PLT one.
> 
> That's why I think all four transformations should be in a single if chain,
> and why the PLT one should come last.
> 
> You said yourself about the addition of (h != NULL && h->use_plt_entry):
> 
>   The current code flow is a bit subtle, this piece works because as a side 
>   effect of the PLT being standard MIPS code the call is qualified as a 
>   cross-mode jump.  However this is not really the reason the call needs to 
>   be redirected for -- the redirection would have to be done regardless 
>   even if we did decide to emit the PLT entry as MIPS16 code for some 
>   reason.
> 
> That is, if we (redundantly) created both standard and MIPS16 PLT
> entries for functions with stubs, and if the "if" statement as you had
> it redirected the call from a standard PLT entry to a MIPS16 PLT entry,
> this code must explicitly ignore that transformation.  So why do it?
> Making a point of doing it ahead of the if-else block, only to explicitly
> ignore it in the if-else block, just makes the code more confusing IMO.

 OK, you've convinced me, we just need to be careful making sure all the 
cases remain mutually exclusive (I'm not sure why the la25_stub case lacks 
a !relocatable qualification -- is that intentional or an oversight?).

 Since we're going through this extra iteration and to avoid any surprises 
I've decided to post the full patch this time, with your lone request 
applied.  You haven't made comments on any other parts of the update so 
I'm assuming they all looked fine to you, but please let me know if you do 
have any other questions or comments.

 I have tested this change with the binutils test suite over the usual 
targets with no regressions (and no problems with the original PLT test 
suite update posted separately).  OK to apply (once the in_plt_section 
change for GDB has been committed)?

2013-06-10  Maciej W. Rozycki  <macro@codesourcery.com>

	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 (plt_entry): New structure.
	(mips_elf_link_hash_entry): Add use_plt_entry member.
	(mips_elf_link_hash_table): Rename plt_entry_size member to
	plt_mips_entry_size.  Add plt_comp_entry_size, plt_mips_offset,
	plt_comp_offset, plt_got_index entries and plt_header_is_comp
	members.
	(STUB_LW_MICROMIPS, STUB_MOVE_MICROMIPS): New macros.
	(STUB_LUI_MICROMIPS, STUB_JALR_MICROMIPS): Likewise.
	(STUB_ORI_MICROMIPS, STUB_LI16U_MICROMIPS): Likewise.
	(STUB_LI16S_MICROMIPS): Likewise.
	(MICROMIPS_FUNCTION_STUB_NORMAL_SIZE): Likewise.
	(MICROMIPS_FUNCTION_STUB_BIG_SIZE): Likewise.
	(micromips_o32_exec_plt0_entry): New variable.
	(mips16_o32_exec_plt_entry): Likewise.
	(micromips_o32_exec_plt_entry): Likewise.
	(mips_elf_link_hash_newfunc): Initialize use_plt_entry.
	(mips_elf_output_extsym): Update to use gotplt_union's plist
	member rather than offset.
	(mips_elf_gotplt_index): Likewise.  Remove the VxWorks
	restriction.  Use MIPS_ELF_GOT_SIZE to calculate GOT address.
	(mips_elf_count_got_symbols): Update to use gotplt_union's plist
	member rather than offset.
	(mips_elf_calculate_relocation): Handle MIPS16/microMIPS PLT
	entries.
	(_bfd_mips_elf_create_dynamic_sections): Don't set PLT sizes
	here.
	(mips_elf_make_plt_record): New function.
	(_bfd_mips_elf_check_relocs): Update comment.  Record occurences
	of JAL relocations that might need a PLT entry.
	(_bfd_mips_elf_adjust_dynamic_symbol): Update to use
	gotplt_union's plist member rather than offset.  Set individual
	PLT entry sizes here.  Handle MIPS16/microMIPS PLT entries.
	Don't set the symbol's value in the symbol table for PLT
	references here.  Don't set the PLT or PLT GOT section sizes
	here.
	(mips_elf_estimate_stub_size): Handle microMIPS stubs.
	(mips_elf_allocate_lazy_stub): Likewise.
	(mips_elf_lay_out_lazy_stubs): Likewise.  Define a _MIPS_STUBS_
	magic symbol.
	(mips_elf_set_plt_sym_value): New function.
	(_bfd_mips_elf_size_dynamic_sections): Set PLT header size and
	PLT and PLT GOT section sizes here.  Set the symbol values in
	the symbol table for PLT references here.  Handle microMIPS
	annotation of the _PROCEDURE_LINKAGE_TABLE_ magic symbol.
	(_bfd_mips_elf_finish_dynamic_symbol): Update to use
	gotplt_union's plist member rather than offset.  Handle
	MIPS16/microMIPS PLT entries.  Handle microMIPS stubs.
	(_bfd_mips_vxworks_finish_dynamic_symbol): Update to use
	gotplt_union's plist member rather than offset.  Use
	MIPS_ELF_GOT_SIZE to calculate GOT address.
	(mips_finish_exec_plt): Handle microMIPS PLT.  Return status.
	(_bfd_mips_elf_finish_dynamic_sections): Handle result from
	mips_finish_exec_plt.
	(_bfd_mips_elf_link_hash_table_create): Update to use
	gotplt_union's plist member rather than offset.
	(_bfd_mips_elf_get_synthetic_symtab): New function.

	include/elf/
	* mips.h (ELF_ST_IS_MIPS_PLT): Respect STO_MIPS16 setting.
	(ELF_ST_SET_MIPS_PLT): Likewise.

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

	ld/
	* emulparams/elf32btsmip.sh: Arrange for .got.plt to be placed
	as close to .plt as possible.
	* scripttempl/elf.sc: Handle $INITIAL_READWRITE_SECTIONS and
	$PLT_NEXT_DATA variables.

	ld/testsuite/
	* ld-mips-elf/jalx-2.dd: Update for microMIPS PLT support.
	* ld-mips-elf/pic-and-nonpic-3a.dd: Update for the _MIPS_STUBS_
	magic symbol.
	* ld-mips-elf/pic-and-nonpic-3b.dd: Likewise.
	* ld-mips-elf/pic-and-nonpic-6-n32.dd: Likewise.
	* ld-mips-elf/pic-and-nonpic-6-n64.dd: Likewise.
	* ld-mips-elf/pic-and-nonpic-6-o32.dd: Likewise.
	* ld-mips-elf/stub-dynsym-1-10000.d: Likewise.
	* ld-mips-elf/stub-dynsym-1-2fe80.d: Likewise.
	* ld-mips-elf/stub-dynsym-1-7fff.d: Likewise.
	* ld-mips-elf/stub-dynsym-1-8000.d: Likewise.
	* ld-mips-elf/stub-dynsym-1-fff0.d: Likewise.
	* ld-mips-elf/tlslib-o32.d: Likewise.

	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.

  Maciej

binutils-umips16-plt-stubs.diff
Index: binutils-fsf-trunk-quilt/bfd/elf32-mips.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/bfd/elf32-mips.c	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/bfd/elf32-mips.c	2013-06-10 15:54:49.011234520 +0100
@@ -2366,7 +2366,6 @@ static const struct ecoff_debug_swap mip
 #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 \
@@ -2378,6 +2377,7 @@ static const struct ecoff_debug_swap mip
 					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
@@ -2505,7 +2505,6 @@ mips_vxworks_final_write_processing (bfd
 #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 \
@@ -2531,4 +2530,6 @@ mips_vxworks_final_write_processing (bfd
 #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: binutils-fsf-trunk-quilt/bfd/elfxx-mips.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/bfd/elfxx-mips.c	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/bfd/elfxx-mips.c	2013-06-10 16:44:59.201145797 +0100
@@ -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.  */
@@ -435,8 +464,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 +507,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.  */
@@ -854,8 +898,28 @@ 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_LUI_MICROMIPS(VAL)						\
+   (0x41b80000 + (VAL))				/* lui t8,VAL */
+#define STUB_JALR_MICROMIPS 0x45d9		/* jalr 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
 
 /* The name of the dynamic interpreter.  This is put in the .interp
    section.  */
@@ -967,7 +1031,26 @@ static const bfd_vma mips_n64_exec_plt0_
   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 subsequent standard PLT entries.  */
 static const bfd_vma mips_exec_plt_entry[] =
 {
   0x3c0f0000,	/* lui $15, %hi(.got.plt entry)			*/
@@ -976,6 +1059,30 @@ 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 the first PLT entry in a VxWorks executable.  */
 static const bfd_vma mips_vxworks_exec_plt0_entry[] =
 {
@@ -1114,6 +1221,7 @@ mips_elf_link_hash_newfunc (struct bfd_h
       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 +2836,8 @@ mips_elf_output_extsym (struct mips_elf_
 
       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 +2847,7 @@ mips_elf_output_extsym (struct mips_elf_
 	    {
 	      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 +3323,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
@@ -4198,7 +4303,7 @@ mips_elf_count_got_symbols (struct mips_
 	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.  */
@@ -5177,10 +5282,7 @@ mips_elf_calculate_relocation (bfd *abfd
 	}
 
       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 +5333,16 @@ mips_elf_calculate_relocation (bfd *abfd
       /* 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 +5384,31 @@ mips_elf_calculate_relocation (bfd *abfd
     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)
@@ -7350,34 +7477,10 @@ _bfd_mips_elf_create_dynamic_sections (b
       || !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 +7608,27 @@ mips_elf_get_section_contents (bfd *abfd
   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,
@@ -8209,6 +8331,28 @@ _bfd_mips_elf_check_relocs (bfd *abfd, s
 	  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;
+	}
+
       /* 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
@@ -8611,11 +8755,16 @@ _bfd_mips_elf_adjust_dynamic_symbol (str
 	   && !(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 +8780,100 @@ _bfd_mips_elf_adjust_dynamic_symbol (str
 					  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 (micromips_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 (mips16_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 +9124,58 @@ mips_elf_estimate_stub_size (bfd *output
   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.  */
+  if (MICROMIPS_P (output_bfd))
+    htab->function_stub_size = (dynsymcount > 0x10000
+				? MICROMIPS_FUNCTION_STUB_BIG_SIZE
+				: MICROMIPS_FUNCTION_STUB_NORMAL_SIZE);
+  else
+    htab->function_stub_size = (dynsymcount > 0x10000
+				? MIPS_FUNCTION_STUB_BIG_SIZE
+				: MIPS_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 +9184,97 @@ mips_elf_allocate_lazy_stub (struct mips
 /* 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 +9306,66 @@ _bfd_mips_elf_size_dynamic_sections (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);
 
-	  h = _bfd_elf_define_linkage_sym (dynobj, info, htab->splt,
-					   "_PROCEDURE_LINKAGE_TABLE_");
-	  htab->root.hplt = h;
-	  if (h == NULL)
-	    return FALSE;
+	  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
+	    size = 2 * ARRAY_SIZE (micromips_o32_exec_plt0_entry);
+
+	  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 +10204,145 @@ _bfd_mips_elf_finish_dynamic_symbol (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 = 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);
+	    }
+	  else
+	    {
+	      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);
+	    }
 	}
 
       /* 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 +10351,34 @@ _bfd_mips_elf_finish_dynamic_symbol (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 = MICROMIPS_FUNCTION_STUB_BIG_SIZE;
+      else
+	stub_big_size = MIPS_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 +10387,76 @@ _bfd_mips_elf_finish_dynamic_symbol (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 (micromips_p)
+	{
+	  idx = 0;
+	  bfd_put_micromips_32 (output_bfd, STUB_LW_MICROMIPS (output_bfd),
+				stub + idx);
+	  idx += 4;
+	  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;
 
-      /* 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);
+	      bfd_put_micromips_32 (output_bfd,
+				    STUB_LUI_MICROMIPS (dynindx_hi),
+				    stub + idx);
+	      idx += 4;
+	    }
+	  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;
 
-      BFD_ASSERT (h->plt.offset <= htab->sstubs->size);
-      memcpy (htab->sstubs->contents + h->plt.offset,
-	      stub, htab->function_stub_size);
+	  /* 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);
+	}
 
-      /* Mark the symbol as undefined.  plt.offset != -1 occurs
+      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.  stub_offset != -1 occurs
 	 only for the referenced symbol.  */
       sym->st_shndx = SHN_UNDEF;
 
@@ -9973,7 +10465,9 @@ _bfd_mips_elf_finish_dynamic_symbol (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 +10655,32 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
   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 +10688,21 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 
       /* 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 +10713,7 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 	  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 +10722,12 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 	  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 +10745,8 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 	}
 
       /* 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 +10813,7 @@ _bfd_mips_vxworks_finish_dynamic_symbol 
 
 /* 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,6 +10828,8 @@ mips_finish_exec_plt (bfd *output_bfd, s
     plt_entry = mips_n64_exec_plt0_entry;
   else if (ABI_N32_P (output_bfd))
     plt_entry = mips_n32_exec_plt0_entry;
+  else if (htab->plt_header_is_comp)
+    plt_entry = micromips_o32_exec_plt0_entry;
   else
     plt_entry = mips_o32_exec_plt0_entry;
 
@@ -10346,14 +10846,49 @@ mips_finish_exec_plt (bfd *output_bfd, s
 
   /* 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
+    {
+      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 +11410,8 @@ _bfd_mips_elf_finish_dynamic_sections (b
       else
 	{
 	  BFD_ASSERT (!info->shared);
-	  mips_finish_exec_plt (output_bfd, info);
+	  if (!mips_finish_exec_plt (output_bfd, info))
+	    return FALSE;
 	}
     }
   return TRUE;
@@ -12868,6 +13404,8 @@ _bfd_mips_elf_link_hash_table_create (bf
       free (ret);
       return NULL;
     }
+  ret->root.init_plt_refcount.plist = NULL;
+  ret->root.init_plt_offset.plist = NULL;
 
   return &ret->root.root;
 }
@@ -14357,6 +14895,226 @@ _bfd_mips_elf_plt_sym_val (bfd_vma i, co
 	  + 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
+    {
+      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.  */
+      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;
+	}
+      /* 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: binutils-fsf-trunk-quilt/bfd/elfxx-mips.h
===================================================================
--- binutils-fsf-trunk-quilt.orig/bfd/elfxx-mips.h	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/bfd/elfxx-mips.h	2013-06-10 15:54:49.000000000 +0100
@@ -152,6 +152,8 @@ extern bfd_boolean _bfd_mips_elf_init_st
    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: binutils-fsf-trunk-quilt/gdb/mips-tdep.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/gdb/mips-tdep.c	2013-06-10 15:54:48.011215103 +0100
+++ binutils-fsf-trunk-quilt/gdb/mips-tdep.c	2013-06-10 15:54:49.011234520 +0100
@@ -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: binutils-fsf-trunk-quilt/include/elf/mips.h
===================================================================
--- binutils-fsf-trunk-quilt.orig/include/elf/mips.h	2013-06-10 16:44:50.200865664 +0100
+++ binutils-fsf-trunk-quilt/include/elf/mips.h	2013-06-10 16:44:59.201145797 +0100
@@ -803,8 +803,14 @@ extern void bfd_mips_elf32_swap_reginfo_
    PLT entries and traditional MIPS lazy binding stubs.  We mark the former
    with STO_MIPS_PLT to distinguish them from the latter.  */
 #define STO_MIPS_PLT		0x8
-#define ELF_ST_IS_MIPS_PLT(other) (((other) & STO_MIPS_FLAGS) == STO_MIPS_PLT)
-#define ELF_ST_SET_MIPS_PLT(other) (((other) & ~STO_MIPS_FLAGS) | STO_MIPS_PLT)
+#define ELF_ST_IS_MIPS_PLT(other)					\
+  ((ELF_ST_IS_MIPS16 (other)						\
+    ? ((other) & (~STO_MIPS16 & STO_MIPS_FLAGS))			\
+    : ((other) & STO_MIPS_FLAGS)) == STO_MIPS_PLT)
+#define ELF_ST_SET_MIPS_PLT(other)					\
+  ((ELF_ST_IS_MIPS16 (other)						\
+    ? ((other) & (STO_MIPS16 | ~STO_MIPS_FLAGS))			\
+    : ((other) & ~STO_MIPS_FLAGS)) | STO_MIPS_PLT)
 
 /* This value is used to mark PIC functions in an object that mixes
    PIC and non-PIC.  Note that this bit overlaps with STO_MIPS16,
Index: binutils-fsf-trunk-quilt/ld/emulparams/elf32btsmip.sh
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/emulparams/elf32btsmip.sh	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/ld/emulparams/elf32btsmip.sh	2013-06-10 15:54:49.011234520 +0100
@@ -8,3 +8,10 @@ LITTLE_OUTPUT_FORMAT="elf32-tradlittlemi
 unset DATA_ADDR
 SHLIB_TEXT_START_ADDR=0
 ENTRY=__start
+
+# Place .got.plt as close to .plt as possible so that the former can be
+# referred to from the latter with the microMIPS ADDIUPC instruction
+# that only has a span of +/-16MB.
+PLT_NEXT_DATA=
+INITIAL_READWRITE_SECTIONS=$OTHER_READWRITE_SECTIONS
+unset OTHER_READWRITE_SECTIONS
Index: binutils-fsf-trunk-quilt/ld/scripttempl/elf.sc
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/scripttempl/elf.sc	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/ld/scripttempl/elf.sc	2013-06-10 15:54:49.011234520 +0100
@@ -10,6 +10,7 @@
 #	OTHER_READONLY_SECTIONS - other than .text .init .rodata ...
 #		(e.g., .PARISC.milli)
 #	OTHER_TEXT_SECTIONS - these get put in .text when relocating
+#	INITIAL_READWRITE_SECTIONS - at start of data segment (after relro)
 #	OTHER_READWRITE_SECTIONS - other than .data .bss .ctors .sdata ...
 #		(e.g., .PARISC.global)
 #	OTHER_RELRO_SECTIONS - other than .data.rel.ro ...
@@ -33,6 +34,7 @@
 #	OTHER_SDATA_SECTIONS - sections just after .sdata.
 #	OTHER_BSS_SYMBOLS - symbols that appear at the start of the
 #		.bss section besides __bss_start.
+#	PLT_NEXT_DATA - .plt next to data segment when .plt is in text segment.
 #	DATA_PLT - .plt should be in data segment, not text segment.
 #	PLT_BEFORE_GOT - .plt just before .got when .plt is in data segement.
 #	BSS_PLT - .plt should be in bss segment
@@ -132,7 +134,7 @@ if test -z "$PLT"; then
   PLT=".plt          ${RELOCATING-0} : { *(.plt)${IREL_IN_PLT+ *(.iplt)} }
   ${IREL_IN_PLT-$IPLT}"
 fi
-test -n "${DATA_PLT-${BSS_PLT-text}}" && TEXT_PLT=yes
+test -n "${DATA_PLT-${BSS_PLT-text}}" && TEXT_PLT=
 if test -z "$GOT"; then
   if test -z "$SEPARATE_GOTPLT"; then
     GOT=".got          ${RELOCATING-0} : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }"
@@ -463,7 +465,7 @@ cat <<EOF
     ${RELOCATING+${INIT_END}}
   } ${FILL}
 
-  ${TEXT_PLT+${PLT}}
+  ${TEXT_PLT+${PLT_NEXT_DATA-${PLT}}}
   ${TINY_READONLY_SECTION}
   .text         ${RELOCATING-0} :
   {
@@ -527,6 +529,7 @@ cat <<EOF
   /* These sections are generated by the Sun/Oracle C++ compiler.  */
   .exception_ranges ${RELOCATING-0} : ONLY_IF_RO { *(.exception_ranges
   .exception_ranges*) }
+  ${TEXT_PLT+${PLT_NEXT_DATA+${PLT}}}
 
   /* Adjust the address for the data segment.  We want to adjust up to
      the same address within the page on the next page up.  */
@@ -562,6 +565,7 @@ cat <<EOF
   ${DATA_GOT+${RELRO_NOW+${GOTPLT}}}
   ${DATA_GOT+${RELRO_NOW-${SEPARATE_GOTPLT+${GOT}}}}
   ${RELOCATING+${DATA_SEGMENT_RELRO_END}}
+  ${INITIAL_READWRITE_SECTIONS}
   ${DATA_GOT+${RELRO_NOW-${SEPARATE_GOTPLT-${GOT}}}}
   ${DATA_GOT+${RELRO_NOW-${GOTPLT}}}
 
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/jalx-2.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/jalx-2.dd	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/jalx-2.dd	2013-06-10 15:54:49.011234520 +0100
@@ -28,8 +28,8 @@
  4400034:	f89e 0020 	sw	a0,32\(s8\)
  4400038:	f8be 0024 	sw	a1,36\(s8\)
  440003c:	41a2 0440 	lui	v0,0x440
- 4400040:	3082 02a0 	addiu	a0,v0,672
- 4400044:	f110 0028 	jalx	44000a0 <printf@plt>
+ 4400040:	3082 0290 	addiu	a0,v0,656
+ 4400044:	f620 004c 	jal	4400098 <printf@micromipsplt>
  4400048:	0000 0000 	nop
  440004c:	f620 0010 	jal	4400020 <internal_function>
  4400050:	0000 0000 	nop
@@ -44,17 +44,18 @@
 Disassembly of section \.plt:
 
 04400080 <_PROCEDURE_LINKAGE_TABLE_>:
- 4400080:	3c1c0440 	lui	gp,0x440
- 4400084:	8f9900d8 	lw	t9,216\(gp\)
- 4400088:	279c00d8 	addiu	gp,gp,216
- 440008c:	031cc023 	subu	t8,t8,gp
- 4400090:	03e07821 	move	t7,ra
- 4400094:	0018c082 	srl	t8,t8,0x2
- 4400098:	0320f809 	jalr	t9
- 440009c:	2718fffe 	addiu	t8,t8,-2
+ 4400080:	7980 0012 	addiu	v1,\$pc,72
+ 4400084:	ff23 0000 	lw	t9,0\(v1\)
+ 4400088:	0535      	subu	v0,v0,v1
+ 440008a:	2525      	srl	v0,v0,2
+ 440008c:	3302 fffe 	addiu	t8,v0,-2
+ 4400090:	0dff      	move	t7,ra
+ 4400092:	45f9      	jalrs	t9
+ 4400094:	0f83      	move	gp,v1
+ 4400096:	0c00      	nop
 
-044000a0 <printf@plt>:
- 44000a0:	3c0f0440 	lui	t7,0x440
- 44000a4:	8df900e0 	lw	t9,224\(t7\)
- 44000a8:	03200008 	jr	t9
- 44000ac:	25f800e0 	addiu	t8,t7,224
+04400098 <printf@micromipsplt>:
+ 4400098:	7900 000e 	addiu	v0,\$pc,56
+ 440009c:	ff22 0000 	lw	t9,0\(v0\)
+ 44000a0:	4599      	jr	t9
+ 44000a2:	0f02      	move	t8,v0
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-3a.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-3a.dd	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-3a.dd	2013-06-10 15:54:49.011234520 +0100
@@ -31,7 +31,7 @@
 #...
 Disassembly of section \.MIPS\.stubs:
 
-00000c00 <.MIPS.stubs>:
+00000c00 <_MIPS_STUBS_>:
  c00:	8f998010 	lw	t9,-32752\(gp\)
  c04:	03e07821 	move	t7,ra
  c08:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-3b.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-3b.dd	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-3b.dd	2013-06-10 15:54:49.011234520 +0100
@@ -42,9 +42,10 @@
 .*:	03200008 	jr	t9
 .*:	00000000 	nop
 .*:	00000000 	nop
-Disassembly of section .MIPS.stubs:
 
-00044030 <\.MIPS\.stubs>:
+Disassembly of section \.MIPS\.stubs:
+
+00044030 <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n32.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n32.dd	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n32.dd	2013-06-10 15:54:49.011234520 +0100
@@ -91,9 +91,10 @@
    44090:	3c02000a 	lui	v0,0xa
    44094:	24422018 	addiu	v0,v0,8216
 	\.\.\.
+
 Disassembly of section \.MIPS\.stubs:
 
-000440a0 <\.MIPS\.stubs>:
+000440a0 <_MIPS_STUBS_>:
    440a0:	8f998010 	lw	t9,-32752\(gp\)
    440a4:	03e07821 	move	t3,ra
    440a8:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n64.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n64.dd	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-n64.dd	2013-06-10 15:54:49.011234520 +0100
@@ -91,9 +91,10 @@
    44090:	3c02000a 	lui	v0,0xa
    44094:	24422018 	addiu	v0,v0,8216
 	\.\.\.
+
 Disassembly of section \.MIPS\.stubs:
 
-0+440a0 <\.MIPS\.stubs>:
+0+440a0 <_MIPS_STUBS_>:
    440a0:	df998010 	ld	t9,-32752\(gp\)
    440a4:	03e0782d 	move	t3,ra
    440a8:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-o32.dd
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-o32.dd	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/pic-and-nonpic-6-o32.dd	2013-06-10 15:54:49.011234520 +0100
@@ -91,9 +91,10 @@
    44090:	3c02000a 	lui	v0,0xa
    44094:	24422018 	addiu	v0,v0,8216
 	\.\.\.
+
 Disassembly of section \.MIPS\.stubs:
 
-000440a0 <\.MIPS\.stubs>:
+000440a0 <_MIPS_STUBS_>:
    440a0:	8f998010 	lw	t9,-32752\(gp\)
    440a4:	03e07821 	move	t7,ra
    440a8:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-10000.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-10000.d	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-10000.d	2013-06-10 15:54:49.011234520 +0100
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	3c180001 	lui	t8,0x1
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-2fe80.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-2fe80.d	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-2fe80.d	2013-06-10 15:54:49.011234520 +0100
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	3c180002 	lui	t8,0x2
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-7fff.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-7fff.d	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-7fff.d	2013-06-10 15:54:49.011234520 +0100
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-8000.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-8000.d	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-8000.d	2013-06-10 15:54:49.011234520 +0100
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-fff0.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/stub-dynsym-1-fff0.d	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/stub-dynsym-1-fff0.d	2013-06-10 15:54:49.011234520 +0100
@@ -3,7 +3,7 @@
 
 Disassembly of section \.MIPS\.stubs:
 
-.* <\.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
 .*:	8f998010 	lw	t9,-32752\(gp\)
 .*:	03e07821 	move	t7,ra
 .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/tlslib-o32.d
===================================================================
--- binutils-fsf-trunk-quilt.orig/ld/testsuite/ld-mips-elf/tlslib-o32.d	2013-06-10 15:44:30.970125768 +0100
+++ binutils-fsf-trunk-quilt/ld/testsuite/ld-mips-elf/tlslib-o32.d	2013-06-10 15:54:49.011234520 +0100
@@ -35,9 +35,10 @@
  .*:	03e00008 	jr	ra
  .*:	27bd0010 	addiu	sp,sp,16
 	...
+
 Disassembly of section .MIPS.stubs:
 
-.* <.MIPS.stubs>:
+.* <_MIPS_STUBS_>:
  .*:	8f998010 	lw	t9,-32752\(gp\)
  .*:	03e07821 	move	t7,ra
  .*:	0320f809 	jalr	t9
Index: binutils-fsf-trunk-quilt/opcodes/mips-dis.c
===================================================================
--- binutils-fsf-trunk-quilt.orig/opcodes/mips-dis.c	2013-06-10 15:53:06.001144329 +0100
+++ binutils-fsf-trunk-quilt/opcodes/mips-dis.c	2013-06-10 15:54:49.011234520 +0100
@@ -2052,6 +2052,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
@@ -2059,7 +2076,7 @@ print_insn_mips16 (bfd_vma memaddr, stru
 {
   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;
@@ -2072,11 +2089,32 @@ print_insn_mips16 (bfd_vma memaddr, stru
   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);
@@ -2950,27 +2988,26 @@ print_insn_micromips (bfd_vma memaddr, s
 static bfd_boolean
 is_compressed_mode_p (struct disassemble_info *info)
 {
-  elf_symbol_type *symbol;
-  int pos;
   int i;
+  int l;
 
-  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;
-    }
+  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;
 }


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-06-10 17:13           ` Maciej W. Rozycki
@ 2013-06-10 18:08             ` Richard Sandiford
  2013-06-10 19:34               ` Maciej W. Rozycki
  0 siblings, 1 reply; 28+ messages in thread
From: Richard Sandiford @ 2013-06-10 18:08 UTC (permalink / raw)
  To: Maciej W. Rozycki; +Cc: Joel Brobecker, Catherine Moore, binutils, gdb-patches

"Maciej W. Rozycki" <macro@codesourcery.com> writes:
> 2013-06-10  Maciej W. Rozycki  <macro@codesourcery.com>
>
> 	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 (plt_entry): New structure.
> 	(mips_elf_link_hash_entry): Add use_plt_entry member.
> 	(mips_elf_link_hash_table): Rename plt_entry_size member to
> 	plt_mips_entry_size.  Add plt_comp_entry_size, plt_mips_offset,
> 	plt_comp_offset, plt_got_index entries and plt_header_is_comp
> 	members.
> 	(STUB_LW_MICROMIPS, STUB_MOVE_MICROMIPS): New macros.
> 	(STUB_LUI_MICROMIPS, STUB_JALR_MICROMIPS): Likewise.
> 	(STUB_ORI_MICROMIPS, STUB_LI16U_MICROMIPS): Likewise.
> 	(STUB_LI16S_MICROMIPS): Likewise.
> 	(MICROMIPS_FUNCTION_STUB_NORMAL_SIZE): Likewise.
> 	(MICROMIPS_FUNCTION_STUB_BIG_SIZE): Likewise.
> 	(micromips_o32_exec_plt0_entry): New variable.
> 	(mips16_o32_exec_plt_entry): Likewise.
> 	(micromips_o32_exec_plt_entry): Likewise.
> 	(mips_elf_link_hash_newfunc): Initialize use_plt_entry.
> 	(mips_elf_output_extsym): Update to use gotplt_union's plist
> 	member rather than offset.
> 	(mips_elf_gotplt_index): Likewise.  Remove the VxWorks
> 	restriction.  Use MIPS_ELF_GOT_SIZE to calculate GOT address.
> 	(mips_elf_count_got_symbols): Update to use gotplt_union's plist
> 	member rather than offset.
> 	(mips_elf_calculate_relocation): Handle MIPS16/microMIPS PLT
> 	entries.
> 	(_bfd_mips_elf_create_dynamic_sections): Don't set PLT sizes
> 	here.
> 	(mips_elf_make_plt_record): New function.
> 	(_bfd_mips_elf_check_relocs): Update comment.  Record occurences
> 	of JAL relocations that might need a PLT entry.
> 	(_bfd_mips_elf_adjust_dynamic_symbol): Update to use
> 	gotplt_union's plist member rather than offset.  Set individual
> 	PLT entry sizes here.  Handle MIPS16/microMIPS PLT entries.
> 	Don't set the symbol's value in the symbol table for PLT
> 	references here.  Don't set the PLT or PLT GOT section sizes
> 	here.
> 	(mips_elf_estimate_stub_size): Handle microMIPS stubs.
> 	(mips_elf_allocate_lazy_stub): Likewise.
> 	(mips_elf_lay_out_lazy_stubs): Likewise.  Define a _MIPS_STUBS_
> 	magic symbol.
> 	(mips_elf_set_plt_sym_value): New function.
> 	(_bfd_mips_elf_size_dynamic_sections): Set PLT header size and
> 	PLT and PLT GOT section sizes here.  Set the symbol values in
> 	the symbol table for PLT references here.  Handle microMIPS
> 	annotation of the _PROCEDURE_LINKAGE_TABLE_ magic symbol.
> 	(_bfd_mips_elf_finish_dynamic_symbol): Update to use
> 	gotplt_union's plist member rather than offset.  Handle
> 	MIPS16/microMIPS PLT entries.  Handle microMIPS stubs.
> 	(_bfd_mips_vxworks_finish_dynamic_symbol): Update to use
> 	gotplt_union's plist member rather than offset.  Use
> 	MIPS_ELF_GOT_SIZE to calculate GOT address.
> 	(mips_finish_exec_plt): Handle microMIPS PLT.  Return status.
> 	(_bfd_mips_elf_finish_dynamic_sections): Handle result from
> 	mips_finish_exec_plt.
> 	(_bfd_mips_elf_link_hash_table_create): Update to use
> 	gotplt_union's plist member rather than offset.
> 	(_bfd_mips_elf_get_synthetic_symtab): New function.
>
> 	include/elf/
> 	* mips.h (ELF_ST_IS_MIPS_PLT): Respect STO_MIPS16 setting.
> 	(ELF_ST_SET_MIPS_PLT): Likewise.
>
> 	gdb/
> 	* mips-tdep.c (mips_elf_make_msymbol_special): Handle MIPS16 and
> 	microMIPS synthetic symbols.
>
> 	ld/
> 	* emulparams/elf32btsmip.sh: Arrange for .got.plt to be placed
> 	as close to .plt as possible.
> 	* scripttempl/elf.sc: Handle $INITIAL_READWRITE_SECTIONS and
> 	$PLT_NEXT_DATA variables.
>
> 	ld/testsuite/
> 	* ld-mips-elf/jalx-2.dd: Update for microMIPS PLT support.
> 	* ld-mips-elf/pic-and-nonpic-3a.dd: Update for the _MIPS_STUBS_
> 	magic symbol.
> 	* ld-mips-elf/pic-and-nonpic-3b.dd: Likewise.
> 	* ld-mips-elf/pic-and-nonpic-6-n32.dd: Likewise.
> 	* ld-mips-elf/pic-and-nonpic-6-n64.dd: Likewise.
> 	* ld-mips-elf/pic-and-nonpic-6-o32.dd: Likewise.
> 	* ld-mips-elf/stub-dynsym-1-10000.d: Likewise.
> 	* ld-mips-elf/stub-dynsym-1-2fe80.d: Likewise.
> 	* ld-mips-elf/stub-dynsym-1-7fff.d: Likewise.
> 	* ld-mips-elf/stub-dynsym-1-8000.d: Likewise.
> 	* ld-mips-elf/stub-dynsym-1-fff0.d: Likewise.
> 	* ld-mips-elf/tlslib-o32.d: Likewise.
>
> 	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.

OK for the binutils bits.

Thanks,
Richard


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-06-10 18:08             ` Richard Sandiford
@ 2013-06-10 19:34               ` Maciej W. Rozycki
  2013-06-25  0:40                 ` Maciej W. Rozycki
  0 siblings, 1 reply; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-06-10 19:34 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Joel Brobecker, Catherine Moore, binutils, gdb-patches

On Mon, 10 Jun 2013, Richard Sandiford wrote:

> OK for the binutils bits.

 Thanks for the review, that's been a long journey! :)  I can self-approve 
the mips-tdep.c change (any comments are welcome though, of course) -- I 
just need to wait for someone to have a look at the generic in_plt_section 
bits so as not to regress GDB at the same time.

  Maciej


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PING][PATCH] in_plt_section: support alternate stub section names (was: [PATCH 1/2] MIPS: Compressed PLT/stubs support)
  2013-06-07 13:25 ` [PATCH] in_plt_section: support alternate stub section names (was: [PATCH 1/2] MIPS: Compressed PLT/stubs support) Maciej W. Rozycki
@ 2013-06-13 12:43   ` Maciej W. Rozycki
  0 siblings, 0 replies; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-06-13 12:43 UTC (permalink / raw)
  To: gdb-patches

Hi,

 Would anyone please review this change, posted here:

http://sourceware.org/ml/gdb-patches/2013-06/msg00150.html

-- it would make my life easier if it happened by the end of tomorrow.  I 
just need to know if the change to objfiles.c is acceptable.

 Thanks a lot!

  Maciej


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PING^2][PATCH] in_plt_section: support alternate stub section names (was: [PATCH 1/2] MIPS: Compressed PLT/stubs support)
  2013-02-21 21:06 ` Tom Tromey
  2013-02-22  0:58   ` Alan Modra
@ 2013-06-20 16:20   ` Maciej W. Rozycki
  2013-06-20 16:50     ` [PING^2][PATCH] in_plt_section: support alternate stub section names Pedro Alves
  1 sibling, 1 reply; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-06-20 16:20 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Richard Sandiford, Catherine Moore, binutils, gdb-patches

Tom,

 I have now recalled that you made this comment about the original version 
of this change:

On Thu, 21 Feb 2013, Tom Tromey wrote:

> Maciej> -/* In SVR4, we recognize a trampoline by it's section name. 
> Maciej> -   That is, if the pc is in a section named ".plt" then we are in
> Maciej> -   a trampoline.  */
> Maciej> +/* In SVR4, we recognize a trampoline by it's section name.  That is,
> Maciej> +   if the pc is in a section named ".plt" then we are in a trampoline.
> Maciej> +   We let targets request an alternative name, this is currently used
> Maciej> +   by the MIPS backend to handle the SVR4 lazy resolution stubs that
> Maciej> +   binutils put into ".MIPS.stubs" instead.  */
> 
> IMO comments like this tend to get stale over time.
> It is better, I think, to describe the function's interface and purpose,
> and leave it to future developers to grep for uses of it, should they
> need to know.

 I have now made the requested update, see below.  Is this version OK to 
apply?

2013-06-20  Maciej W. Rozycki  <macro@codesourcery.com>

	gdb/
	* mips-linux-tdep.c (mips_linux_in_dynsym_stub): Handle
	.MIPS.stubs section like .plt.  Remove unused `name' argument.
	Return 1 rather than the low 16-bit halfword of any instruction
	examined.
	(mips_linux_in_dynsym_resolve_code): Update accordingly.
	* mips-tdep.c (mips_stub_frame_sniffer): Call in_plt_section in
	place of an equivalent hand-coded sequence.
	* objfiles.c (in_plt_section): Reuse the `name' argument as an
	trampoline section name override.

  Maciej

gdb-mips-in-stubs-section.diff
Index: gdb-fsf-trunk-quilt/gdb/mips-linux-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/mips-linux-tdep.c	2013-06-19 16:54:49.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/mips-linux-tdep.c	2013-06-19 16:55:00.280199593 +0100
@@ -30,6 +30,7 @@
 #include "trad-frame.h"
 #include "tramp-frame.h"
 #include "gdbtypes.h"
+#include "objfiles.h"
 #include "solib.h"
 #include "solib-svr4.h"
 #include "solist.h"
@@ -666,25 +667,34 @@ mips_linux_core_read_description (struct
 
 
 /* Check the code at PC for a dynamic linker lazy resolution stub.
-   Because they aren't in the .plt section, we pattern-match on the
-   code generated by GNU ld.  They look like this:
+   GNU ld for MIPS has put lazy resolution stubs into a ".MIPS.stubs"
+   section uniformly since version 2.15.  If the pc is in that section,
+   then we are in such a stub.  Before that ".stub" was used in 32-bit
+   ELF binaries, however we do not bother checking for that since we
+   have never had and that case should be extremely rare these days.
+   Instead we pattern-match on the code generated by GNU ld.  They look
+   like this:
 
    lw t9,0x8010(gp)
    addu t7,ra
    jalr t9,ra
    addiu t8,zero,INDEX
 
-   (with the appropriate doubleword instructions for N64).  Also
-   return the dynamic symbol index used in the last instruction.  */
+   (with the appropriate doubleword instructions for N64).  As any lazy
+   resolution stubs in microMIPS binaries will always be in a
+   ".MIPS.stubs" section we only ever verify standard MIPS patterns. */
 
 static int
-mips_linux_in_dynsym_stub (CORE_ADDR pc, char *name)
+mips_linux_in_dynsym_stub (CORE_ADDR pc)
 {
   gdb_byte buf[28], *p;
   ULONGEST insn, insn1;
   int n64 = (mips_abi (target_gdbarch ()) == MIPS_ABI_N64);
   enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
 
+  if (in_plt_section (pc, ".MIPS.stubs"))
+    return 1;
+
   read_memory (pc - 12, buf, 28);
 
   if (n64)
@@ -742,7 +752,7 @@ mips_linux_in_dynsym_stub (CORE_ADDR pc,
 	return 0;
     }
 
-  return (insn & 0xffff);
+  return 1;
 }
 
 /* Return non-zero iff PC belongs to the dynamic linker resolution
@@ -756,9 +766,10 @@ mips_linux_in_dynsym_resolve_code (CORE_
   if (svr4_in_dynsym_resolve_code (pc))
     return 1;
 
-  /* Pattern match for the stub.  It would be nice if there were a
-     more efficient way to avoid this check.  */
-  if (mips_linux_in_dynsym_stub (pc, NULL))
+  /* Likewise for the stubs.  They live in the .MIPS.stubs section these
+     days, so we check if the PC is within, than fall back to a pattern
+     match.  */
+  if (mips_linux_in_dynsym_stub (pc))
     return 1;
 
   return 0;
Index: gdb-fsf-trunk-quilt/gdb/mips-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/mips-tdep.c	2013-06-19 16:54:49.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/mips-tdep.c	2013-06-19 16:55:00.280199593 +0100
@@ -3628,12 +3628,7 @@ mips_stub_frame_sniffer (const struct fr
   if (in_plt_section (pc, NULL))
     return 1;
 
-  /* Binutils for MIPS puts lazy resolution stubs into .MIPS.stubs.  */
-  s = find_pc_section (pc);
-
-  if (s != NULL
-      && strcmp (bfd_get_section_name (s->objfile->obfd, s->the_bfd_section),
-		 ".MIPS.stubs") == 0)
+  if (in_plt_section (pc, ".MIPS.stubs"))
     return 1;
 
   /* Calling a PIC function from a non-PIC function passes through a
Index: gdb-fsf-trunk-quilt/gdb/objfiles.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/objfiles.c	2013-06-19 16:45:58.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/objfiles.c	2013-06-20 17:10:59.240259104 +0100
@@ -1410,9 +1410,9 @@ find_pc_section (CORE_ADDR pc)
 }
 
 
-/* In SVR4, we recognize a trampoline by it's section name. 
-   That is, if the pc is in a section named ".plt" then we are in
-   a trampoline.  */
+/* Return non-zero if PC is in a dynamic function call trampoline.  In
+   SVR4, we recognize a trampoline by its section name NAME, or ".plt"
+   if NAME is null.  */
 
 int
 in_plt_section (CORE_ADDR pc, char *name)
@@ -1424,7 +1424,7 @@ in_plt_section (CORE_ADDR pc, char *name
 
   retval = (s != NULL
 	    && s->the_bfd_section->name != NULL
-	    && strcmp (s->the_bfd_section->name, ".plt") == 0);
+	    && strcmp (s->the_bfd_section->name, name ? name : ".plt") == 0);
   return (retval);
 }
 \f


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PING^2][PATCH] in_plt_section: support alternate stub section names
  2013-06-20 16:20   ` [PING^2][PATCH] in_plt_section: support alternate stub section names (was: [PATCH 1/2] MIPS: Compressed PLT/stubs support) Maciej W. Rozycki
@ 2013-06-20 16:50     ` Pedro Alves
  2013-06-21 11:43       ` Maciej W. Rozycki
  0 siblings, 1 reply; 28+ messages in thread
From: Pedro Alves @ 2013-06-20 16:50 UTC (permalink / raw)
  To: Maciej W. Rozycki
  Cc: Tom Tromey, Richard Sandiford, Catherine Moore, binutils, gdb-patches

On 06/20/2013 05:19 PM, Maciej W. Rozycki wrote:

>  
>    return 0;
> Index: gdb-fsf-trunk-quilt/gdb/mips-tdep.c
> ===================================================================
> --- gdb-fsf-trunk-quilt.orig/gdb/mips-tdep.c	2013-06-19 16:54:49.000000000 +0100
> +++ gdb-fsf-trunk-quilt/gdb/mips-tdep.c	2013-06-19 16:55:00.280199593 +0100
> @@ -3628,12 +3628,7 @@ mips_stub_frame_sniffer (const struct fr
>    if (in_plt_section (pc, NULL))
>      return 1;
>  
> -  /* Binutils for MIPS puts lazy resolution stubs into .MIPS.stubs.  */
> -  s = find_pc_section (pc);
> -
> -  if (s != NULL
> -      && strcmp (bfd_get_section_name (s->objfile->obfd, s->the_bfd_section),
> -		 ".MIPS.stubs") == 0)
> +  if (in_plt_section (pc, ".MIPS.stubs"))
>      return 1;

Quite honestly, this looks like an odd API to me.  If all
MIPS callers will have to pass in ".MIPS.stubs", then it just
looks like in_plt_section becomes a convenience for "is
pc in section.

It'd make more sense to me to refactor in_plt_section to
something like this, somewhere:

int
pc_in_section (CORE_ADDR pc, const char *name)
{
  struct obj_section *s;
  int retval = 0;

  s = find_pc_section (pc);

  retval = (s != NULL
	    && s->the_bfd_section->name != NULL
	    && strcmp (s->the_bfd_section->name, name) == 0);
  return (retval);
}

And then:

/* In SVR4, we recognize a trampoline by it's section name.
   That is, if the pc is in a section named ".plt" then we are in
   a trampoline.  */

int
in_plt_section (CORE_ADDR pc)
{
  return pc_in_section (pc, ".plt");
}

And then MIPS would have somewhere, mips-tdep.c perhaps,
something like:

int
in_mips_stubs_section (CORE_ADDR pc)
{
  return pc_in_section (pc, ".MIPS.stubs");
}

Or

#define MIPS_STUBS_SECTION ".MIPS.stubs"
pc_in_section (pc, MIPS_STUBS_SECTION);

As bonus, you end up with just one place that
can typo the section name.

Perhaps missed the plan to make in_plt_section fetch the
section name from elsewhere, instead of taking it as argument,
so callers in common code don't care?

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PING^2][PATCH] in_plt_section: support alternate stub section names
  2013-06-20 16:50     ` [PING^2][PATCH] in_plt_section: support alternate stub section names Pedro Alves
@ 2013-06-21 11:43       ` Maciej W. Rozycki
  2013-06-21 15:34         ` Pedro Alves
  0 siblings, 1 reply; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-06-21 11:43 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Tom Tromey, Richard Sandiford, Catherine Moore, gdb-patches

Pedro,

 Thanks for your review.

> > Index: gdb-fsf-trunk-quilt/gdb/mips-tdep.c
> > ===================================================================
> > --- gdb-fsf-trunk-quilt.orig/gdb/mips-tdep.c	2013-06-19 16:54:49.000000000 +0100
> > +++ gdb-fsf-trunk-quilt/gdb/mips-tdep.c	2013-06-19 16:55:00.280199593 +0100
> > @@ -3628,12 +3628,7 @@ mips_stub_frame_sniffer (const struct fr
> >    if (in_plt_section (pc, NULL))
> >      return 1;
> >  
> > -  /* Binutils for MIPS puts lazy resolution stubs into .MIPS.stubs.  */
> > -  s = find_pc_section (pc);
> > -
> > -  if (s != NULL
> > -      && strcmp (bfd_get_section_name (s->objfile->obfd, s->the_bfd_section),
> > -		 ".MIPS.stubs") == 0)
> > +  if (in_plt_section (pc, ".MIPS.stubs"))
> >      return 1;
> 
> Quite honestly, this looks like an odd API to me.  If all
> MIPS callers will have to pass in ".MIPS.stubs", then it just
> looks like in_plt_section becomes a convenience for "is
> pc in section.

 Well, I have focused here, perhaps mistakenly, on the intended use of the 
call -- to determine whether the PC is in a dynamic function call 
trampoline.  Contrary to the description we currently have at 
in_plt_section, .plt is not -- per SVR4 ABI -- a standard name of the 
trampoline section.  The name (and the presence of any such section in the 
first place) is actually left to processor-specific ABI supplements.

 For many processors .plt has indeed been the choice, but for MIPS .plt 
has only recently been added as an ABI extension.  The original MIPS SVR4 
processor-specific ABI supplement defined no specific section name to be 
used for its .plt equivalent.  I can't easily check what IRIX tools chose 
for this section's name (if anything; in a final executable you can have 
ELF segments whose contents are not mapped to any section).  Binutils 
chose .stubs long ago and more recently switched to .MIPS.stubs.  This may 
well be the same names IRIX used in different versions (compare .reginfo 
vs .MIPS.options standard MIPS SVR4 psABI sections).

 That written...

> It'd make more sense to me to refactor in_plt_section to
> something like this, somewhere:
> 
> int
> pc_in_section (CORE_ADDR pc, const char *name)
> {
>   struct obj_section *s;
>   int retval = 0;
> 
>   s = find_pc_section (pc);
> 
>   retval = (s != NULL
> 	    && s->the_bfd_section->name != NULL
> 	    && strcmp (s->the_bfd_section->name, name) == 0);
>   return (retval);
> }
> 
> And then:
> 
> /* In SVR4, we recognize a trampoline by it's section name.
>    That is, if the pc is in a section named ".plt" then we are in
>    a trampoline.  */
> 
> int
> in_plt_section (CORE_ADDR pc)
> {
>   return pc_in_section (pc, ".plt");
> }
> 
> And then MIPS would have somewhere, mips-tdep.c perhaps,
> something like:
> 
> int
> in_mips_stubs_section (CORE_ADDR pc)
> {
>   return pc_in_section (pc, ".MIPS.stubs");
> }
> 
> Or
> 
> #define MIPS_STUBS_SECTION ".MIPS.stubs"
> pc_in_section (pc, MIPS_STUBS_SECTION);
> 
> As bonus, you end up with just one place that
> can typo the section name.
> 
> Perhaps missed the plan to make in_plt_section fetch the
> section name from elsewhere, instead of taking it as argument,
> so callers in common code don't care?

 ... actually I like your suggestion, especially as it seems pc_in_section 
will have more uses than just to check for .plt or .MIPS.stubs.  What I 
don't like is the extra call nesting for something that is otherwise 
rather a trivial piece.  I'm not that particularly fond of macros either.  
How about this change then?

2013-06-21  Maciej W. Rozycki  <macro@codesourcery.com>

	gdb/
	* objfiles.h (pc_in_section): New prototype.
	(in_plt_section): Remove prototype.
	* objfiles.c (pc_in_section): New function.
	(in_plt_section): Remove function.
	* solib-svr4.h (in_plt_section): New function.
	* aarch64-tdep.c (aarch64_stub_unwind_sniffer): Update
	in_plt_section call accordingly.
	* arm-symbian-tdep.c (arm_symbian_skip_trampoline_code):
	Likewise.
	* arm-tdep.c (arm_stub_unwind_sniffer): Likewise.
	* hppa-linux-tdep.c (hppa_linux_find_global_pointer): Likewise.
	* hppa-tdep.c (hppa_in_solib_call_trampoline): Likewise.
	(hppa_skip_trampoline_code): Likewise.
	* hppabsd-tdep.c (hppabsd_find_global_pointer): Likewise.
	* nios2-tdep.c (nios2_stub_frame_sniffer): Likewise.
	* nto-tdep.c (nto_relocate_section_addresses): Likewise.
	* s390-tdep.c (s390_stub_frame_sniffer): Likewise.
	* sh-tdep.c (sh_stub_unwind_sniffer): Likewise.
	* solib-dsbt.c (dsbt_in_dynsym_resolve_code): Likewise.
	* solib-frv.c (frv_in_dynsym_resolve_code): Likewise.
	* solib-svr4.c (svr4_in_dynsym_resolve_code): Likewise.
	* solib-target.c (solib_target_in_dynsym_resolve_code): Likewise.
	* sparc-tdep.c (sparc_analyze_prologue): Likewise.
	* tic6x-tdep.c (tic6x_stub_unwind_sniffer): Likewise.
	* hppa-hpux-tdep.c (in_opd_section): Remove function.
	(hppa64_hpux_find_global_pointer): Use pc_in_section rather than
	in_opd_section.
	* mips-tdep.h (in_mips_stubs_section): New function.
	* mips-linux-tdep.c (mips_linux_in_dynsym_stub): Call
	in_mips_stubs_section.  Remove unused `name' argument.  Return 1 
	rather than the low 16-bit halfword of any instruction examined.
	(mips_linux_in_dynsym_resolve_code): Update
	mips_linux_in_dynsym_stub call accordingly.
	* mips-tdep.c (mips_stub_frame_sniffer): Use in_mips_stubs_section 
	rather than an equivalent hand-coded sequence.

  Maciej

gdb-mips-in-stubs-section.diff
Index: gdb-fsf-trunk-quilt/gdb/aarch64-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/aarch64-tdep.c	2013-05-30 17:44:44.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/aarch64-tdep.c	2013-06-21 01:10:49.551246100 +0100
@@ -39,6 +39,7 @@
 #include "dwarf2-frame.h"
 #include "gdbtypes.h"
 #include "prologue-value.h"
+#include "solib-svr4.h"
 #include "target-descriptions.h"
 #include "user-regs.h"
 #include "language.h"
@@ -1094,7 +1095,7 @@ aarch64_stub_unwind_sniffer (const struc
   gdb_byte dummy[4];
 
   addr_in_block = get_frame_address_in_block (this_frame);
-  if (in_plt_section (addr_in_block, NULL)
+  if (in_plt_section (addr_in_block)
       /* We also use the stub winder if the target memory is unreadable
 	 to avoid having the prologue unwinder trying to read it.  */
       || target_read_memory (get_frame_pc (this_frame), dummy, 4) != 0)
Index: gdb-fsf-trunk-quilt/gdb/arm-symbian-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/arm-symbian-tdep.c	2013-01-30 22:34:28.000000000 +0000
+++ gdb-fsf-trunk-quilt/gdb/arm-symbian-tdep.c	2013-06-21 01:04:03.021206927 +0100
@@ -22,6 +22,7 @@
 #include "objfiles.h"
 #include "osabi.h"
 #include "solib.h"
+#include "solib-svr4.h"
 #include "solib-target.h"
 #include "target.h"
 #include "elf-bfd.h"
@@ -38,7 +39,7 @@ arm_symbian_skip_trampoline_code (struct
   CORE_ADDR dest;
   gdb_byte buf[4];
 
-  if (!in_plt_section (pc, NULL))
+  if (!in_plt_section (pc))
     return 0;
 
   if (target_read_memory (pc, buf, 4) != 0)
Index: gdb-fsf-trunk-quilt/gdb/arm-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/arm-tdep.c	2013-05-10 17:01:47.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/arm-tdep.c	2013-06-21 01:10:07.041186876 +0100
@@ -41,6 +41,7 @@
 #include "gdbtypes.h"
 #include "prologue-value.h"
 #include "remote.h"
+#include "solib-svr4.h"
 #include "target-descriptions.h"
 #include "user-regs.h"
 #include "observer.h"
@@ -2907,7 +2908,7 @@ arm_stub_unwind_sniffer (const struct fr
   gdb_byte dummy[4];
 
   addr_in_block = get_frame_address_in_block (this_frame);
-  if (in_plt_section (addr_in_block, NULL)
+  if (in_plt_section (addr_in_block)
       /* We also use the stub winder if the target memory is unreadable
 	 to avoid having the prologue unwinder trying to read it.  */
       || target_read_memory (get_frame_pc (this_frame), dummy, 4) != 0)
Index: gdb-fsf-trunk-quilt/gdb/hppa-hpux-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/hppa-hpux-tdep.c	2013-05-10 17:01:47.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/hppa-hpux-tdep.c	2013-06-21 02:02:12.741232587 +0100
@@ -65,20 +65,6 @@
 extern void _initialize_hppa_hpux_tdep (void);
 extern initialize_file_ftype _initialize_hppa_hpux_tdep;
 
-static int
-in_opd_section (CORE_ADDR pc)
-{
-  struct obj_section *s;
-  int retval = 0;
-
-  s = find_pc_section (pc);
-
-  retval = (s != NULL
-	    && s->the_bfd_section->name != NULL
-	    && strcmp (s->the_bfd_section->name, ".opd") == 0);
-  return (retval);
-}
-
 /* Return one if PC is in the call path of a trampoline, else return zero.
 
    Note we return one for *any* call trampoline (long-call, arg-reloc), not
@@ -798,7 +784,7 @@ hppa64_hpux_find_global_pointer (struct 
 
   faddr = value_as_address (function);
 
-  if (in_opd_section (faddr))
+  if (pc_in_section (faddr, ".opd"))
     {
       target_read_memory (faddr, buf, sizeof (buf));
       return extract_unsigned_integer (&buf[24], 8, byte_order);
Index: gdb-fsf-trunk-quilt/gdb/hppa-linux-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/hppa-linux-tdep.c	2013-03-08 11:08:54.000000000 +0000
+++ gdb-fsf-trunk-quilt/gdb/hppa-linux-tdep.c	2013-06-21 00:18:52.351224271 +0100
@@ -356,7 +356,7 @@ hppa_linux_find_global_pointer (struct g
   /* If the address is in the plt section, then the real function hasn't 
      yet been fixed up by the linker so we cannot determine the gp of 
      that function.  */
-  if (in_plt_section (faddr, NULL))
+  if (in_plt_section (faddr))
     return 0;
 
   faddr_sect = find_pc_section (faddr);
Index: gdb-fsf-trunk-quilt/gdb/hppa-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/hppa-tdep.c	2013-05-10 17:01:47.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/hppa-tdep.c	2013-06-21 01:12:08.051202254 +0100
@@ -39,6 +39,7 @@
 #include "gdbcmd.h"
 #include "gdbtypes.h"
 #include "objfiles.h"
+#include "solib-svr4.h"
 #include "hppa-tdep.h"
 
 static int hppa_debug = 0;
@@ -2861,7 +2862,7 @@ hppa_in_solib_call_trampoline (struct gd
   unsigned int insn[HPPA_MAX_INSN_PATTERN_LEN];
   struct unwind_table_entry *u;
 
-  if (in_plt_section (pc, name) || hppa_in_dyncall (pc))
+  if (in_plt_section (pc) || hppa_in_dyncall (pc))
     return 1;
 
   /* The GNU toolchain produces linker stubs without unwind
@@ -2918,13 +2919,13 @@ hppa_skip_trampoline_code (struct frame_
       /* fallthrough */
     }
 
-  if (in_plt_section (pc, NULL))
+  if (in_plt_section (pc))
     {
       pc = read_memory_typed_address (pc, func_ptr_type);
 
       /* If the PLT slot has not yet been resolved, the target will be
          the PLT stub.  */
-      if (in_plt_section (pc, NULL))
+      if (in_plt_section (pc))
 	{
 	  /* Sanity check: are we pointing to the PLT stub?  */
   	  if (!hppa_match_insns (gdbarch, pc, hppa_plt_stub, insn))
Index: gdb-fsf-trunk-quilt/gdb/hppabsd-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/hppabsd-tdep.c	2013-01-30 22:34:28.000000000 +0000
+++ gdb-fsf-trunk-quilt/gdb/hppabsd-tdep.c	2013-06-21 00:19:35.361228186 +0100
@@ -47,7 +47,7 @@ hppabsd_find_global_pointer (struct gdba
   /* If the address is in the .plt section, then the real function
      hasn't yet been fixed up by the linker so we cannot determine the
      Global Pointer for that function.  */
-  if (in_plt_section (faddr, NULL))
+  if (in_plt_section (faddr))
     return 0;
 
   faddr_sec = find_pc_section (faddr);
Index: gdb-fsf-trunk-quilt/gdb/mips-linux-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/mips-linux-tdep.c	2013-06-19 16:54:49.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/mips-linux-tdep.c	2013-06-21 00:19:51.859933668 +0100
@@ -30,6 +30,7 @@
 #include "trad-frame.h"
 #include "tramp-frame.h"
 #include "gdbtypes.h"
+#include "objfiles.h"
 #include "solib.h"
 #include "solib-svr4.h"
 #include "solist.h"
@@ -666,25 +667,34 @@ mips_linux_core_read_description (struct
 
 
 /* Check the code at PC for a dynamic linker lazy resolution stub.
-   Because they aren't in the .plt section, we pattern-match on the
-   code generated by GNU ld.  They look like this:
+   GNU ld for MIPS has put lazy resolution stubs into a ".MIPS.stubs"
+   section uniformly since version 2.15.  If the pc is in that section,
+   then we are in such a stub.  Before that ".stub" was used in 32-bit
+   ELF binaries, however we do not bother checking for that since we
+   have never had and that case should be extremely rare these days.
+   Instead we pattern-match on the code generated by GNU ld.  They look
+   like this:
 
    lw t9,0x8010(gp)
    addu t7,ra
    jalr t9,ra
    addiu t8,zero,INDEX
 
-   (with the appropriate doubleword instructions for N64).  Also
-   return the dynamic symbol index used in the last instruction.  */
+   (with the appropriate doubleword instructions for N64).  As any lazy
+   resolution stubs in microMIPS binaries will always be in a
+   ".MIPS.stubs" section we only ever verify standard MIPS patterns. */
 
 static int
-mips_linux_in_dynsym_stub (CORE_ADDR pc, char *name)
+mips_linux_in_dynsym_stub (CORE_ADDR pc)
 {
   gdb_byte buf[28], *p;
   ULONGEST insn, insn1;
   int n64 = (mips_abi (target_gdbarch ()) == MIPS_ABI_N64);
   enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
 
+  if (in_mips_stubs_section (pc))
+    return 1;
+
   read_memory (pc - 12, buf, 28);
 
   if (n64)
@@ -742,7 +752,7 @@ mips_linux_in_dynsym_stub (CORE_ADDR pc,
 	return 0;
     }
 
-  return (insn & 0xffff);
+  return 1;
 }
 
 /* Return non-zero iff PC belongs to the dynamic linker resolution
@@ -756,9 +766,10 @@ mips_linux_in_dynsym_resolve_code (CORE_
   if (svr4_in_dynsym_resolve_code (pc))
     return 1;
 
-  /* Pattern match for the stub.  It would be nice if there were a
-     more efficient way to avoid this check.  */
-  if (mips_linux_in_dynsym_stub (pc, NULL))
+  /* Likewise for the stubs.  They live in the .MIPS.stubs section these
+     days, so we check if the PC is within, than fall back to a pattern
+     match.  */
+  if (mips_linux_in_dynsym_stub (pc))
     return 1;
 
   return 0;
Index: gdb-fsf-trunk-quilt/gdb/mips-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/mips-tdep.c	2013-06-19 16:54:49.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/mips-tdep.c	2013-06-21 01:13:24.059825608 +0100
@@ -52,6 +52,7 @@
 #include "infcall.h"
 #include "floatformat.h"
 #include "remote.h"
+#include "solib-svr4.h"
 #include "target-descriptions.h"
 #include "dwarf2-frame.h"
 #include "user-regs.h"
@@ -3625,15 +3626,7 @@ mips_stub_frame_sniffer (const struct fr
   if (target_read_memory (get_frame_pc (this_frame), dummy, 4) != 0)
     return 1;
 
-  if (in_plt_section (pc, NULL))
-    return 1;
-
-  /* Binutils for MIPS puts lazy resolution stubs into .MIPS.stubs.  */
-  s = find_pc_section (pc);
-
-  if (s != NULL
-      && strcmp (bfd_get_section_name (s->objfile->obfd, s->the_bfd_section),
-		 ".MIPS.stubs") == 0)
+  if (in_plt_section (pc) || in_mips_stubs_section (pc))
     return 1;
 
   /* Calling a PIC function from a non-PIC function passes through a
Index: gdb-fsf-trunk-quilt/gdb/mips-tdep.h
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/mips-tdep.h	2013-06-19 16:54:49.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/mips-tdep.h	2013-06-21 01:14:02.561228728 +0100
@@ -20,6 +20,8 @@
 #ifndef MIPS_TDEP_H
 #define MIPS_TDEP_H
 
+#include "objfiles.h"
+
 struct gdbarch;
 
 /* All the possible MIPS ABIs.  */
@@ -187,4 +189,11 @@ extern void mips_write_pc (struct regcac
 extern struct target_desc *mips_tdesc_gp32;
 extern struct target_desc *mips_tdesc_gp64;
 
+/* Return non-zero if PC is in the MIPS SVR4 lazy-binding stub section.  */
+static inline int
+in_mips_stubs_section (CORE_ADDR pc)
+{
+  return pc_in_section (pc, ".MIPS.stubs");
+}
+
 #endif /* MIPS_TDEP_H */
Index: gdb-fsf-trunk-quilt/gdb/nios2-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/nios2-tdep.c	2013-05-10 17:01:48.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/nios2-tdep.c	2013-06-21 01:14:22.059908100 +0100
@@ -41,6 +41,7 @@
 #include "gdb_assert.h"
 #include "infcall.h"
 #include "regset.h"
+#include "solib-svr4.h"
 #include "target-descriptions.h"
 
 /* To get entry_point_address.  */
@@ -1324,7 +1325,7 @@ nios2_stub_frame_sniffer (const struct f
   if (target_read_memory (get_frame_pc (this_frame), dummy, 4) != 0)
     return 1;
 
-  if (in_plt_section (pc, NULL))
+  if (in_plt_section (pc))
     return 1;
 
   return 0;
Index: gdb-fsf-trunk-quilt/gdb/nto-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/nto-tdep.c	2013-01-30 22:34:28.000000000 +0000
+++ gdb-fsf-trunk-quilt/gdb/nto-tdep.c	2013-06-21 00:24:19.870386783 +0100
@@ -318,7 +318,7 @@ nto_relocate_section_addresses (struct s
 int
 nto_in_dynsym_resolve_code (CORE_ADDR pc)
 {
-  if (in_plt_section (pc, NULL))
+  if (in_plt_section (pc))
     return 1;
   return 0;
 }
Index: gdb-fsf-trunk-quilt/gdb/objfiles.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/objfiles.c	2013-06-19 16:45:58.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/objfiles.c	2013-06-21 00:25:44.880419344 +0100
@@ -1410,12 +1410,10 @@ find_pc_section (CORE_ADDR pc)
 }
 
 
-/* In SVR4, we recognize a trampoline by it's section name. 
-   That is, if the pc is in a section named ".plt" then we are in
-   a trampoline.  */
+/* Return non-zero if PC is in a section called NAME.  */
 
 int
-in_plt_section (CORE_ADDR pc, char *name)
+pc_in_section (CORE_ADDR pc, char *name)
 {
   struct obj_section *s;
   int retval = 0;
@@ -1424,7 +1422,7 @@ in_plt_section (CORE_ADDR pc, char *name
 
   retval = (s != NULL
 	    && s->the_bfd_section->name != NULL
-	    && strcmp (s->the_bfd_section->name, ".plt") == 0);
+	    && strcmp (s->the_bfd_section->name, name) == 0);
   return (retval);
 }
 \f
Index: gdb-fsf-trunk-quilt/gdb/objfiles.h
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/objfiles.h	2013-06-06 20:41:11.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/objfiles.h	2013-06-21 00:48:49.961188758 +0100
@@ -495,7 +495,8 @@ extern int have_minimal_symbols (void);
 
 extern struct obj_section *find_pc_section (CORE_ADDR pc);
 
-extern int in_plt_section (CORE_ADDR, char *);
+/* Return non-zero if PC is in a section called NAME.  */
+extern int pc_in_section (CORE_ADDR, char *);
 
 /* Keep a registry of per-objfile data-pointers required by other GDB
    modules.  */
Index: gdb-fsf-trunk-quilt/gdb/s390-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/s390-tdep.c	2013-05-10 17:01:48.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/s390-tdep.c	2013-06-21 00:29:09.391203827 +0100
@@ -2116,7 +2116,7 @@ s390_stub_frame_sniffer (const struct fr
      have trapped due to an invalid function pointer call.  We handle
      the non-existing current function like a PLT stub.  */
   addr_in_block = get_frame_address_in_block (this_frame);
-  if (in_plt_section (addr_in_block, NULL)
+  if (in_plt_section (addr_in_block)
       || s390_readinstruction (insn, get_frame_pc (this_frame)) < 0)
     return 1;
   return 0;
Index: gdb-fsf-trunk-quilt/gdb/sh-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/sh-tdep.c	2013-05-10 17:01:47.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/sh-tdep.c	2013-06-21 00:29:15.891186375 +0100
@@ -2036,7 +2036,7 @@ sh_stub_unwind_sniffer (const struct fra
   CORE_ADDR addr_in_block;
 
   addr_in_block = get_frame_address_in_block (this_frame);
-  if (in_plt_section (addr_in_block, NULL))
+  if (in_plt_section (addr_in_block))
     return 1;
 
   return 0;
Index: gdb-fsf-trunk-quilt/gdb/solib-dsbt.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/solib-dsbt.c	2013-05-10 17:01:48.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/solib-dsbt.c	2013-06-21 01:15:01.560542073 +0100
@@ -22,6 +22,7 @@
 #include "inferior.h"
 #include "gdbcore.h"
 #include "solib.h"
+#include "solib-svr4.h"
 #include "solist.h"
 #include "objfiles.h"
 #include "symtab.h"
@@ -764,7 +765,7 @@ dsbt_in_dynsym_resolve_code (CORE_ADDR p
 
   return ((pc >= info->interp_text_sect_low && pc < info->interp_text_sect_high)
 	  || (pc >= info->interp_plt_sect_low && pc < info->interp_plt_sect_high)
-	  || in_plt_section (pc, NULL));
+	  || in_plt_section (pc));
 }
 
 /* Print a warning about being unable to set the dynamic linker
Index: gdb-fsf-trunk-quilt/gdb/solib-frv.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/solib-frv.c	2013-05-10 17:01:48.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/solib-frv.c	2013-06-21 01:15:12.561201093 +0100
@@ -22,6 +22,7 @@
 #include "inferior.h"
 #include "gdbcore.h"
 #include "solib.h"
+#include "solib-svr4.h"
 #include "solist.h"
 #include "frv-tdep.h"
 #include "objfiles.h"
@@ -448,7 +449,7 @@ frv_in_dynsym_resolve_code (CORE_ADDR pc
 {
   return ((pc >= interp_text_sect_low && pc < interp_text_sect_high)
 	  || (pc >= interp_plt_sect_low && pc < interp_plt_sect_high)
-	  || in_plt_section (pc, NULL));
+	  || in_plt_section (pc));
 }
 
 /* Given a loadmap and an address, return the displacement needed
Index: gdb-fsf-trunk-quilt/gdb/solib-svr4.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/solib-svr4.c	2013-06-06 20:41:11.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/solib-svr4.c	2013-06-21 00:30:09.891212016 +0100
@@ -1532,7 +1532,7 @@ svr4_in_dynsym_resolve_code (CORE_ADDR p
 	   && pc < info->interp_text_sect_high)
 	  || (pc >= info->interp_plt_sect_low
 	      && pc < info->interp_plt_sect_high)
-	  || in_plt_section (pc, NULL)
+	  || in_plt_section (pc)
 	  || in_gnu_ifunc_stub (pc));
 }
 
Index: gdb-fsf-trunk-quilt/gdb/solib-svr4.h
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/solib-svr4.h	2013-01-30 22:34:28.000000000 +0000
+++ gdb-fsf-trunk-quilt/gdb/solib-svr4.h	2013-06-21 00:56:26.991137107 +0100
@@ -20,7 +20,8 @@
 #ifndef SOLIB_SVR4_H
 #define SOLIB_SVR4_H
 
-struct objfile;
+#include "objfiles.h"
+
 struct target_so_ops;
 
 extern struct target_so_ops svr4_so_ops;
@@ -84,4 +85,11 @@ extern struct link_map_offsets *svr4_lp6
    SVR4 run time loader.  */
 int svr4_in_dynsym_resolve_code (CORE_ADDR pc);
 
+/* Return non-zero if PC is in the SVR4 procedure linkage table section.  */
+static inline int
+in_plt_section (CORE_ADDR pc)
+{
+  return pc_in_section (pc, ".plt");
+}
+
 #endif /* solib-svr4.h */
Index: gdb-fsf-trunk-quilt/gdb/solib-target.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/solib-target.c	2013-05-10 17:01:48.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/solib-target.c	2013-06-21 01:15:38.571204241 +0100
@@ -24,6 +24,7 @@
 #include "symfile.h"
 #include "target.h"
 #include "vec.h"
+#include "solib-svr4.h"
 #include "solib-target.h"
 
 #include "gdb_string.h"
@@ -476,7 +477,7 @@ solib_target_in_dynsym_resolve_code (COR
   /* We don't have a range of addresses for the dynamic linker; there
      may not be one in the program's address space.  So only report
      PLT entries (which may be import stubs).  */
-  return in_plt_section (pc, NULL);
+  return in_plt_section (pc);
 }
 
 struct target_so_ops solib_target_so_ops;
Index: gdb-fsf-trunk-quilt/gdb/sparc-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/sparc-tdep.c	2013-02-17 03:31:06.000000000 +0000
+++ gdb-fsf-trunk-quilt/gdb/sparc-tdep.c	2013-06-21 01:15:58.571226323 +0100
@@ -32,6 +32,7 @@
 #include "objfiles.h"
 #include "osabi.h"
 #include "regcache.h"
+#include "solib-svr4.h"
 #include "target.h"
 #include "value.h"
 
@@ -855,7 +856,7 @@ sparc_analyze_prologue (struct gdbarch *
      dynamic linker patches up the first PLT with some code that
      starts with a SAVE instruction.  Patch up PC such that it points
      at the start of our PLT entry.  */
-  if (tdep->plt_entry_size > 0 && in_plt_section (current_pc, NULL))
+  if (tdep->plt_entry_size > 0 && in_plt_section (current_pc))
     pc = current_pc - ((current_pc - pc) % tdep->plt_entry_size);
 
   insn = sparc_fetch_instruction (pc);
Index: gdb-fsf-trunk-quilt/gdb/tic6x-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/tic6x-tdep.c	2013-05-10 17:01:47.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/tic6x-tdep.c	2013-06-21 01:16:10.571208681 +0100
@@ -43,6 +43,7 @@
 #include "tramp-frame.h"
 #include "linux-tdep.h"
 #include "solib.h"
+#include "solib-svr4.h"
 #include "objfiles.h"
 #include "gdb_assert.h"
 #include "osabi.h"
@@ -530,7 +531,7 @@ tic6x_stub_unwind_sniffer (const struct 
   CORE_ADDR addr_in_block;
 
   addr_in_block = get_frame_address_in_block (this_frame);
-  if (in_plt_section (addr_in_block, NULL))
+  if (in_plt_section (addr_in_block))
     return 1;
 
   return 0;


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PING^2][PATCH] in_plt_section: support alternate stub section names
  2013-06-21 11:43       ` Maciej W. Rozycki
@ 2013-06-21 15:34         ` Pedro Alves
  2013-06-22  2:24           ` Maciej W. Rozycki
  0 siblings, 1 reply; 28+ messages in thread
From: Pedro Alves @ 2013-06-21 15:34 UTC (permalink / raw)
  To: Maciej W. Rozycki
  Cc: Tom Tromey, Richard Sandiford, Catherine Moore, gdb-patches

On 06/21/2013 12:42 PM, Maciej W. Rozycki wrote:

>  Well, I have focused here, perhaps mistakenly, on the intended use of the 
> call -- to determine whether the PC is in a dynamic function call 
> trampoline.  Contrary to the description we currently have at 
> in_plt_section, .plt is not -- per SVR4 ABI -- a standard name of the 
> trampoline section.  The name (and the presence of any such section in the 
> first place) is actually left to processor-specific ABI supplements.
> 
>  For many processors .plt has indeed been the choice, but for MIPS .plt 
> has only recently been added as an ABI extension.  The original MIPS SVR4 
> processor-specific ABI supplement defined no specific section name to be 
> used for its .plt equivalent.  I can't easily check what IRIX tools chose 
> for this section's name (if anything; in a final executable you can have 
> ELF segments whose contents are not mapped to any section).  Binutils 
> chose .stubs long ago and more recently switched to .MIPS.stubs.  This may 
> well be the same names IRIX used in different versions (compare .reginfo 
> vs .MIPS.options standard MIPS SVR4 psABI sections).

Right, but extending in_plt_section torwards a general "in trampoline
section" would imply to me hiding platform details underneath, through
e.g., recording the section name in gdbarch, as calls in common
code wouldn't known about such target details.

> 
>  That written...

...

>  ... actually I like your suggestion, especially as it seems pc_in_section 
> will have more uses than just to check for .plt or .MIPS.stubs.

Exactly.  Excellent.

> What I don't like is the extra call nesting for something that is otherwise 
> rather a trivial piece.  I'm not that particularly fond of macros either.  
> How about this change then?

static inline is fine with me.  However, what I really dislike is the
inclusion of solib-svr4.h in parts of the debugger that have nothing
to do with SVR4, or even are implementing a different shared library
support backend, like solib-frv.c solib-dsbt.c, solib-target.c, etc.
That's really an abstraction violation, there bits have nothing to so
with SVR4.

Clearly PLTs exist in the .plt section on multiple solib implementations,
so I'd rather we keep in_plt_section somewhere central (leaving it in
objfiles.h like where it is today is super fine with me).

> +/* Return non-zero if PC is in the SVR4 procedure linkage table section.  */
> +static inline int
> +in_plt_section (CORE_ADDR pc)
> +{
> +  return pc_in_section (pc, ".plt");
> +}

... and we can then just drop "SVR4" from its describing comment.  Even
Symbian has these.

>  
> +/* Return non-zero if PC is in the MIPS SVR4 lazy-binding stub section.  */
> +static inline int
> +in_mips_stubs_section (CORE_ADDR pc)

GDB coding standards require an empty line between describing comment
and function definition.  (There's at least one more instance in
the patch.)

> +{
> +  return pc_in_section (pc, ".MIPS.stubs");
> +}
> +

 \f
> Index: gdb-fsf-trunk-quilt/gdb/objfiles.h
> ===================================================================
> --- gdb-fsf-trunk-quilt.orig/gdb/objfiles.h	2013-06-06 20:41:11.000000000 +0100
> +++ gdb-fsf-trunk-quilt/gdb/objfiles.h	2013-06-21 00:48:49.961188758 +0100
> @@ -495,7 +495,8 @@ extern int have_minimal_symbols (void);
>  
>  extern struct obj_section *find_pc_section (CORE_ADDR pc);
>  
> -extern int in_plt_section (CORE_ADDR, char *);
> +/* Return non-zero if PC is in a section called NAME.  */
> +extern int pc_in_section (CORE_ADDR, char *);

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PING^2][PATCH] in_plt_section: support alternate stub section names
  2013-06-21 15:34         ` Pedro Alves
@ 2013-06-22  2:24           ` Maciej W. Rozycki
  2013-06-24 12:40             ` Pedro Alves
  0 siblings, 1 reply; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-06-22  2:24 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Tom Tromey, Richard Sandiford, Catherine Moore, gdb-patches

On Fri, 21 Jun 2013, Pedro Alves wrote:

> >  Well, I have focused here, perhaps mistakenly, on the intended use of the 
> > call -- to determine whether the PC is in a dynamic function call 
> > trampoline.  Contrary to the description we currently have at 
> > in_plt_section, .plt is not -- per SVR4 ABI -- a standard name of the 
> > trampoline section.  The name (and the presence of any such section in the 
> > first place) is actually left to processor-specific ABI supplements.
> > 
> >  For many processors .plt has indeed been the choice, but for MIPS .plt 
> > has only recently been added as an ABI extension.  The original MIPS SVR4 
> > processor-specific ABI supplement defined no specific section name to be 
> > used for its .plt equivalent.  I can't easily check what IRIX tools chose 
> > for this section's name (if anything; in a final executable you can have 
> > ELF segments whose contents are not mapped to any section).  Binutils 
> > chose .stubs long ago and more recently switched to .MIPS.stubs.  This may 
> > well be the same names IRIX used in different versions (compare .reginfo 
> > vs .MIPS.options standard MIPS SVR4 psABI sections).
> 
> Right, but extending in_plt_section torwards a general "in trampoline
> section" would imply to me hiding platform details underneath, through
> e.g., recording the section name in gdbarch, as calls in common
> code wouldn't known about such target details.

 FWIW, this has been the original design of the internal API considered 
here, that I excavated and quoted for the purpose of the original 
submission:

http://sourceware.org/ml/gdb-patches/2013-06/msg00150.html

Not that we should repeat or maintain old mistakes, that is.

> > What I don't like is the extra call nesting for something that is otherwise 
> > rather a trivial piece.  I'm not that particularly fond of macros either.  
> > How about this change then?
> 
> static inline is fine with me.  However, what I really dislike is the
> inclusion of solib-svr4.h in parts of the debugger that have nothing
> to do with SVR4, or even are implementing a different shared library
> support backend, like solib-frv.c solib-dsbt.c, solib-target.c, etc.
> That's really an abstraction violation, there bits have nothing to so
> with SVR4.

 Umm, perhaps we have a file naming confusion in our sources or I am 
missing something.  The thing is all of the ELF stuff and its shared 
library features such as the PLT are inherently SVR4 concepts.  Now there 
are I think two possible options -- either our solib-svr4.h stuff is not 
entirely SVR4 and includes things beyond or we support some pre-SVR4 
systems (SunOS perhaps?) that already included early ELF support.  The 
latter unfortunately I cannot comment on as I lack background information 
here.  So what's the story behind it?

> Clearly PLTs exist in the .plt section on multiple solib implementations,
> so I'd rather we keep in_plt_section somewhere central (leaving it in
> objfiles.h like where it is today is super fine with me).

 That was actually what I implemented first before I realised this place 
is too general for this stuff.  While I believe all the binary formats we 
support have the concept of sections, PLT is mostly if not exclusively ELF 
(a.out shared libraries do not have it for sure; I can't comment on ECOFF 
or XCOFF, but even if they have some PLT equivalent, its section name if 
any is unlikely to be .plt).

 That written, given that this patch is blocking a cascade of changes 
finding the exactly right location for in_plt_section seems secondary to 
me and the objfiles module is where it already resides.  Moving it to 
objfiles.h as a static inline function will already be some progress as it 
won't be compiled in cases where it's not actually used (e.g. plain COFF).

 So here's a version you've suggested.  Even with this change applied we 
can continue looking for a better place for in_plt_section.

> > +/* Return non-zero if PC is in the SVR4 procedure linkage table section.  */
> > +static inline int
> > +in_plt_section (CORE_ADDR pc)
> > +{
> > +  return pc_in_section (pc, ".plt");
> > +}
> 
> ... and we can then just drop "SVR4" from its describing comment.  Even
> Symbian has these.

 SVR4-style perhaps then?  I don't know how Symbian has been implemented 
and how close to or far from a typical SVR4 system it is.

> > +/* Return non-zero if PC is in the MIPS SVR4 lazy-binding stub section.  */
> > +static inline int
> > +in_mips_stubs_section (CORE_ADDR pc)
> 
> GDB coding standards require an empty line between describing comment
> and function definition.  (There's at least one more instance in
> the patch.)

 That seems somewhat inconsistent to me where prototypes are intermixed in 
a header with implementations, but I won't argue. ;)

 While making this update I have noticed the NAME argument to 
hppa_in_solib_call_trampoline can be removed.  This argument used to be 
unused already and with the change to in_plt_section being considered here 
it's even more hopelessly useless (see the lone ultimate call site in 
hppa_stub_unwind_sniffer).

 OK to apply?

2013-06-21  Maciej W. Rozycki  <macro@codesourcery.com>

	gdb/
	* objfiles.h (pc_in_section): New prototype.
	(in_plt_section): Remove name argument, replace prototype with
	static inline function.
	* mips-tdep.h (in_mips_stubs_section): New function.
	* hppa-tdep.h (gdbarch_tdep): Remove name argument of
	in_solib_call_trampoline member.
	(hppa_in_solib_call_trampoline): Remove name argument.
	* objfiles.c (pc_in_section): New function.
	(in_plt_section): Remove function.
	* mips-linux-tdep.c (mips_linux_in_dynsym_stub): Call
	in_mips_stubs_section.  Remove name argument.  Return 1 rather 
	than the low 16-bit halfword of any instruction examined.
	(mips_linux_in_dynsym_resolve_code): Update
	mips_linux_in_dynsym_stub call accordingly.
	* mips-tdep.c (mips_stub_frame_sniffer): Use in_mips_stubs_section 
	rather than an equivalent hand-coded sequence.
	* hppa-hpux-tdep.c (in_opd_section): Remove function.
	(hppa32_hpux_in_solib_call_trampoline): Remove name argument.
	(hppa64_hpux_in_solib_call_trampoline): Likewise.
	(hppa64_hpux_find_global_pointer): Use pc_in_section rather than
	in_opd_section.
	* hppa-tdep.c (hppa_stub_unwind_sniffer): Remove name argument
	on call to tdep->in_solib_call_trampoline.
	(hppa_in_solib_call_trampoline): Remove name argument, update
	according to in_plt_section change.
	(hppa_skip_trampoline_code): Update according to in_plt_section
	change.
	* aarch64-tdep.c (aarch64_stub_unwind_sniffer): Likewise.
	* arm-symbian-tdep.c (arm_symbian_skip_trampoline_code):
	Likewise.
	* arm-tdep.c (arm_stub_unwind_sniffer): Likewise.
	* hppa-linux-tdep.c (hppa_linux_find_global_pointer): Likewise.
	* hppabsd-tdep.c (hppabsd_find_global_pointer): Likewise.
	* nios2-tdep.c (nios2_stub_frame_sniffer): Likewise.
	* nto-tdep.c (nto_relocate_section_addresses): Likewise.
	* s390-tdep.c (s390_stub_frame_sniffer): Likewise.
	* sh-tdep.c (sh_stub_unwind_sniffer): Likewise.
	* solib-dsbt.c (dsbt_in_dynsym_resolve_code): Likewise.
	* solib-frv.c (frv_in_dynsym_resolve_code): Likewise.
	* solib-svr4.c (svr4_in_dynsym_resolve_code): Likewise.
	* solib-target.c (solib_target_in_dynsym_resolve_code): Likewise.
	* sparc-tdep.c (sparc_analyze_prologue): Likewise.
	* tic6x-tdep.c (tic6x_stub_unwind_sniffer): Likewise.

  Maciej

gdb-mips-in-stubs-section.diff
Index: gdb-fsf-trunk-quilt/gdb/aarch64-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/aarch64-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/aarch64-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -1094,7 +1094,7 @@ aarch64_stub_unwind_sniffer (const struc
   gdb_byte dummy[4];
 
   addr_in_block = get_frame_address_in_block (this_frame);
-  if (in_plt_section (addr_in_block, NULL)
+  if (in_plt_section (addr_in_block)
       /* We also use the stub winder if the target memory is unreadable
 	 to avoid having the prologue unwinder trying to read it.  */
       || target_read_memory (get_frame_pc (this_frame), dummy, 4) != 0)
Index: gdb-fsf-trunk-quilt/gdb/arm-symbian-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/arm-symbian-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/arm-symbian-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -38,7 +38,7 @@ arm_symbian_skip_trampoline_code (struct
   CORE_ADDR dest;
   gdb_byte buf[4];
 
-  if (!in_plt_section (pc, NULL))
+  if (!in_plt_section (pc))
     return 0;
 
   if (target_read_memory (pc, buf, 4) != 0)
Index: gdb-fsf-trunk-quilt/gdb/arm-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/arm-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/arm-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -2907,7 +2907,7 @@ arm_stub_unwind_sniffer (const struct fr
   gdb_byte dummy[4];
 
   addr_in_block = get_frame_address_in_block (this_frame);
-  if (in_plt_section (addr_in_block, NULL)
+  if (in_plt_section (addr_in_block)
       /* We also use the stub winder if the target memory is unreadable
 	 to avoid having the prologue unwinder trying to read it.  */
       || target_read_memory (get_frame_pc (this_frame), dummy, 4) != 0)
Index: gdb-fsf-trunk-quilt/gdb/hppa-hpux-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/hppa-hpux-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/hppa-hpux-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -65,28 +65,13 @@
 extern void _initialize_hppa_hpux_tdep (void);
 extern initialize_file_ftype _initialize_hppa_hpux_tdep;
 
-static int
-in_opd_section (CORE_ADDR pc)
-{
-  struct obj_section *s;
-  int retval = 0;
-
-  s = find_pc_section (pc);
-
-  retval = (s != NULL
-	    && s->the_bfd_section->name != NULL
-	    && strcmp (s->the_bfd_section->name, ".opd") == 0);
-  return (retval);
-}
-
 /* Return one if PC is in the call path of a trampoline, else return zero.
 
    Note we return one for *any* call trampoline (long-call, arg-reloc), not
    just shared library trampolines (import, export).  */
 
 static int
-hppa32_hpux_in_solib_call_trampoline (struct gdbarch *gdbarch,
-				      CORE_ADDR pc, char *name)
+hppa32_hpux_in_solib_call_trampoline (struct gdbarch *gdbarch, CORE_ADDR pc)
 {
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
   struct bound_minimal_symbol minsym;
@@ -156,8 +141,7 @@ hppa32_hpux_in_solib_call_trampoline (st
 }
 
 static int
-hppa64_hpux_in_solib_call_trampoline (struct gdbarch *gdbarch,
-				      CORE_ADDR pc, char *name)
+hppa64_hpux_in_solib_call_trampoline (struct gdbarch *gdbarch, CORE_ADDR pc)
 {
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
 
@@ -798,7 +782,7 @@ hppa64_hpux_find_global_pointer (struct 
 
   faddr = value_as_address (function);
 
-  if (in_opd_section (faddr))
+  if (pc_in_section (faddr, ".opd"))
     {
       target_read_memory (faddr, buf, sizeof (buf));
       return extract_unsigned_integer (&buf[24], 8, byte_order);
Index: gdb-fsf-trunk-quilt/gdb/hppa-linux-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/hppa-linux-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/hppa-linux-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -356,7 +356,7 @@ hppa_linux_find_global_pointer (struct g
   /* If the address is in the plt section, then the real function hasn't 
      yet been fixed up by the linker so we cannot determine the gp of 
      that function.  */
-  if (in_plt_section (faddr, NULL))
+  if (in_plt_section (faddr))
     return 0;
 
   faddr_sect = find_pc_section (faddr);
Index: gdb-fsf-trunk-quilt/gdb/hppa-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/hppa-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/hppa-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -2417,7 +2417,7 @@ hppa_stub_unwind_sniffer (const struct f
 
   if (pc == 0
       || (tdep->in_solib_call_trampoline != NULL
-	  && tdep->in_solib_call_trampoline (gdbarch, pc, NULL))
+	  && tdep->in_solib_call_trampoline (gdbarch, pc))
       || gdbarch_in_solib_return_trampoline (gdbarch, pc, NULL))
     return 1;
   return 0;
@@ -2855,13 +2855,12 @@ hppa_in_dyncall (CORE_ADDR pc)
 }
 
 int
-hppa_in_solib_call_trampoline (struct gdbarch *gdbarch,
-			       CORE_ADDR pc, char *name)
+hppa_in_solib_call_trampoline (struct gdbarch *gdbarch, CORE_ADDR pc)
 {
   unsigned int insn[HPPA_MAX_INSN_PATTERN_LEN];
   struct unwind_table_entry *u;
 
-  if (in_plt_section (pc, name) || hppa_in_dyncall (pc))
+  if (in_plt_section (pc) || hppa_in_dyncall (pc))
     return 1;
 
   /* The GNU toolchain produces linker stubs without unwind
@@ -2918,13 +2917,13 @@ hppa_skip_trampoline_code (struct frame_
       /* fallthrough */
     }
 
-  if (in_plt_section (pc, NULL))
+  if (in_plt_section (pc))
     {
       pc = read_memory_typed_address (pc, func_ptr_type);
 
       /* If the PLT slot has not yet been resolved, the target will be
          the PLT stub.  */
-      if (in_plt_section (pc, NULL))
+      if (in_plt_section (pc))
 	{
 	  /* Sanity check: are we pointing to the PLT stub?  */
   	  if (!hppa_match_insns (gdbarch, pc, hppa_plt_stub, insn))
Index: gdb-fsf-trunk-quilt/gdb/hppa-tdep.h
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/hppa-tdep.h	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/hppa-tdep.h	2013-06-21 21:35:57.141226418 +0100
@@ -90,11 +90,9 @@ struct gdbarch_tdep
   CORE_ADDR (*find_global_pointer) (struct gdbarch *, struct value *);
 
   /* For shared libraries, each call goes through a small piece of
-     trampoline code in the ".plt", or equivalent, section.
-     IN_SOLIB_CALL_TRAMPOLINE evaluates to nonzero if we are currently
-     stopped in one of these.  */
-  int (*in_solib_call_trampoline) (struct gdbarch *gdbarch,
-				   CORE_ADDR pc, char *name);
+     trampoline code in the ".plt" section.  IN_SOLIB_CALL_TRAMPOLINE
+     evaluates to nonzero if we are currently stopped in one of these.  */
+  int (*in_solib_call_trampoline) (struct gdbarch *gdbarch, CORE_ADDR pc);
 
   /* For targets that support multiple spaces, we may have additional stubs
      in the return path.  These stubs are internal to the ABI, and users are
@@ -242,7 +240,7 @@ extern struct minimal_symbol *
 extern struct hppa_objfile_private *hppa_init_objfile_priv_data (struct objfile *objfile);
 
 extern int hppa_in_solib_call_trampoline (struct gdbarch *gdbarch,
-					  CORE_ADDR pc, char *name);
+					  CORE_ADDR pc);
 extern CORE_ADDR hppa_skip_trampoline_code (struct frame_info *, CORE_ADDR pc);
 
 #endif  /* hppa-tdep.h */
Index: gdb-fsf-trunk-quilt/gdb/hppabsd-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/hppabsd-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/hppabsd-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -47,7 +47,7 @@ hppabsd_find_global_pointer (struct gdba
   /* If the address is in the .plt section, then the real function
      hasn't yet been fixed up by the linker so we cannot determine the
      Global Pointer for that function.  */
-  if (in_plt_section (faddr, NULL))
+  if (in_plt_section (faddr))
     return 0;
 
   faddr_sec = find_pc_section (faddr);
Index: gdb-fsf-trunk-quilt/gdb/mips-linux-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/mips-linux-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/mips-linux-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -30,6 +30,7 @@
 #include "trad-frame.h"
 #include "tramp-frame.h"
 #include "gdbtypes.h"
+#include "objfiles.h"
 #include "solib.h"
 #include "solib-svr4.h"
 #include "solist.h"
@@ -666,25 +667,34 @@ mips_linux_core_read_description (struct
 
 
 /* Check the code at PC for a dynamic linker lazy resolution stub.
-   Because they aren't in the .plt section, we pattern-match on the
-   code generated by GNU ld.  They look like this:
+   GNU ld for MIPS has put lazy resolution stubs into a ".MIPS.stubs"
+   section uniformly since version 2.15.  If the pc is in that section,
+   then we are in such a stub.  Before that ".stub" was used in 32-bit
+   ELF binaries, however we do not bother checking for that since we
+   have never had and that case should be extremely rare these days.
+   Instead we pattern-match on the code generated by GNU ld.  They look
+   like this:
 
    lw t9,0x8010(gp)
    addu t7,ra
    jalr t9,ra
    addiu t8,zero,INDEX
 
-   (with the appropriate doubleword instructions for N64).  Also
-   return the dynamic symbol index used in the last instruction.  */
+   (with the appropriate doubleword instructions for N64).  As any lazy
+   resolution stubs in microMIPS binaries will always be in a
+   ".MIPS.stubs" section we only ever verify standard MIPS patterns. */
 
 static int
-mips_linux_in_dynsym_stub (CORE_ADDR pc, char *name)
+mips_linux_in_dynsym_stub (CORE_ADDR pc)
 {
   gdb_byte buf[28], *p;
   ULONGEST insn, insn1;
   int n64 = (mips_abi (target_gdbarch ()) == MIPS_ABI_N64);
   enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
 
+  if (in_mips_stubs_section (pc))
+    return 1;
+
   read_memory (pc - 12, buf, 28);
 
   if (n64)
@@ -742,7 +752,7 @@ mips_linux_in_dynsym_stub (CORE_ADDR pc,
 	return 0;
     }
 
-  return (insn & 0xffff);
+  return 1;
 }
 
 /* Return non-zero iff PC belongs to the dynamic linker resolution
@@ -756,9 +766,10 @@ mips_linux_in_dynsym_resolve_code (CORE_
   if (svr4_in_dynsym_resolve_code (pc))
     return 1;
 
-  /* Pattern match for the stub.  It would be nice if there were a
-     more efficient way to avoid this check.  */
-  if (mips_linux_in_dynsym_stub (pc, NULL))
+  /* Likewise for the stubs.  They live in the .MIPS.stubs section these
+     days, so we check if the PC is within, than fall back to a pattern
+     match.  */
+  if (mips_linux_in_dynsym_stub (pc))
     return 1;
 
   return 0;
Index: gdb-fsf-trunk-quilt/gdb/mips-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/mips-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/mips-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -3625,15 +3625,7 @@ mips_stub_frame_sniffer (const struct fr
   if (target_read_memory (get_frame_pc (this_frame), dummy, 4) != 0)
     return 1;
 
-  if (in_plt_section (pc, NULL))
-    return 1;
-
-  /* Binutils for MIPS puts lazy resolution stubs into .MIPS.stubs.  */
-  s = find_pc_section (pc);
-
-  if (s != NULL
-      && strcmp (bfd_get_section_name (s->objfile->obfd, s->the_bfd_section),
-		 ".MIPS.stubs") == 0)
+  if (in_plt_section (pc) || in_mips_stubs_section (pc))
     return 1;
 
   /* Calling a PIC function from a non-PIC function passes through a
Index: gdb-fsf-trunk-quilt/gdb/mips-tdep.h
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/mips-tdep.h	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/mips-tdep.h	2013-06-22 00:22:17.770414915 +0100
@@ -20,6 +20,8 @@
 #ifndef MIPS_TDEP_H
 #define MIPS_TDEP_H
 
+#include "objfiles.h"
+
 struct gdbarch;
 
 /* All the possible MIPS ABIs.  */
@@ -187,4 +189,12 @@ extern void mips_write_pc (struct regcac
 extern struct target_desc *mips_tdesc_gp32;
 extern struct target_desc *mips_tdesc_gp64;
 
+/* Return non-zero if PC is in a MIPS SVR4 lazy-binding stub section.  */
+
+static inline int
+in_mips_stubs_section (CORE_ADDR pc)
+{
+  return pc_in_section (pc, ".MIPS.stubs");
+}
+
 #endif /* MIPS_TDEP_H */
Index: gdb-fsf-trunk-quilt/gdb/nios2-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/nios2-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/nios2-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -1324,7 +1324,7 @@ nios2_stub_frame_sniffer (const struct f
   if (target_read_memory (get_frame_pc (this_frame), dummy, 4) != 0)
     return 1;
 
-  if (in_plt_section (pc, NULL))
+  if (in_plt_section (pc))
     return 1;
 
   return 0;
Index: gdb-fsf-trunk-quilt/gdb/nto-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/nto-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/nto-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -318,7 +318,7 @@ nto_relocate_section_addresses (struct s
 int
 nto_in_dynsym_resolve_code (CORE_ADDR pc)
 {
-  if (in_plt_section (pc, NULL))
+  if (in_plt_section (pc))
     return 1;
   return 0;
 }
Index: gdb-fsf-trunk-quilt/gdb/objfiles.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/objfiles.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/objfiles.c	2013-06-21 21:35:57.141226418 +0100
@@ -1410,12 +1410,10 @@ find_pc_section (CORE_ADDR pc)
 }
 
 
-/* In SVR4, we recognize a trampoline by it's section name. 
-   That is, if the pc is in a section named ".plt" then we are in
-   a trampoline.  */
+/* Return non-zero if PC is in a section called NAME.  */
 
 int
-in_plt_section (CORE_ADDR pc, char *name)
+pc_in_section (CORE_ADDR pc, char *name)
 {
   struct obj_section *s;
   int retval = 0;
@@ -1424,7 +1422,7 @@ in_plt_section (CORE_ADDR pc, char *name
 
   retval = (s != NULL
 	    && s->the_bfd_section->name != NULL
-	    && strcmp (s->the_bfd_section->name, ".plt") == 0);
+	    && strcmp (s->the_bfd_section->name, name) == 0);
   return (retval);
 }
 \f
Index: gdb-fsf-trunk-quilt/gdb/objfiles.h
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/objfiles.h	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/objfiles.h	2013-06-22 00:20:15.760414815 +0100
@@ -495,7 +495,17 @@ extern int have_minimal_symbols (void);
 
 extern struct obj_section *find_pc_section (CORE_ADDR pc);
 
-extern int in_plt_section (CORE_ADDR, char *);
+/* Return non-zero if PC is in a section called NAME.  */
+extern int pc_in_section (CORE_ADDR, char *);
+
+/* Return non-zero if PC is in a SVR4-style procedure linkage table
+   section.  */
+
+static inline int
+in_plt_section (CORE_ADDR pc)
+{
+  return pc_in_section (pc, ".plt");
+}
 
 /* Keep a registry of per-objfile data-pointers required by other GDB
    modules.  */
Index: gdb-fsf-trunk-quilt/gdb/s390-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/s390-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/s390-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -2116,7 +2116,7 @@ s390_stub_frame_sniffer (const struct fr
      have trapped due to an invalid function pointer call.  We handle
      the non-existing current function like a PLT stub.  */
   addr_in_block = get_frame_address_in_block (this_frame);
-  if (in_plt_section (addr_in_block, NULL)
+  if (in_plt_section (addr_in_block)
       || s390_readinstruction (insn, get_frame_pc (this_frame)) < 0)
     return 1;
   return 0;
Index: gdb-fsf-trunk-quilt/gdb/sh-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/sh-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/sh-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -2036,7 +2036,7 @@ sh_stub_unwind_sniffer (const struct fra
   CORE_ADDR addr_in_block;
 
   addr_in_block = get_frame_address_in_block (this_frame);
-  if (in_plt_section (addr_in_block, NULL))
+  if (in_plt_section (addr_in_block))
     return 1;
 
   return 0;
Index: gdb-fsf-trunk-quilt/gdb/solib-dsbt.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/solib-dsbt.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/solib-dsbt.c	2013-06-21 21:35:57.141226418 +0100
@@ -764,7 +764,7 @@ dsbt_in_dynsym_resolve_code (CORE_ADDR p
 
   return ((pc >= info->interp_text_sect_low && pc < info->interp_text_sect_high)
 	  || (pc >= info->interp_plt_sect_low && pc < info->interp_plt_sect_high)
-	  || in_plt_section (pc, NULL));
+	  || in_plt_section (pc));
 }
 
 /* Print a warning about being unable to set the dynamic linker
Index: gdb-fsf-trunk-quilt/gdb/solib-frv.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/solib-frv.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/solib-frv.c	2013-06-21 21:35:57.141226418 +0100
@@ -448,7 +448,7 @@ frv_in_dynsym_resolve_code (CORE_ADDR pc
 {
   return ((pc >= interp_text_sect_low && pc < interp_text_sect_high)
 	  || (pc >= interp_plt_sect_low && pc < interp_plt_sect_high)
-	  || in_plt_section (pc, NULL));
+	  || in_plt_section (pc));
 }
 
 /* Given a loadmap and an address, return the displacement needed
Index: gdb-fsf-trunk-quilt/gdb/solib-svr4.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/solib-svr4.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/solib-svr4.c	2013-06-21 21:35:57.141226418 +0100
@@ -1532,7 +1532,7 @@ svr4_in_dynsym_resolve_code (CORE_ADDR p
 	   && pc < info->interp_text_sect_high)
 	  || (pc >= info->interp_plt_sect_low
 	      && pc < info->interp_plt_sect_high)
-	  || in_plt_section (pc, NULL)
+	  || in_plt_section (pc)
 	  || in_gnu_ifunc_stub (pc));
 }
 
Index: gdb-fsf-trunk-quilt/gdb/solib-target.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/solib-target.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/solib-target.c	2013-06-21 21:35:57.141226418 +0100
@@ -476,7 +476,7 @@ solib_target_in_dynsym_resolve_code (COR
   /* We don't have a range of addresses for the dynamic linker; there
      may not be one in the program's address space.  So only report
      PLT entries (which may be import stubs).  */
-  return in_plt_section (pc, NULL);
+  return in_plt_section (pc);
 }
 
 struct target_so_ops solib_target_so_ops;
Index: gdb-fsf-trunk-quilt/gdb/sparc-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/sparc-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/sparc-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -855,7 +855,7 @@ sparc_analyze_prologue (struct gdbarch *
      dynamic linker patches up the first PLT with some code that
      starts with a SAVE instruction.  Patch up PC such that it points
      at the start of our PLT entry.  */
-  if (tdep->plt_entry_size > 0 && in_plt_section (current_pc, NULL))
+  if (tdep->plt_entry_size > 0 && in_plt_section (current_pc))
     pc = current_pc - ((current_pc - pc) % tdep->plt_entry_size);
 
   insn = sparc_fetch_instruction (pc);
Index: gdb-fsf-trunk-quilt/gdb/tic6x-tdep.c
===================================================================
--- gdb-fsf-trunk-quilt.orig/gdb/tic6x-tdep.c	2013-06-21 21:35:54.000000000 +0100
+++ gdb-fsf-trunk-quilt/gdb/tic6x-tdep.c	2013-06-21 21:35:57.141226418 +0100
@@ -530,7 +530,7 @@ tic6x_stub_unwind_sniffer (const struct 
   CORE_ADDR addr_in_block;
 
   addr_in_block = get_frame_address_in_block (this_frame);
-  if (in_plt_section (addr_in_block, NULL))
+  if (in_plt_section (addr_in_block))
     return 1;
 
   return 0;


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PING^2][PATCH] in_plt_section: support alternate stub section names
  2013-06-22  2:24           ` Maciej W. Rozycki
@ 2013-06-24 12:40             ` Pedro Alves
  2013-06-24 23:34               ` Maciej W. Rozycki
  0 siblings, 1 reply; 28+ messages in thread
From: Pedro Alves @ 2013-06-24 12:40 UTC (permalink / raw)
  To: Maciej W. Rozycki
  Cc: Tom Tromey, Richard Sandiford, Catherine Moore, gdb-patches

On 06/22/2013 12:32 AM, Maciej W. Rozycki wrote:
> On Fri, 21 Jun 2013, Pedro Alves wrote:
> 
>>>  Well, I have focused here, perhaps mistakenly, on the intended use of the 
>>> call -- to determine whether the PC is in a dynamic function call 
>>> trampoline.  Contrary to the description we currently have at 
>>> in_plt_section, .plt is not -- per SVR4 ABI -- a standard name of the 
>>> trampoline section.  The name (and the presence of any such section in the 
>>> first place) is actually left to processor-specific ABI supplements.
>>>
>>>  For many processors .plt has indeed been the choice, but for MIPS .plt 
>>> has only recently been added as an ABI extension.  The original MIPS SVR4 
>>> processor-specific ABI supplement defined no specific section name to be 
>>> used for its .plt equivalent.  I can't easily check what IRIX tools chose 
>>> for this section's name (if anything; in a final executable you can have 
>>> ELF segments whose contents are not mapped to any section).  Binutils 
>>> chose .stubs long ago and more recently switched to .MIPS.stubs.  This may 
>>> well be the same names IRIX used in different versions (compare .reginfo 
>>> vs .MIPS.options standard MIPS SVR4 psABI sections).
>>
>> Right, but extending in_plt_section torwards a general "in trampoline
>> section" would imply to me hiding platform details underneath, through
>> e.g., recording the section name in gdbarch, as calls in common
>> code wouldn't known about such target details.
> 
>  FWIW, this has been the original design of the internal API considered 
> here, that I excavated and quoted for the purpose of the original 
> submission:
> 
> http://sourceware.org/ml/gdb-patches/2013-06/msg00150.html
> 
> Not that we should repeat or maintain old mistakes, that is.

Hmm.  Using macros rather than gdbarch is something we've
moved away from since and we don't want to go back to,
of course.

But something is looking backwards to me.  Not so sure I'd
call it an old mistake.  For these in_plt_section calls:

nto-tdep.c:321:  if (in_plt_section (pc, NULL))
solib-dsbt.c:767:         || in_plt_section (pc, NULL));
solib-frv.c:451:          || in_plt_section (pc, NULL));
solib-svr4.c:1535:        || in_plt_section (pc, NULL)
solib-target.c:479:  return in_plt_section (pc, NULL);

it sounds like we really want to also check if the PC is in the
MIPS stubs section.  These are all in
target_solib_ops->in_dynsym_resolve_code implementations, used by
this bit of infrun:

  if (execution_direction != EXEC_REVERSE
      && ecs->event_thread->control.step_over_calls == STEP_OVER_UNDEBUGGABLE
      && in_solib_dynsym_resolve_code (stop_pc))
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    {
      CORE_ADDR pc_after_resolver =
	gdbarch_skip_solib_resolver (gdbarch, stop_pc);

      if (debug_infrun)
	 fprintf_unfiltered (gdb_stdlog,
			     "infrun: stepped into dynsym resolve code\n");

      if (pc_after_resolver)
	{
	  /* Set up a step-resume breakpoint at the address
	     indicated by SKIP_SOLIB_RESOLVER.  */
	  struct symtab_and_line sr_sal;

	  init_sal (&sr_sal);
	  sr_sal.pc = pc_after_resolver;
	  sr_sal.pspace = get_frame_program_space (frame);

	  insert_step_resume_breakpoint_at_sal (gdbarch,
						sr_sal, null_frame_id);
	}

      keep_going (ecs);
      return;
    }

in order to skip resolve code.  In the SVR4 case, that's:

/* Return 1 if PC lies in the dynamic symbol resolution code of the
   SVR4 run time loader.  */

int
svr4_in_dynsym_resolve_code (CORE_ADDR pc)
{
  struct svr4_info *info = get_svr4_info ();

  return ((pc >= info->interp_text_sect_low
	   && pc < info->interp_text_sect_high)
	  || (pc >= info->interp_plt_sect_low
	      && pc < info->interp_plt_sect_high)
	  || in_plt_section (pc, NULL)
             ^^^^^^^^^^^^^^^^^^^^^^^^^
	  || in_gnu_ifunc_stub (pc));
}

So it sounds like MIPS is still missing out a bit on this
optimization, single-stepping while in the .MIPS.stubs section,
even with your change.

I think it'd be better to reinstate IN_SOLIB_TRAMPOLINE, modernized
as a gdbarch hook.  The default gdbarch_in_solib_trampoline
would then be in_plt_section.  But MIPS would install its
own version, based on the existing mips_linux_in_dynsym_resolve_code,
with your changes:

static int
mips_linux_in_solib_trampoline (CORE_ADDR pc)
{
  /* Check whether the PC is in the .plt section, used by non-PIC
     executables.  */
  if (in_plt_section (pc))
     return 1;

  /* Likewise for the stubs.  They live in the .MIPS.stubs section these
     days, so we check if the PC is within, than fall back to a pattern
     match.  */
  if (mips_linux_in_dynsym_stub (pc))
     return 1;

  /* Pattern match for the stub.  It would be nice if there were a
     more efficient way to avoid this check.  */
  if (mips_linux_in_dynsym_stub (pc, NULL))
    return 1;

  return 0;
}


svr4_in_dynsym_resolve_code would then be adjusted like this:

int
svr4_in_dynsym_resolve_code (CORE_ADDR pc)
{
  struct svr4_info *info = get_svr4_info ();

  return ((pc >= info->interp_text_sect_low
	   && pc < info->interp_text_sect_high)
	  || (pc >= info->interp_plt_sect_low
	      && pc < info->interp_plt_sect_high)
-	  || in_plt_section (pc)
+	  || gdbarch_in_solib_trampoline (pc)
	  || in_gnu_ifunc_stub (pc));
}

Likewise for the other target_so_ops in_dynsym_resolve_code
hooks I pointed out at the top of the email.

As bonus, we'd no longer need the mips_svr4_so_ops ugly
hack in mips-linux-tdep.c at all:

  /* Initialize this lazily, to avoid an initialization order
     dependency on solib-svr4.c's _initialize routine.  */
  if (mips_svr4_so_ops.in_dynsym_resolve_code == NULL)
    {
      mips_svr4_so_ops = svr4_so_ops;
      mips_svr4_so_ops.in_dynsym_resolve_code
	= mips_linux_in_dynsym_resolve_code;
    }
  set_solib_ops (gdbarch, &mips_svr4_so_ops);


(the fact that mips_svr4_so_ops exists at all is what I'm
calling a hack.)

>> static inline is fine with me.  However, what I really dislike is the
>> inclusion of solib-svr4.h in parts of the debugger that have nothing
>> to do with SVR4, or even are implementing a different shared library
>> support backend, like solib-frv.c solib-dsbt.c, solib-target.c, etc.
>> That's really an abstraction violation, there bits have nothing to so
>> with SVR4.
> 
>  Umm, perhaps we have a file naming confusion in our sources 

GDB supports various different shared library mechanisms.
Each is abstracted out behind a "struct target_so_ops" instance,
and implemented on a solib-*.c file:

$ ls solib-*.c
solib-aix.c     solib-dsbt.c  solib-ia64-hpux.c  solib-osf.c   solib-som.c
solib-sunos.c  solib-target.c  solib-darwin.c  solib-frv.c   solib-irix.c
solib-pa64.c  solib-spu.c  solib-svr4.c

Some of these modules need to export functions to other modules, and
therefore have a corresponding .h file.

Not sure what isn't clear in the naming.

> or I am
> missing something.  The thing is all of the ELF stuff and its shared 
> library features such as the PLT are inherently SVR4 concepts.  Now there 
> are I think two possible options -- either our solib-svr4.h stuff is not 
> entirely SVR4 and includes things beyond or we support some pre-SVR4 
> systems (SunOS perhaps?) that already included early ELF support.  The 
> latter unfortunately I cannot comment on as I lack background information 
> here.  So what's the story behind it?

Not sure exactly what you're asking, but the history I believe was
that solib.c originally started out as supporting SunOS and then SVR4
came later, an then over time target_so_ops was invented, and SVR4 ended
up in solib-svr4.c, SunOS aged, and ended up split in solib-sunos.c.
The solib-target.c came along, and that one is (supposedly) entirely target_ops
driven.  IOW, the (remote) target feeds GDB the list of shared libraries,
reports events for load/unload, etc, unlike with svr4 and others, where GDB
coordinates with the loader by reading the loaders globals, and gets reported
of events through breakpoints and similars.

>  That written, given that this patch is blocking a cascade of changes 
> finding the exactly right location for in_plt_section seems secondary to 
> me and the objfiles module is where it already resides.  Moving it to 
> objfiles.h as a static inline function will already be some progress as it 
> won't be compiled in cases where it's not actually used (e.g. plain COFF).
> 
>  So here's a version you've suggested.  Even with this change applied we 
> can continue looking for a better place for in_plt_section.

Alright.

>  OK to apply?

Please mention the inclusion of "objfiles.h" in the ChangeLog,
in the files that needed it.  Okay with that change, but I'd
like the idea above to be explored.

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PING^2][PATCH] in_plt_section: support alternate stub section names
  2013-06-24 12:40             ` Pedro Alves
@ 2013-06-24 23:34               ` Maciej W. Rozycki
  2013-06-25  9:57                 ` Pedro Alves
  0 siblings, 1 reply; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-06-24 23:34 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Tom Tromey, Richard Sandiford, Catherine Moore, gdb-patches

On Mon, 24 Jun 2013, Pedro Alves wrote:

> >> Right, but extending in_plt_section torwards a general "in trampoline
> >> section" would imply to me hiding platform details underneath, through
> >> e.g., recording the section name in gdbarch, as calls in common
> >> code wouldn't known about such target details.
> > 
> >  FWIW, this has been the original design of the internal API considered 
> > here, that I excavated and quoted for the purpose of the original 
> > submission:
> > 
> > http://sourceware.org/ml/gdb-patches/2013-06/msg00150.html
> > 
> > Not that we should repeat or maintain old mistakes, that is.
> 
> Hmm.  Using macros rather than gdbarch is something we've
> moved away from since and we don't want to go back to,
> of course.

 Absolutely, I just referred to passing an extra optional NAME argument 
(that was originally used) as the old mistake.

> But something is looking backwards to me.  Not so sure I'd
> call it an old mistake.  For these in_plt_section calls:
> 
> nto-tdep.c:321:  if (in_plt_section (pc, NULL))
> solib-dsbt.c:767:         || in_plt_section (pc, NULL));
> solib-frv.c:451:          || in_plt_section (pc, NULL));
> solib-svr4.c:1535:        || in_plt_section (pc, NULL)
> solib-target.c:479:  return in_plt_section (pc, NULL);
> 
> it sounds like we really want to also check if the PC is in the
> MIPS stubs section.  These are all in
> target_solib_ops->in_dynsym_resolve_code implementations, used by
> this bit of infrun:
> 
>   if (execution_direction != EXEC_REVERSE
>       && ecs->event_thread->control.step_over_calls == STEP_OVER_UNDEBUGGABLE
>       && in_solib_dynsym_resolve_code (stop_pc))
>          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>     {
>       CORE_ADDR pc_after_resolver =
> 	gdbarch_skip_solib_resolver (gdbarch, stop_pc);
> 
>       if (debug_infrun)
> 	 fprintf_unfiltered (gdb_stdlog,
> 			     "infrun: stepped into dynsym resolve code\n");
> 
>       if (pc_after_resolver)
> 	{
> 	  /* Set up a step-resume breakpoint at the address
> 	     indicated by SKIP_SOLIB_RESOLVER.  */
> 	  struct symtab_and_line sr_sal;
> 
> 	  init_sal (&sr_sal);
> 	  sr_sal.pc = pc_after_resolver;
> 	  sr_sal.pspace = get_frame_program_space (frame);
> 
> 	  insert_step_resume_breakpoint_at_sal (gdbarch,
> 						sr_sal, null_frame_id);
> 	}
> 
>       keep_going (ecs);
>       return;
>     }
> 
> in order to skip resolve code.  In the SVR4 case, that's:
> 
> /* Return 1 if PC lies in the dynamic symbol resolution code of the
>    SVR4 run time loader.  */
> 
> int
> svr4_in_dynsym_resolve_code (CORE_ADDR pc)
> {
>   struct svr4_info *info = get_svr4_info ();
> 
>   return ((pc >= info->interp_text_sect_low
> 	   && pc < info->interp_text_sect_high)
> 	  || (pc >= info->interp_plt_sect_low
> 	      && pc < info->interp_plt_sect_high)
> 	  || in_plt_section (pc, NULL)
>              ^^^^^^^^^^^^^^^^^^^^^^^^^
> 	  || in_gnu_ifunc_stub (pc));
> }
> 
> So it sounds like MIPS is still missing out a bit on this
> optimization, single-stepping while in the .MIPS.stubs section,
> even with your change.

 Well, the hack you've mentioned below takes care of this for MIPS/Linux 
with the use of mips_linux_in_dynsym_resolve_code, however other MIPS 
targets using ELF shared libraries (NetBSD for example) are indeed left 
unhandled.  Not sure why MIPS/IRIX defines its irix_in_dynsym_resolve_code 
to return 0 in all cases, IRIX certainly uses MIPS stubs (though obviously 
not PLT).

> I think it'd be better to reinstate IN_SOLIB_TRAMPOLINE, modernized
> as a gdbarch hook.  The default gdbarch_in_solib_trampoline
> would then be in_plt_section.  But MIPS would install its
> own version, based on the existing mips_linux_in_dynsym_resolve_code,
> with your changes:
> 
> static int
> mips_linux_in_solib_trampoline (CORE_ADDR pc)
> {
>   /* Check whether the PC is in the .plt section, used by non-PIC
>      executables.  */
>   if (in_plt_section (pc))
>      return 1;
> 
>   /* Likewise for the stubs.  They live in the .MIPS.stubs section these
>      days, so we check if the PC is within, than fall back to a pattern
>      match.  */
>   if (mips_linux_in_dynsym_stub (pc))
>      return 1;

 You obviously mean in_mips_stubs_section here, don't you?

> 
>   /* Pattern match for the stub.  It would be nice if there were a
>      more efficient way to avoid this check.  */
>   if (mips_linux_in_dynsym_stub (pc, NULL))
>     return 1;
> 
>   return 0;
> }
> 
> 
> svr4_in_dynsym_resolve_code would then be adjusted like this:
> 
> int
> svr4_in_dynsym_resolve_code (CORE_ADDR pc)
> {
>   struct svr4_info *info = get_svr4_info ();
> 
>   return ((pc >= info->interp_text_sect_low
> 	   && pc < info->interp_text_sect_high)
> 	  || (pc >= info->interp_plt_sect_low
> 	      && pc < info->interp_plt_sect_high)
> -	  || in_plt_section (pc)
> +	  || gdbarch_in_solib_trampoline (pc)
> 	  || in_gnu_ifunc_stub (pc));
> }
> 
> Likewise for the other target_so_ops in_dynsym_resolve_code
> hooks I pointed out at the top of the email.
> 
> As bonus, we'd no longer need the mips_svr4_so_ops ugly
> hack in mips-linux-tdep.c at all:
> 
>   /* Initialize this lazily, to avoid an initialization order
>      dependency on solib-svr4.c's _initialize routine.  */
>   if (mips_svr4_so_ops.in_dynsym_resolve_code == NULL)
>     {
>       mips_svr4_so_ops = svr4_so_ops;
>       mips_svr4_so_ops.in_dynsym_resolve_code
> 	= mips_linux_in_dynsym_resolve_code;
>     }
>   set_solib_ops (gdbarch, &mips_svr4_so_ops);
> 
> 
> (the fact that mips_svr4_so_ops exists at all is what I'm
> calling a hack.)

 Indeed, mips_linux_in_dynsym_resolve_code will then go in its current 
form, becoming gdbarch's in_solib_trampoline backend.  It sounds like a 
plan to me, thanks for the hint, I'll give it a shot.

> >> static inline is fine with me.  However, what I really dislike is the
> >> inclusion of solib-svr4.h in parts of the debugger that have nothing
> >> to do with SVR4, or even are implementing a different shared library
> >> support backend, like solib-frv.c solib-dsbt.c, solib-target.c, etc.
> >> That's really an abstraction violation, there bits have nothing to so
> >> with SVR4.
> > 
> >  Umm, perhaps we have a file naming confusion in our sources 
> 
> GDB supports various different shared library mechanisms.
> Each is abstracted out behind a "struct target_so_ops" instance,
> and implemented on a solib-*.c file:
> 
> $ ls solib-*.c
> solib-aix.c     solib-dsbt.c  solib-ia64-hpux.c  solib-osf.c   solib-som.c
> solib-sunos.c  solib-target.c  solib-darwin.c  solib-frv.c   solib-irix.c
> solib-pa64.c  solib-spu.c  solib-svr4.c
> 
> Some of these modules need to export functions to other modules, and
> therefore have a corresponding .h file.
> 
> Not sure what isn't clear in the naming.

 Well, it may be my view of things -- I see SVR4 (as far as binary file 
formats are concerned; there's certainly a lot beyond that in SVR4) as the 
set of features as defined back in mid 1990s, starting from the ELF format 
itself and including the way ELF shared libraries are handled in 
particular.  So for me solib-svr4.c would then be the base feature set 
common to all ELF shared library systems, and then any features beyond 
that, added as newer systems invented them, included in their respective 
add-on modules.

 E.g. if an ELF shared library system FrobOS running on an odd processor 
added a capability to map segments of a library to memory in the reverse 
direction, but supported all the other usual library features, then it 
would use solib-svr4.c for the usual services and additionally solib-rev.c 
for the reverse-mapping features.  But as I say, perhaps it's my view of 
things that is reversed. ;)

> > or I am
> > missing something.  The thing is all of the ELF stuff and its shared 
> > library features such as the PLT are inherently SVR4 concepts.  Now there 
> > are I think two possible options -- either our solib-svr4.h stuff is not 
> > entirely SVR4 and includes things beyond or we support some pre-SVR4 
> > systems (SunOS perhaps?) that already included early ELF support.  The 
> > latter unfortunately I cannot comment on as I lack background information 
> > here.  So what's the story behind it?
> 
> Not sure exactly what you're asking, but the history I believe was
> that solib.c originally started out as supporting SunOS and then SVR4
> came later, an then over time target_so_ops was invented, and SVR4 ended
> up in solib-svr4.c, SunOS aged, and ended up split in solib-sunos.c.
> The solib-target.c came along, and that one is (supposedly) entirely target_ops
> driven.  IOW, the (remote) target feeds GDB the list of shared libraries,
> reports events for load/unload, etc, unlike with svr4 and others, where GDB
> coordinates with the loader by reading the loaders globals, and gets reported
> of events through breakpoints and similars.

 Hmm, I never really dug into this area, but from your description I 
gather that the line between the individual ELF solib-*.c modules is not 
as clear as it probably should be.  I.e. I don't know exactly what the 
relationship between SunOS and a generic SVR4 system wrt shared libraries 
is, but assuming SVR4 is a strict superset of SunOS, then I'd expect 
solib-svr4.c to implement the SVR4 additions only and rely on 
solib-sunos.c for the rest.  All the SVR4 systems and ones that are a 
superset of SVR4 features would useboth solib-svr4.c and solib-sunos.c, 
and obviously SunOS would only use solib-sunos.c.

 Contrariwise if a generic SVR4 system was actually not a superset of 
SunOS and the latter had some extra features beyond a common ELF shared 
library feature set, then a base ELF shared library module, say 
solib-elf.c would handle the common feature set.  Than solib-sunos.c would 
handle the SunOS additions and consequently SunOS would use solib-elf.c 
and solib-sunos.c.  Similarly a generic SVR4 system would use solib-elf.c 
and solib-svr4.c.  And that dreaded FrobOS would use all of solib-elf.c, 
solib-svr4.c and solib-rev.c.

> >  That written, given that this patch is blocking a cascade of changes 
> > finding the exactly right location for in_plt_section seems secondary to 
> > me and the objfiles module is where it already resides.  Moving it to 
> > objfiles.h as a static inline function will already be some progress as it 
> > won't be compiled in cases where it's not actually used (e.g. plain COFF).
> > 
> >  So here's a version you've suggested.  Even with this change applied we 
> > can continue looking for a better place for in_plt_section.
> 
> Alright.

 Good!

> >  OK to apply?
> 
> Please mention the inclusion of "objfiles.h" in the ChangeLog,
> in the files that needed it.  Okay with that change, but I'd
> like the idea above to be explored.

 OK, thanks.  Here's the final ChangeLog entry I've committed.  I'll look 
into redesigning the pieces you've mentioned above, but I'll ask for 
assistance with testing as I have no access to non-Linux MIPS ELF shared 
library systems.

2013-06-24  Maciej W. Rozycki  <macro@codesourcery.com>

	* objfiles.h (pc_in_section): New prototype.
	(in_plt_section): Remove name argument, replace prototype with
	static inline function.
	* mips-tdep.h: Include "objfiles.h".
	(in_mips_stubs_section): New function.
	* hppa-tdep.h (gdbarch_tdep): Remove name argument of
	in_solib_call_trampoline member.
	(hppa_in_solib_call_trampoline): Remove name argument.
	* objfiles.c (pc_in_section): New function.
	(in_plt_section): Remove function.
	* mips-linux-tdep.c: Include "objfiles.h".
	(mips_linux_in_dynsym_stub): Call in_mips_stubs_section.  Remove
	name argument.  Return 1 rather than the low 16-bit halfword of
	any instruction examined.
	(mips_linux_in_dynsym_resolve_code): Update
	mips_linux_in_dynsym_stub call accordingly.
	* mips-tdep.c (mips_stub_frame_sniffer): Use in_mips_stubs_section
	rather than an equivalent hand-coded sequence.
	* hppa-hpux-tdep.c (in_opd_section): Remove function.
	(hppa32_hpux_in_solib_call_trampoline): Remove name argument.
	(hppa64_hpux_in_solib_call_trampoline): Likewise.
	(hppa64_hpux_find_global_pointer): Use pc_in_section rather than
	in_opd_section.
	* hppa-tdep.c (hppa_stub_unwind_sniffer): Remove name argument
	on call to tdep->in_solib_call_trampoline.
	(hppa_in_solib_call_trampoline): Remove name argument, update
	according to in_plt_section change.
	(hppa_skip_trampoline_code): Update according to in_plt_section
	change.
	* aarch64-tdep.c (aarch64_stub_unwind_sniffer): Likewise.
	* arm-symbian-tdep.c (arm_symbian_skip_trampoline_code):
	Likewise.
	* arm-tdep.c (arm_stub_unwind_sniffer): Likewise.
	* hppa-linux-tdep.c (hppa_linux_find_global_pointer): Likewise.
	* hppabsd-tdep.c (hppabsd_find_global_pointer): Likewise.
	* nios2-tdep.c (nios2_stub_frame_sniffer): Likewise.
	* nto-tdep.c (nto_relocate_section_addresses): Likewise.
	* s390-tdep.c (s390_stub_frame_sniffer): Likewise.
	* sh-tdep.c (sh_stub_unwind_sniffer): Likewise.
	* solib-dsbt.c (dsbt_in_dynsym_resolve_code): Likewise.
	* solib-frv.c (frv_in_dynsym_resolve_code): Likewise.
	* solib-svr4.c (svr4_in_dynsym_resolve_code): Likewise.
	* solib-target.c (solib_target_in_dynsym_resolve_code): Likewise.
	* sparc-tdep.c (sparc_analyze_prologue): Likewise.
	* tic6x-tdep.c (tic6x_stub_unwind_sniffer): Likewise.

  Maciej


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-06-10 19:34               ` Maciej W. Rozycki
@ 2013-06-25  0:40                 ` Maciej W. Rozycki
  0 siblings, 0 replies; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-06-25  0:40 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Joel Brobecker, Catherine Moore, binutils, gdb-patches

On Mon, 10 Jun 2013, Maciej W. Rozycki wrote:

> > OK for the binutils bits.
> 
>  Thanks for the review, that's been a long journey! :)  I can self-approve 
> the mips-tdep.c change (any comments are welcome though, of course) -- I 
> just need to wait for someone to have a look at the generic in_plt_section 
> bits so as not to regress GDB at the same time.

 I have committed this change now, thanks to everybody involved.  I'll 
post an updated version of the test suite part shortly.

  Maciej


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PING^2][PATCH] in_plt_section: support alternate stub section names
  2013-06-24 23:34               ` Maciej W. Rozycki
@ 2013-06-25  9:57                 ` Pedro Alves
  0 siblings, 0 replies; 28+ messages in thread
From: Pedro Alves @ 2013-06-25  9:57 UTC (permalink / raw)
  To: Maciej W. Rozycki
  Cc: Tom Tromey, Richard Sandiford, Catherine Moore, gdb-patches

On 06/24/2013 11:58 PM, Maciej W. Rozycki wrote:
> On Mon, 24 Jun 2013, Pedro Alves wrote:
>> I think it'd be better to reinstate IN_SOLIB_TRAMPOLINE, modernized
>> as a gdbarch hook.  The default gdbarch_in_solib_trampoline
>> would then be in_plt_section.  But MIPS would install its
>> own version, based on the existing mips_linux_in_dynsym_resolve_code,
>> with your changes:
>>
>> static int
>> mips_linux_in_solib_trampoline (CORE_ADDR pc)
>> {
>>   /* Check whether the PC is in the .plt section, used by non-PIC
>>      executables.  */
>>   if (in_plt_section (pc))
>>      return 1;
>>
>>   /* Likewise for the stubs.  They live in the .MIPS.stubs section these
>>      days, so we check if the PC is within, than fall back to a pattern
>>      match.  */
>>   if (mips_linux_in_dynsym_stub (pc))
>>      return 1;
> 
>  You obviously mean in_mips_stubs_section here, don't you?

Yep.

>> As bonus, we'd no longer need the mips_svr4_so_ops ugly
>> hack in mips-linux-tdep.c at all:

...

>  Indeed, mips_linux_in_dynsym_resolve_code will then go in its current 
> form, becoming gdbarch's in_solib_trampoline backend.  It sounds like a 
> plan to me, thanks for the hint, I'll give it a shot.

Great, thanks.

>>>> static inline is fine with me.  However, what I really dislike is the
>>>> inclusion of solib-svr4.h in parts of the debugger that have nothing
>>>> to do with SVR4, or even are implementing a different shared library
>>>> support backend, like solib-frv.c solib-dsbt.c, solib-target.c, etc.
>>>> That's really an abstraction violation, there bits have nothing to so
>>>> with SVR4.
>>>
>>>  Umm, perhaps we have a file naming confusion in our sources 
>>
>> GDB supports various different shared library mechanisms.
>> Each is abstracted out behind a "struct target_so_ops" instance,
>> and implemented on a solib-*.c file:
>>
>> $ ls solib-*.c
>> solib-aix.c     solib-dsbt.c  solib-ia64-hpux.c  solib-osf.c   solib-som.c
>> solib-sunos.c  solib-target.c  solib-darwin.c  solib-frv.c   solib-irix.c
>> solib-pa64.c  solib-spu.c  solib-svr4.c
>>
>> Some of these modules need to export functions to other modules, and
>> therefore have a corresponding .h file.
>>
>> Not sure what isn't clear in the naming.
> 
>  Well, it may be my view of things -- I see SVR4 (as far as binary file 
> formats are concerned; there's certainly a lot beyond that in SVR4) as the 
> set of features as defined back in mid 1990s, starting from the ELF format 
> itself and including the way ELF shared libraries are handled in 
> particular.  So for me solib-svr4.c would then be the base feature set 
> common to all ELF shared library systems, and then any features beyond 
> that, added as newer systems invented them, included in their respective 
> add-on modules.

Many ELF systems supported will use solib-svr4.c.
However, each solib-foo.c module implements it's own struct target_so_ops.
They're all decoupled and independent from each other.  If a system is
sufficiently svr4-like, it'll use solib-svr4.c.  But e.g., solib-target.c is used
for the Windows port, which is COFF/PE as you know.  Or e.g., the sh/fr-v/bfin/tic6x
ports which are ELF based, but use FDPIC or DSBT (solib-frv.c, solib-dsbt.c),
and these ports don't include solib-srv4.c in the build at all, even though
the FDPIC/DSBT models are clearly inspired by SVR4 (and mention so in their
ABIs documents even).  Comparing solib-dsbt.c/solib-frv.c with solib-srv4.c,
and to me it looks like the similarities are only superficial.  A diff between
solib-svr4.c and solib-frv.c finds more difference than similarity.

The included-in-build issue is actually a good mental test --- say in_plt_section
was indeed implemented as a static inline in solib-svr4.h, instead of objfiles.h.
At some point, we decide it's too big to be inline, and make it extern.
Where would we define it then?  We wouldn't be able to put the definition
in solib-svr4.c, because of e.g., the Windows ports that don't include
solib-svr4.o in the build, even though your previous proposal made
solib-target.c include solib-svr4.h.

> 
>  E.g. if an ELF shared library system FrobOS running on an odd processor 
> added a capability to map segments of a library to memory in the reverse 
> direction, but supported all the other usual library features, then it 
> would use solib-svr4.c for the usual services and additionally solib-rev.c 
> for the reverse-mapping features.  But as I say, perhaps it's my view of 
> things that is reversed. ;)

If it's a minor detail, sure.  The MIPS case with its overriding of
in_dynsym_resolve_code is an example.  NTO/QNX is another example --
i386-nto-tdep.c:i386nto_init_abi.

>  Hmm, I never really dug into this area, but from your description I 
> gather that the line between the individual ELF solib-*.c modules is not 
> as clear as it probably should be.  I.e. I don't know exactly what the 
> relationship between SunOS and a generic SVR4 system wrt shared libraries 
> is, but assuming SVR4 is a strict superset of SunOS, then I'd expect 
> solib-svr4.c to implement the SVR4 additions only and rely on 
> solib-sunos.c for the rest.  All the SVR4 systems and ones that are a 
> superset of SVR4 features would useboth solib-svr4.c and solib-sunos.c, 
> and obviously SunOS would only use solib-sunos.c.

Nope.  solib-sunos.c is about a.out SunOS, though it's
actually only used by *BSD a.out ports, nowadays.

/* The shared library implementation found on BSD a.out systems is
   very similar to the SunOS implementation.  However, the data
   structures defined in <link.h> are named very differently.  Make up
   for those differences here.  */

We no longer support !ELF SunOS.  The Solaris support uses solib-svr4.c.

solib-sunos.c is self contained, as are all solib-*.c modules.
The differences to svr4 add up enough that bundling support in the
same module would be harder to maintain, IMO, and obviously in the
opinion of whoever split them up (Kevin, 2001, git 968327f).

> 
>  Contrariwise if a generic SVR4 system was actually not a superset of 
> SunOS and the latter had some extra features beyond a common ELF shared 
> library feature set, then a base ELF shared library module, say 
> solib-elf.c would handle the common feature set.  Than solib-sunos.c would 
> handle the SunOS additions and consequently SunOS would use solib-elf.c 
> and solib-sunos.c.  Similarly a generic SVR4 system would use solib-elf.c 
> and solib-svr4.c.  And that dreaded FrobOS would use all of solib-elf.c, 
> solib-svr4.c and solib-rev.c.

The question is then how much is there really to be shared.
I can't think of much.  Yeah, maybe in_plt_section could go there.
As I mention, even Symbian has plts (the system doesn't run elf
binaries, though the toolchain is elf based, with the binaries
post-linker processed into the final e32 format for space constrains
issues, though clearly there's elf inspiration.).  But if you
look at it as just a wrapper for pc_in_section that helps avoid
typing ".plt" in the source, there's no need to put any semantic
awareness of PLTs in the section's contents in the function itself;
leave that to the callers.  With the introduction of
gdbarch_in_solib_trampoline, uses of in_plt_section will go down
further, so I'm not sure I'd bother, though I certainly wouldn't object.

>> Please mention the inclusion of "objfiles.h" in the ChangeLog,
>> in the files that needed it.  Okay with that change, but I'd
>> like the idea above to be explored.
> 
>  OK, thanks.  Here's the final ChangeLog entry I've committed.  I'll look 
> into redesigning the pieces you've mentioned above, but I'll ask for 
> assistance with testing as I have no access to non-Linux MIPS ELF shared 
> library systems.

Thanks!

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH 1/2] MIPS: Compressed PLT/stubs support
  2013-03-11 13:53     ` Joel Brobecker
@ 2013-06-26 15:02       ` Maciej W. Rozycki
  0 siblings, 0 replies; 28+ messages in thread
From: Maciej W. Rozycki @ 2013-06-26 15:02 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: Richard Sandiford, Catherine Moore, binutils, gdb-patches

Joel,

On Mon, 11 Mar 2013, Joel Brobecker wrote:

> >  Of course it's still possible that a fortnight away you'll still not
> >  have rolled 7.6 out for one reason or another, but assuming that all
> >  goes well, what is the prospect of having 7.7 released before
> >  binutils 2.24?
> 
> We have several options if you miss 7.6. We can look at a 7.6.1,
> for instance. And if binutils keeps to its current typical pace
> of 1 major release a year, with the last one being around Oct,
> GDB 7.7 should also be out by then.

 Thanks for the details, I'll keep an eye on upcoming releases now that 
the change has finally gone in, and I hope we won't need a special GDB 
7.6.1 release, although I recall the binutils 2.23 release was a little 
bit later last year than originally scheduled (i.e. late Aug/early Sep).

  Maciej


^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2013-06-26 12:49 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-02-19 20:44 [PATCH 1/2] MIPS: Compressed PLT/stubs support Maciej W. Rozycki
2013-02-19 20:45 ` [PATCH 2/2] MIPS: Compressed PLT/stubs support test cases Maciej W. Rozycki
2013-02-20 21:53 ` [PATCH 1/2] MIPS: Compressed PLT/stubs support Richard Sandiford
2013-03-09  4:04   ` Maciej W. Rozycki
2013-03-09  9:58     ` Richard Sandiford
2013-06-08  0:22       ` Maciej W. Rozycki
2013-06-08 16:04         ` Richard Sandiford
2013-06-10 17:13           ` Maciej W. Rozycki
2013-06-10 18:08             ` Richard Sandiford
2013-06-10 19:34               ` Maciej W. Rozycki
2013-06-25  0:40                 ` Maciej W. Rozycki
2013-03-11 13:53     ` Joel Brobecker
2013-06-26 15:02       ` Maciej W. Rozycki
2013-02-21 21:06 ` Tom Tromey
2013-02-22  0:58   ` Alan Modra
2013-02-22  6:06     ` Alan Modra
2013-02-22 20:09       ` Tom Tromey
2013-03-09  4:06         ` Maciej W. Rozycki
2013-06-20 16:20   ` [PING^2][PATCH] in_plt_section: support alternate stub section names (was: [PATCH 1/2] MIPS: Compressed PLT/stubs support) Maciej W. Rozycki
2013-06-20 16:50     ` [PING^2][PATCH] in_plt_section: support alternate stub section names Pedro Alves
2013-06-21 11:43       ` Maciej W. Rozycki
2013-06-21 15:34         ` Pedro Alves
2013-06-22  2:24           ` Maciej W. Rozycki
2013-06-24 12:40             ` Pedro Alves
2013-06-24 23:34               ` Maciej W. Rozycki
2013-06-25  9:57                 ` Pedro Alves
2013-06-07 13:25 ` [PATCH] in_plt_section: support alternate stub section names (was: [PATCH 1/2] MIPS: Compressed PLT/stubs support) Maciej W. Rozycki
2013-06-13 12:43   ` [PING][PATCH] " Maciej W. Rozycki

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox