PowerPC64 DT_RELR relative reloc addresses

Section addresses can change between ppc64_elf_size_stubs and
ppc64_elf_build_stubs due to .eh_frame editing.  The idea of stashing
r_offset final addresses calculated in ppc64_elf_size_stubs for use by
ppc64_elf_build_stubs was never a good idea.  Instead, we need to keep
section/offset pairs.

	* elf64-ppc.c (struct ppc_link_hash_table): Delete relr_addr.
	Add relr section/offset array.
	(append_relr_off): Rewrite.  Update all callers.
	(sort_relr): New function.
	(ppc64_elf_size_stubs): Adjust to suit new relative reloc stash.
	(ppc64_elf_build_stubs): Likewise.
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index dd79c05..89ce4cf 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -3269,10 +3269,14 @@
   /* The size of reliplt used by got entry relocs.  */
   bfd_size_type got_reli_size;
 
-  /* DT_RELR array of r_offset.  */
+  /* DT_RELR array of section/r_offset.  */
   size_t relr_alloc;
   size_t relr_count;
-  bfd_vma *relr_addr;
+  struct
+  {
+    asection *sec;
+    bfd_vma off;
+  } *relr;
 
   /* Statistics.  */
   unsigned long stub_count[ppc_stub_save_res];
@@ -13410,6 +13414,31 @@
     }
 }
 
+/* Stash R_PPC64_RELATIVE reloc at input section SEC, r_offset OFF to
+   the array of such relocs.  */
+
+static bool
+append_relr_off (struct ppc_link_hash_table *htab, asection *sec, bfd_vma off)
+{
+  if (htab->relr_count >= htab->relr_alloc)
+    {
+      if (htab->relr_alloc == 0)
+	htab->relr_alloc = 4096;
+      else
+	htab->relr_alloc *= 2;
+      htab->relr = bfd_realloc (htab->relr,
+				htab->relr_alloc * sizeof (*htab->relr));
+      if (htab->relr == NULL)
+	return false;
+    }
+  htab->relr[htab->relr_count].sec = sec;
+  htab->relr[htab->relr_count].off = off;
+  htab->relr_count++;
+  return true;
+}
+
+/* qsort comparator for bfd_vma args.  */
+
 static int
 compare_relr_address (const void *arg1, const void *arg2)
 {
@@ -13418,25 +13447,30 @@
   return a < b ? -1 : a > b ? 1 : 0;
 }
 
-static bool
-append_relr_off (struct ppc_link_hash_table *htab, bfd_vma off)
+/* Produce a malloc'd sorted array of reloc addresses from the info
+   stored by append_relr_off.  */
+
+static bfd_vma *
+sort_relr (struct ppc_link_hash_table *htab)
 {
-  if (htab->relr_count >= htab->relr_alloc)
-    {
-      if (htab->relr_alloc == 0)
-	htab->relr_alloc = 4096;
-      else
-	htab->relr_alloc *= 2;
-      htab->relr_addr
-	= bfd_realloc (htab->relr_addr,
-		       htab->relr_alloc * sizeof (htab->relr_addr[0]));
-      if (htab->relr_addr == NULL)
-	return false;
-    }
-  htab->relr_addr[htab->relr_count++] = off;
-  return true;
+  bfd_vma *addr = bfd_malloc (htab->relr_count * sizeof (*addr));
+  if (addr == NULL)
+    return NULL;
+
+  for (size_t i = 0; i < htab->relr_count; i++)
+    addr[i] = (htab->relr[i].sec->output_section->vma
+	       + htab->relr[i].sec->output_offset
+	       + htab->relr[i].off);
+
+  if (htab->relr_count > 1)
+    qsort (addr, htab->relr_count, sizeof (*addr), compare_relr_address);
+
+  return addr;
 }
 
