Fix alpha-elf relaxation
authorRichard Henderson <rth@twiddle.net>
Mon, 21 Apr 2014 15:14:18 +0000 (08:14 -0700)
committerRichard Henderson <rth@twiddle.net>
Mon, 21 Apr 2014 15:14:18 +0000 (08:14 -0700)
ld/
* emultempl/alphaelf.em (alpha_after_parse): Enable 2 relax passes.
bfd/
* elf64-alpha.c (elf64_alpha_size_got_sections): New may_merge
parameter; honor it and disable got merging when false.
(elf64_alpha_relax_got_load): Do not relax to GPREL relocs during
the first pass of relaxation.
(elf64_alpha_relax_with_lituse): Likewise.  Move relaxed relocs to
the end of the LITERAL+LITUSE chain.
(elf64_alpha_relax_section): Only process LITERAL relocs during the
second pass of relaxation.

bfd/ChangeLog
bfd/elf64-alpha.c
ld/ChangeLog
ld/emultempl/alphaelf.em

index 3a6b0d09a168e0c1bdf73895aaed854fb814bdb2..6ac864875a243e1e024acda1416a746622e7ccb3 100644 (file)
@@ -1,5 +1,14 @@
 2014-04-21  Richard Henderson  <rth@redhat.com>
 
+       * elf64-alpha.c (elf64_alpha_size_got_sections): New may_merge
+       parameter; honor it and disable got merging when false.
+       (elf64_alpha_relax_got_load): Do not relax to GPREL relocs during
+       the first pass of relaxation.
+       (elf64_alpha_relax_with_lituse): Likewise.  Move relaxed relocs to
+       the end of the LITERAL+LITUSE chain.
+       (elf64_alpha_relax_section): Only process LITERAL relocs during the
+       second pass of relaxation.
+
        * configure.ac (use_secureplt): Enable by default.
        * configure: Rebuild.
 
index c9876fbb01a9d27a006bbb9b08014c5d5f15c571..073e5782abd24cc9d4951f90b4d78a1660742f14 100644 (file)
@@ -2466,7 +2466,8 @@ elf64_alpha_calc_got_offsets (struct bfd_link_info *info)
 /* Constructs the gots.  */
 
 static bfd_boolean
