[COMMITTED] Fix alpha-elf relaxation

Richard Henderson rth@redhat.com
Mon Apr 21 15:58:00 GMT 2014


On 04/21/2014 08:57 AM, Richard Henderson wrote:
> Tested with a gcc bootstrap, which triggered the problem quite easily in
> cc1plus and f951, and committed.

... and now with the patch attached ...


r~

-------------- next part --------------
    Fix alpha-elf relaxation
    
    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.



diff --git a/bfd/elf64-alpha.c b/bfd/elf64-alpha.c
index c9876fb..073e578 100644
--- a/bfd/elf64-alpha.c
+++ b/bfd/elf64-alpha.c
@@ -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.  */
diff --git a/ld/emultempl/alphaelf.em b/ld/emultempl/alphaelf.em
index 091ef66..a36fc7d 100644
--- a/ld/emultempl/alphaelf.em
+++ b/ld/emultempl/alphaelf.em
@@ -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 ('+',


More information about the Binutils mailing list