ppc476 workaround for ld -r fixes
authorAlan Modra <amodra@gmail.com>
Mon, 17 Feb 2014 06:21:25 +0000 (16:51 +1030)
committerAlan Modra <amodra@gmail.com>
Mon, 17 Feb 2014 06:30:19 +0000 (17:00 +1030)
This fixes the glaring error that the ppc476 workaround wasn't
actually enabled for ld -r, and adjusts relocations to match moved
code.

bfd/
* elf32-ppc.c (ppc_elf_relocate_section): Move relocs on insns
patched for ppc476 workaround.  Reapply branch taken/not taken
relocs.
ld/
* emultempl/ppc32elf.em (ppc_after_open_output): Really enable
ppc476 workaround for ld -r.

bfd/ChangeLog
bfd/elf32-ppc.c
ld/ChangeLog
ld/emultempl/ppc32elf.em

index 39d9def..86f9d84 100644 (file)
@@ -1,3 +1,9 @@
+2014-02-17  Alan Modra  <amodra@gmail.com>
+
+       * elf32-ppc.c (ppc_elf_relocate_section): Move relocs on insns
+       patched for ppc476 workaround.  Reapply branch taken/not taken
+       relocs.
+
 2014-02-12  Alan Modra  <amodra@gmail.com>
 
        * elf32-ppc.c (ppc_elf_relax_section): Don't build long-branch
index d8e6108..1c8724f 100644 (file)
@@ -9230,13 +9230,14 @@ ppc_elf_relocate_section (bfd *output_bfd,
             the word alone.  */
          is_data = FALSE;
          lo = relocs;
-         hi = lo + input_section->reloc_count;
+         hi = relend;
+         rel = NULL;
          while (lo < hi)
            {
              rel = lo + (hi - lo) / 2;
              if (rel->r_offset < offset)
                lo = rel + 1;
-             else if (rel->r_offset > offset)
+             else if (rel->r_offset > offset + 3)
                hi = rel;
              else
                {
@@ -9285,12 +9286,53 @@ ppc_elf_relocate_section (bfd *output_bfd,
          patch_addr = (patch_addr + 15) & -16;
          patch_off = patch_addr - start_addr;
          bfd_put_32 (input_bfd, B + patch_off - offset, contents + offset);
+
+         if (rel != NULL
+             && rel->r_offset >= offset
+             && rel->r_offset < offset + 4)
+           {
+             /* If the insn we are patching had a reloc, adjust the
+                reloc r_offset so that the reloc applies to the moved
+                location.  This matters for -r and --emit-relocs.  */
+             if (rel + 1 != relend)
+               {
+                 Elf_Internal_Rela tmp = *rel;
+
+                 /* Keep the relocs sorted by r_offset.  */
+                 memmove (rel, rel + 1, (relend - (rel + 1)) * sizeof (*rel));
+                 relend[-1] = tmp;
+               }
+             relend[-1].r_offset += patch_off - offset;
+           }
+         else
+           rel = NULL;
+
          if ((insn & (0x3f << 26)) == (16u << 26) /* bc */
              && (insn & 2) == 0 /* relative */)
            {
              bfd_vma delta = ((insn & 0xfffc) ^ 0x8000) - 0x8000;
 
              delta += offset - patch_off;
+             if (info->relocatable && rel != NULL)
+               delta = 0;
+             if (!info->relocatable && rel != NULL)
+               {
+                 enum elf_ppc_reloc_type r_type;
+
+                 r_type = ELF32_R_TYPE (relend[-1].r_info);
+                 if (r_type == R_PPC_REL14_BRTAKEN)
+                   insn |= BRANCH_PREDICT_BIT;
+                 else if (r_type == R_PPC_REL14_BRNTAKEN)
+                   insn &= ~BRANCH_PREDICT_BIT;
+                 else
+                   BFD_ASSERT (r_type == R_PPC_REL14);
+
+                 if ((r_type == R_PPC_REL14_BRTAKEN
+                      || r_type == R_PPC_REL14_BRNTAKEN)
+                     && delta + 0x8000 < 0x10000
+                     && (bfd_signed_vma) delta < 0)
+                   insn ^= BRANCH_PREDICT_BIT;
+               }
              if (delta + 0x8000 < 0x10000)
                {
                  bfd_put_32 (input_bfd,
@@ -9304,6 +9346,13 @@ ppc_elf_relocate_section (bfd *output_bfd,
                }
              else
                {
+                 if (rel != NULL)
+                   {
+                     unsigned int r_sym = ELF32_R_SYM (relend[-1].r_info);
+
+                     relend[-1].r_offset += 8;
+                     relend[-1].r_info = ELF32_R_INFO (r_sym, R_PPC_REL24);
+                   }
                  bfd_put_32 (input_bfd,
                              (insn & ~0xfffc) | 8,
                              contents + patch_off);
index 9ab809e..38d81b0 100644 (file)
@@ -1,3 +1,8 @@
+2014-02-17  Alan Modra  <amodra@gmail.com>
+
+       * emultempl/ppc32elf.em (ppc_after_open_output): Really enable
+       ppc476 workaround for ld -r.
+
 2014-02-12  Alan Modra  <amodra@gmail.com>
 
        * emultempl/ppc32elf.em (pagesize): New static var.
index 8de230a..f5a3a70 100644 (file)
@@ -51,8 +51,6 @@ ppc_after_open_output (void)
   if (pagesize == 0)
     pagesize = config.commonpagesize;
   params.pagesize_p2 = bfd_log2 (pagesize);
-  if (link_info.relocatable)
-    params.ppc476_workaround = 0;
   ppc_elf_link_params (&link_info, &params);
 }