-elf64_alpha_size_got_sections (struct bfd_link_info *info)
+elf64_alpha_size_got_sections (struct bfd_link_info *info,
+                               bfd_boolean may_merge)
 {
   bfd *i, *got_list, *cur_got_obj = NULL;
   struct alpha_elf_link_hash_table * htab;
@@ -2521,21 +2522,24 @@ elf64_alpha_size_got_sections (struct bfd_link_info *info)
   if (cur_got_obj == NULL)
     return FALSE;
 
-  i = alpha_elf_tdata(cur_got_obj)->got_link_next;
-  while (i != NULL)
+  if (may_merge)
     {
-      if (elf64_alpha_can_merge_gots (cur_got_obj, i))
+      i = alpha_elf_tdata(cur_got_obj)->got_link_next;
+      while (i != NULL)
        {
-         elf64_alpha_merge_gots (cur_got_obj, i);
+         if (elf64_alpha_can_merge_gots (cur_got_obj, i))
+           {
+             elf64_alpha_merge_gots (cur_got_obj, i);
 
-         alpha_elf_tdata(i)->got->size = 0;
-         i = alpha_elf_tdata(i)->got_link_next;
-         alpha_elf_tdata(cur_got_obj)->got_link_next = i;
-       }
-      else
-       {
-         cur_got_obj = i;
-         i = alpha_elf_tdata(i)->got_link_next;
+             alpha_elf_tdata(i)->got->size = 0;
+             i = alpha_elf_tdata(i)->got_link_next;
+             alpha_elf_tdata(cur_got_obj)->got_link_next = i;
+           }
+         else
+           {
+             cur_got_obj = i;
+             i = alpha_elf_tdata(i)->got_link_next;
+           }
        }
     }
 
@@ -2638,7 +2642,7 @@ elf64_alpha_always_size_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   if (htab == NULL)
     return FALSE;
 
-  if (!elf64_alpha_size_got_sections (info))
+  if (!elf64_alpha_size_got_sections (info, TRUE))
     return FALSE;
 
   /* Allocate space for all of the .got subsections.  */
@@ -3074,6 +3078,10 @@ elf64_alpha_relax_got_load (struct alpha_relax_info *info, bfd_vma symval,
        }
       else
        {
+         /* We may only create GPREL relocs during the second pass.  */
+         if (info->link_info->relax_pass == 0)
+           return TRUE;
+
          disp = symval - info->gp;
          insn = (OP_LDA << 26) | (insn & 0x03ff0000);
          r_type = R_ALPHA_GPREL16;
@@ -3214,21 +3222,27 @@ static bfd_boolean
 elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
                               bfd_vma symval, Elf_Internal_Rela *irel)
 {
-  Elf_Internal_Rela *urel, *irelend = info->relend;
-  int flags, count, i;
+  Elf_Internal_Rela *urel, *erel, *irelend = info->relend;
+  int flags;
   bfd_signed_vma disp;
   bfd_boolean fits16;
   bfd_boolean fits32;
   bfd_boolean lit_reused = FALSE;
   bfd_boolean all_optimized = TRUE;
+  bfd_boolean changed_contents;
+  bfd_boolean changed_relocs;
+  bfd_byte *contents = info->contents;
+  bfd *abfd = info->abfd;
+  bfd_vma sec_output_vma;
   unsigned int lit_insn;
+  int relax_pass;
 
-  lit_insn = bfd_get_32 (info->abfd, info->contents + irel->r_offset);
+  lit_insn = bfd_get_32 (abfd, contents + irel->r_offset);
   if (lit_insn >> 26 != OP_LDQ)
     {
       ((*_bfd_error_handler)
        ("%B: %A+0x%lx: warning: LITERAL relocation against unexpected insn",
-       info->abfd, info->sec,
+       abfd, info->sec,
        (unsigned long) irel->r_offset));
       return TRUE;
     }
@@ -3237,25 +3251,32 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
   if (alpha_elf_dynamic_symbol_p (&info->h->root, info->link_info))
     return TRUE;
 
+  changed_contents = info->changed_contents;
+  changed_relocs = info->changed_relocs;
+  sec_output_vma = info->sec->output_section->vma + info->sec->output_offset;
+  relax_pass = info->link_info->relax_pass;
+
   /* Summarize how this particular LITERAL is used.  */
-  for (urel = irel+1, flags = count = 0; urel < irelend; ++urel, ++count)
+  for (erel = irel+1, flags = 0; erel < irelend; ++erel)
     {
-      if (ELF64_R_TYPE (urel->r_info) != R_ALPHA_LITUSE)
+      if (ELF64_R_TYPE (erel->r_info) != R_ALPHA_LITUSE)
        break;
-      if (urel->r_addend <= 6)
-       flags |= 1 << urel->r_addend;
+      if (erel->r_addend <= 6)
+       flags |= 1 << erel->r_addend;
     }
 
   /* A little preparation for the loop...  */
   disp = symval - info->gp;
 
-  for (urel = irel+1, i = 0; i < count; ++i, ++urel)
+  for (urel = irel+1; urel < erel; ++urel)
     {
+      bfd_vma urel_r_offset = urel->r_offset;
       unsigned int insn;
       int insn_disp;
       bfd_signed_vma xdisp;
+      Elf_Internal_Rela nrel;
 
-      insn = bfd_get_32 (info->abfd, info->contents + urel->r_offset);
+      insn = bfd_get_32 (abfd, contents + urel_r_offset);
 
       switch (urel->r_addend)
        {
@@ -3267,6 +3288,13 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
          break;
 
        case LITUSE_ALPHA_BASE:
+         /* We may only create GPREL relocs during the second pass.  */
+         if (relax_pass == 0)
+           {
+             all_optimized = FALSE;
+             break;
+           }
+
          /* We can always optimize 16-bit displacements.  */
 
          /* Extract the displacement from the instruction, sign-extending
@@ -3284,14 +3312,20 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
              /* Take the op code and dest from this insn, take the base
                 register from the literal insn.  Leave the offset alone.  */
              insn = (insn & 0xffe0ffff) | (lit_insn & 0x001f0000);
-             urel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
-                                          R_ALPHA_GPREL16);
-             urel->r_addend = irel->r_addend;
-             info->changed_relocs = TRUE;
-
-             bfd_put_32 (info->abfd, (bfd_vma) insn,
-                         info->contents + urel->r_offset);
-             info->changed_contents = TRUE;
+             bfd_put_32 (abfd, (bfd_vma) insn, contents + urel_r_offset);
+             changed_contents = TRUE;
+
+             nrel = *urel;
+             nrel.r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
+                                         R_ALPHA_GPREL16);
+             nrel.r_addend = irel->r_addend;
+
+             /* As we adjust, move the reloc to the end so that we don't
+                break the LITERAL+LITUSE chain.  */
+             if (urel < --erel)
+               *urel-- = *erel;
+             *erel = nrel;
+             changed_relocs = TRUE;
            }
 
          /* If all mem+byte, we can optimize 32-bit mem displacements.  */
@@ -3302,15 +3336,16 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
              irel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
                                           R_ALPHA_GPRELHIGH);
              lit_insn = (OP_LDAH << 26) | (lit_insn & 0x03ff0000);
