public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] Fixing MIPS16 vs LA25 stubs
@ 2011-12-10  9:52 Chung-Lin Tang
  2011-12-10 11:32 ` Richard Sandiford
  0 siblings, 1 reply; 2+ messages in thread
From: Chung-Lin Tang @ 2011-12-10  9:52 UTC (permalink / raw)
  To: binutils; +Cc: Richard Sandiford

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

Hi Richard,
The MIPS16 code has some complicated relations with LA25 stubs
(LUI/ADDUI $25 sequences added by BFD when calling from non-PIC to PIC).

Current PLT support is capable of adding (either prepending or separate
stub) LA25 sequences to create $t9 on entry of a PIC callee. However,
when the called routine is MIPS16 code with a FP-stub (stub is 32-bit),
then we have, for example:

int main (void)
{
  return __fixdfdi (0.0);  // libgcc function
}

Build as:
mips-linux-gnu-gcc -c -O2 testcase.c    # build as 32-bit
mips-linux-gnu-gcc -mips16 testcase.o   # link with MIPS16 libs
./a.out
Segmentation fault

Disassembly:
00400930 <__fn_stub___fixdfdi>:
  400930:       3c1c0002        lui     gp,0x2
  400934:       279c8100        addiu   gp,gp,-32512
  400938:       0399e021        addu    gp,gp,t9
  40093c:       8f998024        lw      t9,-32732(gp)  <--segfault
  400940:       27390621        addiu   t9,t9,1569
  400944:       44056000        mfc1    a1,$f12
  400948:       03200008        jr      t9
  40094c:       44046800        mfc1    a0,$f13

The call is from main:
00400610 <main>:
  400610:       44806000        mtc1    zero,$f12
  400614:       0810024c        j       400930 <__fn_stub___fixdfdi>
  400618:       44806800        mtc1    zero,$f13
  40061c:       00000000        nop

The actual __fixdfdi body, which is MIPS16 code:
00400620 <__fixdfdi>:
  400620:       f000 6a02       li      v0,2
  400624:       f410 0b0c       la      v1,3f8a30 <_DYNAMIC-0x774c>
  400628:       f400 3240       sll     v0,16
  40062c:       e269            addu    v0,v1
  40062e:       659a            move    gp,v0
  400630:       64f6            save    48,ra,s0-s1
  ...

The problem is that 'la $25' headers/trampolines are needed for the
"stub", which is 32-bit code and calculates $gp using $t9, not the
MIPS16 function itself (which as the BFD support already correctly
determines, are never needed due to MIPS16's PC-relative capabilities).
Also note that this problem is avoided when -mno-plt is used.


My patch attached here modifies the LA25 insertion to accommodate the
case where we have a "MIPS16 function with a kept 32-bit stub", and uses
the stub as the point of insertion. The correctly linked results are now
like:
00400610 <main>:
  400610:       44806000        mtc1    zero,$f12
  400614:       0810024c        j       400930 <.pic.__fixdfdi>
  400618:       44806800        mtc1    zero,$f13
  40061c:       00000000        nop
...
00400930 <.pic.__fixdfdi>:
  400930:       3c190040        lui     t9,0x40
  400934:       27390938        addiu   t9,t9,2360

00400938 <__fn_stub___fixdfdi>:
  400938:       3c1c0002        lui     gp,0x2
  40093c:       279c80f8        addiu   gp,gp,-32520
  400940:       0399e021        addu    gp,gp,t9
  400944:       8f998024        lw      t9,-32732(gp)
  400948:       27390621        addiu   t9,t9,1569
  40094c:       44056000        mfc1    a1,$f12
  400950:       03200008        jr      t9
  400954:       44046800        mfc1    a0,$f13

Which should be correctly generated "stub of stub" code :)  This is for
the 32-bit->16-bit case. There's also some handling to skip the LA25
stub for 16-bit->16-bit case.

Thanks,
Chung-Lin

2011-12-10  Chung-Lin Tang  <cltang@codesourcery.com>
	    Catherine Moore  <clm@codesourcery.com>
	    Sandra Loosemore  <sandra@codesourcery.com>

        * elfxx-mips.c (mips_elf_local_pic_function_p): Return true when
        H is a MIPS16 function with a kept 32-bit stub. Update comments.
        (mips_elf_add_la25_intro): Use MIPS16 stub section as place of
        insertion when it exists.
        (mips_elf_add_la25_stub): Always use LUI/ADDIU prepending for
        MIPS16 stubs. Update comments.
        (mips_elf_calculate_relocation): Redirect relocation to point to
        the LA25 stub if it exists, instead of the MIPS16 stub. Don't
	use la25 stub for mips16->mips16 calls.
        (mips_elf_create_la25_stub): Change $25 target calculation to
        base on MIPS16 stub if it exists.




[-- Attachment #2: la25.diff --]
[-- Type: text/plain, Size: 4572 bytes --]

Index: elfxx-mips.c
===================================================================
RCS file: /cvs/src/src/bfd/elfxx-mips.c,v
retrieving revision 1.297
diff -u -p -r1.297 elfxx-mips.c
--- elfxx-mips.c	8 Dec 2011 20:47:24 -0000	1.297
+++ elfxx-mips.c	10 Dec 2011 09:23:38 -0000
@@ -1573,7 +1573,9 @@ _bfd_mips_elf_init_stubs (struct bfd_lin
 /* Return true if H is a locally-defined PIC function, in the sense
    that it might need $25 to be valid on entry.  Note that MIPS16
    functions never need $25 to be valid on entry; they set up $gp
-   using PC-relative instructions instead.  */
+   using PC-relative instructions instead.  We do process the case
+   where a MIPS16 function has a 32-bit stub, where the $25 load
+   is added for the 32-bit stub itself, not the 16-bit function.  */
 
 static bfd_boolean
 mips_elf_local_pic_function_p (struct mips_elf_link_hash_entry *h)
@@ -1582,7 +1584,8 @@ mips_elf_local_pic_function_p (struct mi
 	   || h->root.root.type == bfd_link_hash_defweak)
 	  && h->root.def_regular
 	  && !bfd_is_abs_section (h->root.root.u.def.section)
-	  && !ELF_ST_IS_MIPS16 (h->root.other)
+	  && (!ELF_ST_IS_MIPS16 (h->root.other)
+	      || (h->fn_stub && h->need_fn_stub))
 	  && (PIC_OBJECT_P (h->root.root.u.def.section->owner)
 	      || ELF_ST_IS_MIPS_PIC (h->root.other)));
 }
@@ -1610,8 +1613,10 @@ mips_elf_add_la25_intro (struct mips_elf
     return FALSE;
   sprintf (name, ".text.stub.%d", (int) htab_elements (htab->la25_stubs));
 
-  /* Create the section.  */
-  input_section = stub->h->root.root.u.def.section;
+  /* Create the section.  If H is a MIPS16 function with a stub, use the
+     stub function section.  */
+  input_section = (stub->h->fn_stub
+		   ? stub->h->fn_stub : stub->h->root.root.u.def.section);
   s = htab->add_stub_section (name, input_section,
 			      input_section->output_section);
   if (s == NULL)
@@ -1686,10 +1691,12 @@ mips_elf_add_la25_stub (struct bfd_link_
   void **slot;
 
   /* Prefer to use LUI/ADDIU stubs if the function is at the beginning
-     of the section and if we would need no more than 2 nops.  */
+     of the section and if we would need no more than 2 nops.  Also
+     when processing a 32-bit stub for a MIPS16 function, we can always
+     preprend before the stub section.  */
   s = h->root.root.u.def.section;
   value = h->root.root.u.def.value;
-  use_trampoline_p = (value != 0 || s->alignment_power > 4);
+  use_trampoline_p = (value != 0 || s->alignment_power > 4) && !h->fn_stub;
 
   /* Describe the stub we want.  */
   search.stub_section = NULL;
@@ -5196,7 +5203,15 @@ mips_elf_calculate_relocation (bfd *abfd
 	  sec = h->fn_stub;
 	}
 
-      symbol = sec->output_section->vma + sec->output_offset;
+      /* If a LA25 header for the stub itself exists, point to the prepended
+	 LUI/ADDIU sequence.  */
+      if (h != NULL && h->la25_stub)
+	symbol = (h->la25_stub->stub_section->output_section->vma
+		  + h->la25_stub->stub_section->output_offset
+		  + h->la25_stub->offset);
+      else
+	symbol = sec->output_section->vma + sec->output_offset;
+
       /* The target is 16-bit, but the stub isn't.  */
       target_is_16_bit_code_p = FALSE;
     }
@@ -5246,7 +5261,8 @@ mips_elf_calculate_relocation (bfd *abfd
   /* If this is a direct call to a PIC function, redirect to the
      non-PIC stub.  */
   else if (h != NULL && h->la25_stub
-	   && mips_elf_relocation_needs_la25_stub (input_bfd, r_type))
+	   && mips_elf_relocation_needs_la25_stub (input_bfd, r_type)
+	   && !(r_type == R_MIPS16_26 && target_is_16_bit_code_p))
     symbol = (h->la25_stub->stub_section->output_section->vma
 	      + h->la25_stub->stub_section->output_offset
 	      + h->la25_stub->offset);
@@ -9615,10 +9631,16 @@ mips_elf_create_la25_stub (void **slot, 
   /* Work out where in the section this stub should go.  */
   offset = stub->offset;
 
-  /* Work out the target address.  */
-  target = (stub->h->root.root.u.def.section->output_section->vma
-	    + stub->h->root.root.u.def.section->output_offset
-	    + stub->h->root.root.u.def.value);
+  /* Work out the target address.  If a MIPS16 32-bit stub exists,
+     use it as the target instead.  */
+  if (stub->h->fn_stub)
+    target = (stub->h->fn_stub->output_section->vma
+	      + stub->h->fn_stub->output_offset);
+  else
+    target = (stub->h->root.root.u.def.section->output_section->vma
+	      + stub->h->root.root.u.def.section->output_offset
+	      + stub->h->root.root.u.def.value);
+
   target_high = ((target + 0x8000) >> 16) & 0xffff;
   target_low = (target & 0xffff);
 

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

* Re: [PATCH] Fixing MIPS16 vs LA25 stubs
  2011-12-10  9:52 [PATCH] Fixing MIPS16 vs LA25 stubs Chung-Lin Tang
@ 2011-12-10 11:32 ` Richard Sandiford
  0 siblings, 0 replies; 2+ messages in thread
