This is the mail archive of the binutils@sources.redhat.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: fix ppc32 long branch stubs


On Thu, Nov 06, 2003 at 02:19:24AM -0800, Eric Christopher wrote:
> re: comments - I believe that there was a reason for it, but it escapes
> me at the moment.

I'm taking it out.  As we discussed on irc, it was likely due to trying
to keep code compiled with -fpic or -fPIC position independent when
linked static.  That won't work anyway, due to lack of dynamic relocs,
non-pic crt* and probably other reasons.

Looking closer at the code, I discovered a number of other errors, so
have made quite a few fixes.  Also made some improvements.

	* elf32-ppc.c (ppc_elf_install_value): Delete.
	(shared_stub_entry, stub_entry): Make them arrays of ints.
	Remove initial branch.
	(ppc_elf_relax_section): Write one branch around all trampolines
	instead.  Correct bogus R_PPC_PLTREL24 handling.  Correct
	branch range check.  Only use shared_stub_entry when shared.
	Test that branches can reach stubs.  Write trampolines out at
	end so that just one realloc is used.  Handle little-endian
	mode.  Move relevant code from ppc_elf_install_value to here.
	(ppc_elf_relocate_section): Move code handling RELAX32 from
	ppc_elf_install_value to here.

Index: bfd/elf32-ppc.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-ppc.c,v
retrieving revision 1.105
diff -u -p -r1.105 elf32-ppc.c
--- bfd/elf32-ppc.c	6 Nov 2003 02:58:40 -0000	1.105
+++ bfd/elf32-ppc.c	6 Nov 2003 14:01:54 -0000
@@ -1625,84 +1625,26 @@ ppc_elf_howto_init (void)
     }
 }
 
-static bfd_reloc_status_type
-ppc_elf_install_value (bfd *abfd,
-		       bfd_byte *hit_addr,
-		       bfd_vma v,
-		       unsigned int r_type)
-{
-  bfd_vma t0, t1;
-#ifdef BFD_HOST_U_64_BIT
-  BFD_HOST_U_64_BIT val = (BFD_HOST_U_64_BIT) v;
-#else
-  bfd_vma val = v;
-#endif
-
-  switch (r_type)
-    {
-    case R_PPC_RELAX32:
-    case R_PPC_RELAX32PC:
-      t0 = bfd_get_32 (abfd, hit_addr);
-      t1 = bfd_get_32 (abfd, hit_addr + 4);
-
-      /* We're clearing the bits for R_PPC_ADDR16_HA
-	 and R_PPC_ADDR16_LO here.  */
-      t0 &= ~0xffff;
-      t1 &= ~0xffff;
-
-      /* t0 is HA, t1 is lo */
-      t0 |= ((val + 0x8000) >> 16) & 0xffff;
-      t1 |= val & 0xffff;
-
-      bfd_put_32 (abfd, t0, hit_addr);
-      bfd_put_32 (abfd, t1, hit_addr + 4);
-      break;
+#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
 
-    case R_PPC_REL24:
-    case R_PPC_LOCAL24PC:
-    case R_PPC_PLTREL24:
-      t0 = bfd_get_32 (abfd, hit_addr);
-      t0 &= ~0x3fffffc;
-      t0 |= val & 0x3fffffc;
-      bfd_put_32 (abfd, t0, hit_addr);
-      break;
-
-    case R_PPC_REL14:
-    case R_PPC_REL14_BRTAKEN:
-    case R_PPC_REL14_BRNTAKEN:
-      t0 = bfd_get_32 (abfd, hit_addr);
-      t0 &= ~0xfffc;
-      t0 |= val & 0xfffc;
-      bfd_put_32 (abfd, t0, hit_addr);
-      break;
-
-    default:
-      return bfd_reloc_notsupported;
-    }
-
-  return bfd_reloc_ok;
-}
-
-static const bfd_byte shared_stub_entry[] =
+static const int shared_stub_entry[] =
   {
-    0x48, 0x00, 0x00, 0x24, /* b .+36 */
-    0x7c, 0x08, 0x02, 0xa6, /* mflr 0 */
-    0x42, 0x9f, 0x00, 0x05, /* bcl 20, 31, .Lxxx */
-    0x7d, 0x68, 0x02, 0xa6, /* mflr 11 */
-    0x3d, 0x6b, 0x00, 0x00, /* addis 11, 11, (xxx-.Lxxx)@ha */
-    0x39, 0x6b, 0x00, 0x18, /* addi 11, 11, (xxx-.Lxxx)@l */
-    0x7c, 0x08, 0x03, 0xa6, /* mtlr 0 */
-    0x7d, 0x69, 0x03, 0xa6, /* mtctr 11 */
-    0x4e, 0x80, 0x04, 0x20, /* bctr */
+    0x7c0802a6, /* mflr 0 */
+    0x429f0005, /* bcl 20, 31, .Lxxx */
+    0x7d6802a6, /* mflr 11 */
+    0x3d6b0000, /* addis 11, 11, (xxx-.Lxxx)@ha */
+    0x396b0018, /* addi 11, 11, (xxx-.Lxxx)@l */
+    0x7c0803a6, /* mtlr 0 */
+    0x7d6903a6, /* mtctr 11 */
+    0x4e800420, /* bctr */
   };
 
