[PATCH 1/2] MIPS: Compressed PLT/stubs support

Maciej W. Rozycki
2013-02-19T20:44:49Z

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;
}