-             bfd_put_32 (info->abfd, (bfd_vma) lit_insn,
-                         info->contents + irel->r_offset);
+             bfd_put_32 (abfd, (bfd_vma) lit_insn, contents + irel->r_offset);
              lit_reused = TRUE;
-             info->changed_contents = TRUE;
+             changed_contents = TRUE;
 
+              /* Since all relocs must be optimized, don't bother swapping
+                this relocation to the end.  */
              urel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
                                           R_ALPHA_GPRELLOW);
              urel->r_addend = irel->r_addend;
-             info->changed_relocs = TRUE;
+             changed_relocs = TRUE;
            }
          else
            all_optimized = FALSE;
@@ -3324,14 +3359,19 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
 
          insn &= ~ (unsigned) 0x001ff000;
          insn |= ((symval & 7) << 13) | 0x1000;
-
-         urel->r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
-         urel->r_addend = 0;
-         info->changed_relocs = TRUE;
-
-         bfd_put_32 (info->abfd, (bfd_vma) insn,
-                     info->contents + urel->r_offset);
-         info->changed_contents = TRUE;
+         bfd_put_32 (abfd, (bfd_vma) insn, contents + urel_r_offset);
+         changed_contents = TRUE;
+
+         nrel = *urel;
+         nrel.r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
+         nrel.r_addend = 0;
+
+         /* As we adjust, move the reloc to the end so that we don't
+            break the LITERAL+LITUSE chain.  */
+         if (urel < --erel)
+           *urel-- = *erel;
+         *erel = nrel;
+         changed_relocs = TRUE;
          break;
 
        case LITUSE_ALPHA_JSR:
@@ -3348,18 +3388,15 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
            if (info->h && info->h->root.root.type == bfd_link_hash_undefweak)
              {
                insn |= 31 << 16;
-               bfd_put_32 (info->abfd, (bfd_vma) insn,
-                           info->contents + urel->r_offset);
+               bfd_put_32 (abfd, (bfd_vma) insn, contents + urel_r_offset);
 
-               info->changed_contents = TRUE;
+               changed_contents = TRUE;
                break;
              }
 
            /* If not zero, place to jump without needing pv.  */
            optdest = elf64_alpha_relax_opt_call (info, symval);
-           org = (info->sec->output_section->vma
-                  + info->sec->output_offset
-                  + urel->r_offset + 4);
+           org = sec_output_vma + urel_r_offset + 4;
            odisp = (optdest ? optdest : symval) - org;
 
            if (odisp >= -0x400000 && odisp < 0x400000)
@@ -3371,27 +3408,32 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
                  insn = (OP_BSR << 26) | (insn & 0x03e00000);
                else
                  insn = (OP_BR << 26) | (insn & 0x03e00000);
+               bfd_put_32 (abfd, (bfd_vma) insn, contents + urel_r_offset);
+               changed_contents = TRUE;
 