-static const bfd_byte stub_entry[] =
+static const int stub_entry[] =
   {
-    0x48, 0x00, 0x00, 0x14, /* b .+20 */
-    0x3d, 0x60, 0x00, 0x00, /* lis 11,xxx@ha */
-    0x39, 0x6b, 0x00, 0x00, /* addi 11,11,xxx@l */
-    0x7d, 0x69, 0x03, 0xa6, /* mtctr 11 */
-    0x4e, 0x80, 0x04, 0x20, /* bctr */
+    0x3d600000, /* lis 11,xxx@ha */
+    0x396b0000, /* addi 11,11,xxx@l */
+    0x7d6903a6, /* mtctr 11 */
+    0x4e800420, /* bctr */
   };
 
 
@@ -1726,9 +1668,9 @@ ppc_elf_relax_section (bfd *abfd,
   Elf_Internal_Rela *internal_relocs = NULL;
   Elf_Internal_Rela *irel, *irelend;
   struct one_fixup *fixups = NULL;
-  bfd_boolean changed_contents = FALSE;
-  bfd_boolean changed_relocs = FALSE;
+  bfd_boolean changed;
   struct ppc_elf_link_hash_table *ppc_info;
+  bfd_size_type trampoff;
 
   *again = FALSE;
 
@@ -1743,6 +1685,10 @@ ppc_elf_relax_section (bfd *abfd,
   if (isec->_cooked_size == 0)
     isec->_cooked_size = isec->_raw_size;
 
+  trampoff = (isec->_cooked_size + 3) & (bfd_vma) -4;
+  /* Space for a branch around any trampolines.  */
+  trampoff += 4;
+
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
 
   /* Get a copy of the native relocations.  */
@@ -1772,21 +1718,26 @@ ppc_elf_relax_section (bfd *abfd,
   for (irel = internal_relocs; irel < irelend; irel++)
     {
       unsigned long r_type = ELF32_R_TYPE (irel->r_info);
-      bfd_vma symaddr, reladdr, trampoff, toff, roff;
+      bfd_vma symaddr, reladdr, toff, roff;
       asection *tsec;
-      bfd_size_type amt;
       struct one_fixup *f;
       size_t insn_offset = 0;
-      bfd_vma max_branch_offset;
+      bfd_vma max_branch_offset, val;
+      bfd_byte *hit_addr;
+      unsigned long t0;
 
       switch (r_type)
 	{
 	case R_PPC_REL24:
 	case R_PPC_LOCAL24PC:
+	case R_PPC_PLTREL24:
+	  max_branch_offset = 1 << 25;
+	  break;
+
 	case R_PPC_REL14:
 	case R_PPC_REL14_BRTAKEN:
 	case R_PPC_REL14_BRNTAKEN:
-	case R_PPC_PLTREL24:
+	  max_branch_offset = 1 << 15;
 	  break;
 
 	default:
@@ -1824,7 +1775,7 @@ ppc_elf_relax_section (bfd *abfd,
 	}
       else
 	{
-	  /* Need dynamic symbol handling.  */
+	  /* Global symbol handling.  */
 	  unsigned long indx;
 	  struct elf_link_hash_entry *h;
 
@@ -1835,62 +1786,34 @@ ppc_elf_relax_section (bfd *abfd,
 		 || h->root.type == bfd_link_hash_warning)
 	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
-	  if (r_type == R_PPC_PLTREL24)
+	  if (r_type == R_PPC_PLTREL24
+	      && ppc_info->plt != NULL
+	      && h->plt.offset != (bfd_vma) -1)
 	    {
-	      Elf_Internal_Sym *isym;
-
-	      if (h->plt.offset == (bfd_vma) -1
-		  || ppc_info->plt == NULL)
-		{
-
-		  /* Read this BFD's local symbols.  */
-		  if (isymbuf == NULL)
-		    {
-		      isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
-		      if (isymbuf == NULL)
-			isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
-							symtab_hdr->sh_info, 0,
-							NULL, NULL, NULL);
-		      if (isymbuf == 0)
-			goto error_return;
-		    }
-		  isym = isymbuf + ELF32_R_SYM (irel->r_info);
-
-		  if (isym->st_shndx == SHN_UNDEF)
-		    /* We can't do anthing with undefined symbols.  */
-		    continue;
-		  else if (isym->st_shndx == SHN_ABS)
-		    tsec = bfd_abs_section_ptr;
-		  else if (isym->st_shndx == SHN_COMMON)
-		    tsec = bfd_com_section_ptr;
-		  else
-		    tsec = h->root.u.def.section;
-
-		  toff = h->root.u.def.value;
-		}
-	      else
-		{
-		  tsec = ppc_info->plt;
-		  toff = h->plt.offset;
-		}
+	      tsec = ppc_info->plt;
+	      toff = h->plt.offset;
 	    }
-	  else if (h->root.type == bfd_link_hash_undefined
-		   || h->root.type == bfd_link_hash_undefweak)
-	    continue;
-
-	  else
+	  else if (h->root.type == bfd_link_hash_defined
+		   || h->root.type == bfd_link_hash_defweak)
 	    {
 	      tsec = h->root.u.def.section;
 	      toff = h->root.u.def.value;
 	    }
+	  else
+	    continue;
 	}
 
+      /* If the branch and target are in the same section, you have
+	 no hope of adding stubs.  We'll error out later should the
+	 branch overflow.  */
+      if (tsec == isec)
+	continue;
+
+      toff += irel->r_addend;
       if (tsec->sec_info_type == ELF_INFO_TYPE_MERGE)
 	toff = _bfd_merged_section_offset (abfd, &tsec,
 					   elf_section_data (tsec)->sec_info,
-					   toff + irel->r_addend, 0);
-      else
-	toff += irel->r_addend;
+					   toff, 0);
 
       symaddr = tsec->output_section->vma + tsec->output_offset + toff;
 
@@ -1898,70 +1821,41 @@ ppc_elf_relax_section (bfd *abfd,
 
       reladdr = (isec->output_section->vma
 		 + isec->output_offset
-		 + roff) & (bfd_vma) -4;
+		 + roff);
 
       /* If the branch is in range, no need to do anything.  */
-      max_branch_offset = 1 << 25;
-      if (r_type != R_PPC_REL24
-	  && r_type != R_PPC_LOCAL24PC
-	  && r_type != R_PPC_PLTREL24)
-	max_branch_offset = 1 << 15;
-
-      if ((bfd_vma) (symaddr - reladdr) + max_branch_offset
-	  <= 2 * max_branch_offset)
+      if (symaddr - reladdr + max_branch_offset < 2 * max_branch_offset)
 	continue;
 
-      /* If the branch and target are in the same section, you have
-	 no hope.  We'll error out later.  */
-      if (tsec == isec)
-	continue;
-
-      /* Look for an existing fixup to this address.
-	 ??? What if the existing fixup is for a non-pic stub, and the
-	 new one requires a pic stub?  This presumably could happen with
-	 a static link and a mix of R_PPC_LOCAL24PC and R_PPC_REL24
-	 relocs to a symbol needing long branch stubs.
-	 ??? Why do we require R_PPC_LOCAL24PC and branches to the plt
-	 to have a shared branch stub?  Shared branch stubs should only
-	 be needed when info->shared.  */
+      /* Look for an existing fixup to this address.  */
       for (f = fixups; f ; f = f->next)
 	if (f->tsec == tsec && f->toff == toff)
 	  break;
 
       if (f == NULL)
 	{
-	  const bfd_byte *stub;
 	  size_t size;
 	  unsigned long stub_rtype;
 
-	  if (link_info->shared
-	      || tsec == ppc_info->plt
-	      || r_type == R_PPC_LOCAL24PC)
-	    {
-	      stub = shared_stub_entry;
-	      size = sizeof (shared_stub_entry);
-	      insn_offset = 16;
+	  val = trampoff - roff;
+	  if (val >= max_branch_offset)
+	    /* Oh dear, we can't reach a trampoline.  Don't try to add
+	       one.  We'll report an error later.  */
+	    continue;
+
+	  if (link_info->shared)
+	    {
+	      size = 4 * ARRAY_SIZE (shared_stub_entry);
+	      insn_offset = 12;
 	      stub_rtype = R_PPC_RELAX32PC;
 	    }
 	  else
 	    {
-	      stub = stub_entry;
-	      size = sizeof (stub_entry);
-	      insn_offset = 4;
+	      size = 4 * ARRAY_SIZE (stub_entry);
+	      insn_offset = 0;
 	      stub_rtype = R_PPC_RELAX32;
 	    }
 
-	  /* Resize the current section to make room for the new branch.  */
-	  trampoff = (isec->_cooked_size + 3) & (bfd_vma) - 4;
-	  amt = trampoff + size;
-	  contents = bfd_realloc (contents, amt);
-	  if (contents == NULL)
-	    abort ();
-
-	  isec->_cooked_size = amt;
-
-	  memcpy (contents + trampoff, stub, size);
-
 	  /* Hijack the old relocation.  Since we need two
 	     relocations for this use a "composite" reloc.  */
 	  irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
@@ -1975,31 +1869,95 @@ ppc_elf_relax_section (bfd *abfd,
 	  f->toff = toff;
 	  f->trampoff = trampoff;
 	  fixups = f;
+
+	  trampoff += size;
 	}
       else
 	{
+	  val = f->trampoff - roff;
+	  if (val >= max_branch_offset)
+	    continue;
+
 	  /* Nop out the reloc, since we're finalizing things here.  */
 	  irel->r_info = ELF32_R_INFO (0, R_PPC_NONE);
 	}
 
-      /* Fix up the existing branch to hit the trampoline.  Hope like
-	 hell this doesn't overflow too.  */
-      if (ppc_elf_install_value (abfd, contents + roff,
-				 f->trampoff - (roff & (bfd_vma) -3) + 4,
-				 r_type) != bfd_reloc_ok)
-	abort ();
+      /* Fix up the existing branch to hit the trampoline.  */
+      hit_addr = contents + roff;
+      switch (r_type)
+	{
+	case R_PPC_REL24:
+	case R_PPC_LOCAL24PC:
+	case R_PPC_PLTREL24:
+	  t0 = bfd_get_32 (abfd, hit_addr);
+	  t0 &= ~0x3fffffc;
+	  t0 |= val & 0x3fffffc;
+	  bfd_put_32 (abfd, t0, hit_addr);
+	  break;
 
-      changed_contents = TRUE;
-      changed_relocs = TRUE;
+	case R_PPC_REL14:
+	case R_PPC_REL14_BRTAKEN:
+	case R_PPC_REL14_BRNTAKEN:
+	  t0 = bfd_get_32 (abfd, hit_addr);
+	  t0 &= ~0xfffc;
+	  t0 |= val & 0xfffc;
+	  bfd_put_32 (abfd, t0, hit_addr);
+	  break;
+	}
     }
 
-  /* Clean up.  */
-  while (fixups)
+  /* Write out the trampolines.  */
+  changed = fixups != NULL;
+  if (fixups != NULL)
     {
-      struct one_fixup *f = fixups;
-      fixups = fixups->next;
-      free (f);
+      const int *stub;
+      bfd_byte *dest;
+      bfd_vma val;
+      int i, size;
+
+      do
+	{
+	  struct one_fixup *f = fixups;
+	  fixups = fixups->next;
+	  free (f);
+	}
+      while (fixups);
+
+      contents = bfd_realloc (contents, trampoff);
+      if (contents == NULL)
+	goto error_return;
+
+      isec->_cooked_size = (isec->_cooked_size + 3) & (bfd_vma) -4;
+      /* Branch around the trampolines.  */
+      val = trampoff - isec->_cooked_size + 0x48000000;
+      dest = contents + isec->_cooked_size;
+      isec->_cooked_size = trampoff;
+      bfd_put_32 (abfd, val, dest);
+      dest += 4;
+
+      if (link_info->shared)
+	{
+	  stub = shared_stub_entry;
+	  size = ARRAY_SIZE (shared_stub_entry);
+	}
+      else
+	{
+	  stub = stub_entry;
+	  size = ARRAY_SIZE (stub_entry);
+	}
+
+      i = 0;
+      while (dest < contents + trampoff)
+	{
+	  bfd_put_32 (abfd, stub[i], dest);
+	  i++;
+	  if (i == size)
+	    i = 0;
+	  dest += 4;
+	}
+      BFD_ASSERT (i == 0);
     }
+
   if (isymbuf != NULL
       && symtab_hdr->contents != (unsigned char *) isymbuf)
     {
@@ -2015,7 +1973,7 @@ ppc_elf_relax_section (bfd *abfd,
   if (contents != NULL
       && elf_section_data (isec)->this_hdr.contents != contents)
     {
-      if (!changed_contents && !link_info->keep_memory)
+      if (!changed && !link_info->keep_memory)
 	free (contents);
       else
 	{
@@ -2026,13 +1984,13 @@ ppc_elf_relax_section (bfd *abfd,
 
   if (elf_section_data (isec)->relocs != internal_relocs)
     {
-      if (!changed_relocs)
+      if (!changed)
 	free (internal_relocs);
       else
 	elf_section_data (isec)->relocs = internal_relocs;
     }
 
-  *again = changed_contents || changed_relocs;
+  *again = changed;
   return TRUE;
 
  error_return:
@@ -5462,8 +5420,26 @@ ppc_elf_relocate_section (bfd *output_bf
 			 + rel->r_offset - 4);
 	  /* Fall thru */
 	case R_PPC_RELAX32:
-	  ppc_elf_install_value (output_bfd, contents + rel->r_offset,
-				 relocation + addend, r_type);
+	  {
+	    unsigned long t0;
+	    unsigned long t1;
+
+	    t0 = bfd_get_32 (output_bfd, contents + rel->r_offset);
+	    t1 = bfd_get_32 (output_bfd, contents + rel->r_offset + 4);
+
+	    /* We're clearing the bits for R_PPC_ADDR16_HA
+	       and R_PPC_ADDR16_LO here.  */
+	    t0 &= ~0xffff;
+	    t1 &= ~0xffff;
+
+	    /* t0 is HA, t1 is LO */
+	    relocation += addend;
+	    t0 |= ((relocation + 0x8000) >> 16) & 0xffff;
+	    t1 |= relocation & 0xffff;
+
+	    bfd_put_32 (output_bfd, t0, contents + rel->r_offset);
+	    bfd_put_32 (output_bfd, t1, contents + rel->r_offset + 4);
+	  }
 	  continue;
 
 	  /* Indirect .sdata relocation.  */

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]