From: Richard Sandiford @ 2011-12-10 11:32 UTC (permalink / raw)
  To: Chung-Lin Tang; +Cc: binutils

Chung-Lin Tang <cltang@codesourcery.com> writes:
> @@ -1573,7 +1573,9 @@ _bfd_mips_elf_init_stubs (struct bfd_lin
>  /* Return true if H is a locally-defined PIC function, in the sense
>     that it might need $25 to be valid on entry.  Note that MIPS16
>     functions never need $25 to be valid on entry; they set up $gp
> -   using PC-relative instructions instead.  */
> +   using PC-relative instructions instead.  We do process the case
> +   where a MIPS16 function has a 32-bit stub, where the $25 load
> +   is added for the 32-bit stub itself, not the 16-bit function.  */

Nitpick, but this feels too tacked on.  How about:

/* Return true if H is a locally-defined PIC function, in the sense
   that it or its fn_stub might need $25 to be valid on entry.
   Note that MIPS16 functions set up $gp using PC-relative instructions,
   so they themselves never need $25 to be valid.  Only non-MIPS16
   entry points are of interest here.  */

> @@ -1610,8 +1613,10 @@ mips_elf_add_la25_intro (struct mips_elf
>      return FALSE;
>    sprintf (name, ".text.stub.%d", (int) htab_elements (htab->la25_stubs));
>  
> -  /* Create the section.  */
> -  input_section = stub->h->root.root.u.def.section;
> +  /* Create the section.  If H is a MIPS16 function with a stub, use the
> +     stub function section.  */
> +  input_section = (stub->h->fn_stub
> +		   ? stub->h->fn_stub : stub->h->root.root.u.def.section);
>    s = htab->add_stub_section (name, input_section,
>  			      input_section->output_section);
>    if (s == NULL)

Let's split this out into:

/* Set *SEC to the input section that contains the target of STUB.
   Return the offset of the target from the start of that section.  */

static bfd_vma
mips_elf_get_la25_target (struct mips_elf_la25_stub *stub,
			  asection **sec)
{
  if (ELF_ST_IS_MIPS16 (stub->h))
    {
      BFD_ASSERT (stub->h->need_fn_stub);
      *sec = stub->h->fn_stub;
      return 0;
    }
  else
    {
      *sec = stub->h->root.root.u.def.section;
      return h->root.root.u.def.value;
    }
}

(just after mips_elf_local_pic_function_p would be a good place).
Then the code above becomes:

  /* Create the section.  */
  mips_elf_get_la25_target (stub, &input_section);

> @@ -1686,10 +1691,12 @@ mips_elf_add_la25_stub (struct bfd_link_
>    void **slot;
>  
>    /* Prefer to use LUI/ADDIU stubs if the function is at the beginning
> -     of the section and if we would need no more than 2 nops.  */
> +     of the section and if we would need no more than 2 nops.  Also
> +     when processing a 32-bit stub for a MIPS16 function, we can always
> +     preprend before the stub section.  */
>    s = h->root.root.u.def.section;
>    value = h->root.root.u.def.value;
> -  use_trampoline_p = (value != 0 || s->alignment_power > 4);
> +  use_trampoline_p = (value != 0 || s->alignment_power > 4) && !h->fn_stub;

This then becomes:

  /* Prefer to use LUI/ADDIU stubs if the function is at the beginning
     of the section and if we would need no more than 2 nops.  */
  value = mips_elf_get_la25_target (stub, &s);
  use_trampoline_p = (value != 0 || s->alignment_power > 4);

(I don't want to special-case stub sections here: if someone does for
whatever reason give stub sections a large alignment, the separate
trampoline is better.)

> @@ -5196,7 +5203,15 @@ mips_elf_calculate_relocation (bfd *abfd
>  	  sec = h->fn_stub;
>  	}
>  
> -      symbol = sec->output_section->vma + sec->output_offset;
> +      /* If a LA25 header for the stub itself exists, point to the prepended
> +	 LUI/ADDIU sequence.  */
> +      if (h != NULL && h->la25_stub)
> +	symbol = (h->la25_stub->stub_section->output_section->vma
> +		  + h->la25_stub->stub_section->output_offset
> +		  + h->la25_stub->offset);
> +      else
> +	symbol = sec->output_section->vma + sec->output_offset;
> +

This becomes a bit confusing, because it's only relevant for the !local_p case.
How about:

      if (local_p)
	{
	  sec = elf_tdata (input_bfd)->local_stubs[r_symndx];
	  value = 0;
	}
      else
	{
	  BFD_ASSERT (h->need_fn_stub);
	  if (h->la25_stub)
	    {
	      sec = h->la25_stub->section;
	      value = h->la25_stub->offset;
	    }
	  else
	    {
	      sec = h->fn_stub;
	      value = 0;
	    }
	}

      symbol = sec->output_section->vma + sec->output_offset + value;
      /* The target is 16-bit, but the stub isn't.  */
      target_is_16_bit_code_p = FALSE;

> @@ -5246,7 +5261,8 @@ mips_elf_calculate_relocation (bfd *abfd
>    /* If this is a direct call to a PIC function, redirect to the
>       non-PIC stub.  */
>    else if (h != NULL && h->la25_stub
> -	   && mips_elf_relocation_needs_la25_stub (input_bfd, r_type))
> +	   && mips_elf_relocation_needs_la25_stub (input_bfd, r_type)
> +	   && !(r_type == R_MIPS16_26 && target_is_16_bit_code_p))
>      symbol = (h->la25_stub->stub_section->output_section->vma
>  	      + h->la25_stub->stub_section->output_offset
>  	      + h->la25_stub->offset);

This logically belongs in mips_elf_relocation_needs_la25_stub.
Let's pass the target_is_16_bit_code_p there:

/* Return TRUE if a relocation of type R_TYPE from INPUT_BFD might
   require an la25 stub.  TARGET_IS_16_BIT_CODE_P is true if the
   target is microMIPS or MIPS16 code.

   See also mips_elf_local_pic_function_p, which determines whether the
   destination function ever requires a stub.  */

static bfd_boolean
mips_elf_relocation_needs_la25_stub (bfd *input_bfd, int r_type,
				     bfd_boolean target_is_16_bit_code_p)
{
  /* We specifically ignore branches and jumps from EF_PIC objects,
     where the onus is on the compiler or programmer to perform any
     necessary initialization of $25.  Sometimes such initialization
     is unnecessary; for example, -mno-shared functions do not use
     the incoming value of $25, and may therefore be called directly.  */
  if (PIC_OBJECT_P (input_bfd))
    return FALSE;

  switch (r_type)
    {
    case R_MIPS_26:
    case R_MIPS_PC16:
    case R_MICROMIPS_26_S1:
    case R_MICROMIPS_PC7_S1:
    case R_MICROMIPS_PC10_S1:
    case R_MICROMIPS_PC16_S1:
    case R_MICROMIPS_PC23_S2:
      return TRUE;

    case R_MIPS16_26:
      return !target_is_16_bit_code_p;

    default:
      return FALSE;
    }
}

> @@ -9615,10 +9631,16 @@ mips_elf_create_la25_stub (void **slot, 
>    /* Work out where in the section this stub should go.  */
>    offset = stub->offset;
>  
> -  /* Work out the target address.  */
> -  target = (stub->h->root.root.u.def.section->output_section->vma
> -	    + stub->h->root.root.u.def.section->output_offset
> -	    + stub->h->root.root.u.def.value);
> +  /* Work out the target address.  If a MIPS16 32-bit stub exists,
> +     use it as the target instead.  */
> +  if (stub->h->fn_stub)
> +    target = (stub->h->fn_stub->output_section->vma
> +	      + stub->h->fn_stub->output_offset);
> +  else
> +    target = (stub->h->root.root.u.def.section->output_section->vma
> +	      + stub->h->root.root.u.def.section->output_offset
> +	      + stub->h->root.root.u.def.value);
> +

This becomes:

  /* Work out the target address.  */
  target = mips_elf_get_la25_target (stub, &s);
  target += s->output_section->offset + s->output_offset;

OK with those changes, thanks.

Richard

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

end of thread, other threads:[~2011-12-10 11:32 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-12-10  9:52 [PATCH] Fixing MIPS16 vs LA25 stubs Chung-Lin Tang
2011-12-10 11:32 ` Richard Sandiford

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).