+/* Look over GOT and PLT entries saved on elf_local_got_ents for all
+   all input files, stashing info about needed relative relocs.  */
+
 static bool
 got_and_plt_relr_for_local_syms (struct bfd_link_info *info)
 {
@@ -13484,10 +13518,7 @@
 	      && isym->st_shndx != SHN_ABS)
 	    {
 	      asection *got = ppc64_elf_tdata (gent->owner)->got;
-	      bfd_vma r_offset = (got->output_section->vma
-				  + got->output_offset
-				  + gent->got.offset);
-	      if (!append_relr_off (htab, r_offset))
+	      if (!append_relr_off (htab, got, gent->got.offset))
 		{
 		  htab->stub_error = true;
 		  return false;
@@ -13502,10 +13533,7 @@
 	    if (pent->plt.offset != (bfd_vma) -1
 		&& ELF_ST_TYPE (isym->st_info) != STT_GNU_IFUNC)
 	      {
-		bfd_vma r_offset = (pent->plt.offset
-				    + htab->pltlocal->output_offset
-				    + htab->pltlocal->output_section->vma);
-		if (!append_relr_off (htab, r_offset))
+		if (!append_relr_off (htab, htab->pltlocal, pent->plt.offset))
 		  {
 		    if (symtab_hdr->contents != (unsigned char *) local_syms)
 		      free (local_syms);
@@ -13525,6 +13553,9 @@
   return true;
 }
 
+/* Stash info about needed GOT and PLT entry relative relocs for
+   global symbol H.  */
+
 static bool
 got_and_plt_relr (struct elf_link_hash_entry *h, void *inf)
 {
@@ -13556,10 +13587,7 @@
 	      && gent->got.offset != (bfd_vma) -1)
 	    {
 	      asection *got = ppc64_elf_tdata (gent->owner)->got;
-	      bfd_vma r_offset = (got->output_section->vma
-				  + got->output_offset
-				  + gent->got.offset);
-	      if (!append_relr_off (htab, r_offset))
+	      if (!append_relr_off (htab, got, gent->got.offset))
 		{
 		  htab->stub_error = true;
 		  return false;
@@ -13571,10 +13599,7 @@
 	for (pent = h->plt.plist; pent != NULL; pent = pent->next)
 	  if (pent->plt.offset != (bfd_vma) -1)
 	    {
-	      bfd_vma r_offset = (htab->pltlocal->output_section->vma
-				  + htab->pltlocal->output_offset
-				  + pent->plt.offset);
-	      if (!append_relr_off (htab, r_offset))
+	      if (!append_relr_off (htab, htab->pltlocal, pent->plt.offset))
 		{
 		  htab->stub_error = true;
 		  return false;
@@ -13852,9 +13877,7 @@
 							  irela->r_offset);
 		      if (r_offset >= (bfd_vma) -2)
 			continue;
-		      r_offset += (section->output_section->vma
-				   + section->output_offset);
-		      if (!append_relr_off (htab, r_offset))
+		      if (!append_relr_off (htab, section, r_offset))
 			goto error_ret_free_internal;
 		      continue;
 		    }
@@ -14205,9 +14228,7 @@
 	  bfd_vma r_offset;
 
 	  for (r_offset = 0; r_offset < htab->brlt->size; r_offset += 8)
-	    if (!append_relr_off (htab, (r_offset
-					 + htab->brlt->output_section->vma
-					 + htab->brlt->output_offset)))
+	    if (!append_relr_off (htab, htab->brlt, r_offset))
 	      return false;
 
 	  if (!got_and_plt_relr_for_local_syms (info))
@@ -14216,28 +14237,28 @@
 	  if (htab->stub_error)
 	    return false;
 
-	  if (htab->relr_count > 1)
-	    qsort (htab->relr_addr, htab->relr_count, sizeof (*htab->relr_addr),
-		   compare_relr_address);
+	  bfd_vma *relr_addr = sort_relr (htab);
+	  if (htab->relr_count != 0 && relr_addr == NULL)
+	    return false;
 
 	  size_t i = 0;
 	  while (i < htab->relr_count)
 	    {
-	      bfd_vma base = htab->relr_addr[i];
+	      bfd_vma base = relr_addr[i];
 	      htab->elf.srelrdyn->size += 8;
 	      i++;
 	      /* Handle possible duplicate address.  This can happen
 		 as sections increase in size when adding stubs.  */
 	      while (i < htab->relr_count
-		     && htab->relr_addr[i] == base)
+		     && relr_addr[i] == base)
 		i++;
 	      base += 8;
 	      while (1)
 		{
 		  size_t start_i = i;
 		  while (i < htab->relr_count
-			 && htab->relr_addr[i] - base < 63 * 8
-			 && (htab->relr_addr[i] - base) % 8 == 0)
+			 && relr_addr[i] - base < 63 * 8
+			 && (relr_addr[i] - base) % 8 == 0)
 		    i++;
 		  if (i == start_i)
 		    break;
@@ -14245,6 +14266,7 @@
 		  base += 63 * 8;
 		}
 	    }
+	  free (relr_addr);
 	}
 
       for (group = htab->group; group != NULL; group = group->next)
@@ -15184,17 +15206,21 @@
       if (htab->elf.srelrdyn->contents == NULL)
 	return false;
 
+      bfd_vma *relr_addr = sort_relr (htab);
+      if (htab->relr_count != 0 && relr_addr == NULL)
+	return false;
+
       size_t i = 0;
       bfd_byte *loc = htab->elf.srelrdyn->contents;
       while (i < htab->relr_count)
 	{
-	  bfd_vma base = htab->relr_addr[i];
+	  bfd_vma base = relr_addr[i];
 	  BFD_ASSERT (base % 2 == 0);
 	  bfd_put_64 (htab->elf.dynobj, base, loc);
 	  loc += 8;
 	  i++;
 	  while (i < htab->relr_count
-		 && htab->relr_addr[i] == base)
+		 && relr_addr[i] == base)
 	    {
 	      htab->stub_error = true;
 	      i++;
@@ -15204,10 +15230,10 @@
 	    {
 	      bfd_vma bits = 0;
 	      while (i < htab->relr_count
-		     && htab->relr_addr[i] - base < 63 * 8
-		     && (htab->relr_addr[i] - base) % 8 == 0)
+		     && relr_addr[i] - base < 63 * 8
+		     && (relr_addr[i] - base) % 8 == 0)
 		{
-		  bits |= (bfd_vma) 1 << ((htab->relr_addr[i] - base) / 8);
+		  bits |= (bfd_vma) 1 << ((relr_addr[i] - base) / 8);
 		  i++;
 		}
 	      if (bits == 0)
@@ -15217,6 +15243,7 @@
 	      base += 63 * 8;
 	    }
 	}
+      free (relr_addr);
       /* Pad any excess with 1's, a do-nothing encoding.  */
       while ((size_t) (loc - htab->elf.srelrdyn->contents)
 	     < htab->elf.srelrdyn->size)