-               urel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
-                                            R_ALPHA_BRADDR);
-               urel->r_addend = irel->r_addend;
+               nrel = *urel;
+               nrel.r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
+                                           R_ALPHA_BRADDR);
+               nrel.r_addend = irel->r_addend;
 
                if (optdest)
-                 urel->r_addend += optdest - symval;
+                 nrel.r_addend += optdest - symval;
                else
                  all_optimized = FALSE;
 
-               bfd_put_32 (info->abfd, (bfd_vma) insn,
-                           info->contents + urel->r_offset);
-
                /* Kill any HINT reloc that might exist for this insn.  */
                xrel = (elf64_alpha_find_reloc_at_ofs
-                       (info->relocs, info->relend, urel->r_offset,
+                       (info->relocs, info->relend, urel_r_offset,
                         R_ALPHA_HINT));
                if (xrel)
                  xrel->r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
 
-               info->changed_contents = TRUE;
+               /* As we adjust, move the reloc to the end so that we don't
+                  break the LITERAL+LITUSE chain.  */
+               if (urel < --erel)
+                 *urel-- = *erel;
+               *erel = nrel;
+
                info->changed_relocs = TRUE;
              }
            else
@@ -3403,14 +3445,14 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
              {
                Elf_Internal_Rela *gpdisp
                  = (elf64_alpha_find_reloc_at_ofs
-                    (info->relocs, irelend, urel->r_offset + 4,
+                    (info->relocs, irelend, urel_r_offset + 4,
                      R_ALPHA_GPDISP));
                if (gpdisp)
                  {
-                   bfd_byte *p_ldah = info->contents + gpdisp->r_offset;
+                   bfd_byte *p_ldah = contents + gpdisp->r_offset;
                    bfd_byte *p_lda = p_ldah + gpdisp->r_addend;
-                   unsigned int ldah = bfd_get_32 (info->abfd, p_ldah);
-                   unsigned int lda = bfd_get_32 (info->abfd, p_lda);
+                   unsigned int ldah = bfd_get_32 (abfd, p_ldah);
+                   unsigned int lda = bfd_get_32 (abfd, p_lda);
 
                    /* Verify that the instruction is "ldah $29,0($26)".
                       Consider a function that ends in a noreturn call,
@@ -3419,12 +3461,12 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
                       In that case the insn would use $27 as the base.  */
                    if (ldah == 0x27ba0000 && lda == 0x23bd0000)
                      {
-                       bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP, p_ldah);
-                       bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP, p_lda);
+                       bfd_put_32 (abfd, (bfd_vma) INSN_UNOP, p_ldah);
+                       bfd_put_32 (abfd, (bfd_vma) INSN_UNOP, p_lda);
 
                        gpdisp->r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
-                       info->changed_contents = TRUE;
-                       info->changed_relocs = TRUE;
+                       changed_contents = TRUE;
+                       changed_relocs = TRUE;
                      }
                  }
              }
@@ -3433,6 +3475,9 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
        }
     }
 
+  /* If we reused the literal instruction, we must have optimized all.  */
+  BFD_ASSERT(!lit_reused || all_optimized);
+
   /* If all cases were optimized, we can reduce the use count on this
      got entry by one, possibly eliminating it.  */
   if (all_optimized)
@@ -3452,17 +3497,19 @@ elf64_alpha_relax_with_lituse (struct alpha_relax_info *info,
       if (!lit_reused)
        {
          irel->r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
-         info->changed_relocs = TRUE;
+         changed_relocs = TRUE;
 
-         bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP,
-                     info->contents + irel->r_offset);
-         info->changed_contents = TRUE;
+         bfd_put_32 (abfd, (bfd_vma) INSN_UNOP, contents + irel->r_offset);
+         changed_contents = TRUE;
        }
-
-      return TRUE;
     }
-  else
-    return elf64_alpha_relax_got_load (info, symval, irel, R_ALPHA_LITERAL);
+
+  info->changed_contents = changed_contents;
+  info->changed_relocs = changed_relocs;
+
+  if (all_optimized || relax_pass == 0)
+    return TRUE;
+  return elf64_alpha_relax_got_load (info, symval, irel, R_ALPHA_LITERAL);
 }
 
 static bfd_boolean
