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

Maciej W. Rozycki macro@codesourcery.com
Sat Mar 9 04:04:00 GMT 2013


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



More information about the Binutils mailing list