@@ -3724,6 +3771,7 @@ elf64_alpha_relax_section (bfd *abfd, asection *sec,
   struct alpha_elf_got_entry **local_got_entries;
   struct alpha_relax_info info;
   struct alpha_elf_link_hash_table * htab;
+  int relax_pass;
 
   htab = alpha_elf_hash_table (link_info);
   if (htab == NULL)
@@ -3739,15 +3787,19 @@ elf64_alpha_relax_section (bfd *abfd, asection *sec,
     return TRUE;
 
   BFD_ASSERT (is_alpha_elf (abfd));
+  relax_pass = link_info->relax_pass;
 
   /* Make sure our GOT and PLT tables are up-to-date.  */
   if (htab->relax_trip != link_info->relax_trip)
     {
       htab->relax_trip = link_info->relax_trip;
 
-      /* This should never fail after the initial round, since the only
-        error is GOT overflow, and relaxation only shrinks the table.  */
-      if (!elf64_alpha_size_got_sections (link_info))
+      /* This should never fail after the initial round, since the only error
+         is GOT overflow, and relaxation only shrinks the table.  However, we
+        may only merge got sections during the first pass.  If we merge
+        sections after we've created GPREL relocs, the GP for the merged
+        section backs up which may put the relocs out of range.  */
+      if (!elf64_alpha_size_got_sections (link_info, relax_pass == 0))
        abort ();
       if (elf_hash_table (link_info)->dynamic_sections_created)
        {
@@ -3802,24 +3854,21 @@ elf64_alpha_relax_section (bfd *abfd, asection *sec,
       unsigned long r_symndx = ELF64_R_SYM (irel->r_info);
 
       /* Early exit for unhandled or unrelaxable relocations.  */
-      switch (r_type)
-       {
-       case R_ALPHA_LITERAL:
-       case R_ALPHA_GPRELHIGH:
-       case R_ALPHA_GPRELLOW:
-       case R_ALPHA_GOTDTPREL:
-       case R_ALPHA_GOTTPREL:
-       case R_ALPHA_TLSGD:
-         break;
-
-       case R_ALPHA_TLSLDM:
-         /* The symbol for a TLSLDM reloc is ignored.  Collapse the
-             reloc to the STN_UNDEF (0) symbol so that they all match.  */
-         r_symndx = STN_UNDEF;
-         break;
-
-       default:
-         continue;
+      if (r_type != R_ALPHA_LITERAL)
+        {
+          /* We complete everything except LITERAL in the first pass.  */
+         if (relax_pass != 0)
+           continue;
+         if (r_type == R_ALPHA_TLSLDM)
+           {
+             /* The symbol for a TLSLDM reloc is ignored.  Collapse the
+                 reloc to the STN_UNDEF (0) symbol so that they all match.  */
+             r_symndx = STN_UNDEF;
+           }
+         else if (r_type != R_ALPHA_GOTDTPREL
+                  && r_type != R_ALPHA_GOTTPREL
+                  && r_type != R_ALPHA_TLSGD)
+           continue;
        }
 
       /* Get the value of the symbol referred to by the reloc.  */
index 7d0e63cd4e8639072cb332e2f055d811193b5f87..94380ccb25c1462d523f1d5519cd4404f28226d0 100644 (file)
@@ -1,3 +1,7 @@
+2014-04-21  Richard Henderson  <rth@redhat.com>
+
+       * emultempl/alphaelf.em (alpha_after_parse): Enable 2 relax passes.
+
 2014-04-16  Steve Ellcey  <sellcey@mips.com>
 
        * emultempl/elf32.em: Include safe-ctype.h.
index 091ef6608bbf15d640524554a571840424c081d6..a36fc7d1c25f7c6e429b89b22a5f14ca9d850437 100644 (file)
@@ -72,6 +72,7 @@ alpha_after_open (void)
 static void
 alpha_after_parse (void)
 {
+  link_info.relax_pass = 2;
   if (limit_32bit && !link_info.shared && !link_info.relocatable)
     lang_section_start (".interp",
                        exp_binop ('+',