Blob Blame History Raw
--- binutils.orig/bfd/elf64-ppc.c	2022-06-13 12:05:13.325289568 +0100
+++ binutils-2.38/bfd/elf64-ppc.c	2022-06-13 12:05:36.080143584 +0100
@@ -3270,10 +3270,14 @@ struct ppc_link_hash_table
   /* 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];
@@ -13419,16 +13423,11 @@ maybe_strip_output (struct bfd_link_info
     }
 }
 
-static int
-compare_relr_address (const void *arg1, const void *arg2)
-{
-  bfd_vma a = *(bfd_vma *) arg1;
-  bfd_vma b = *(bfd_vma *) arg2;
-  return a < b ? -1 : a > b ? 1 : 0;
-}
+/* 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, bfd_vma off)
+append_relr_off (struct ppc_link_hash_table *htab, asection *sec, bfd_vma off)
 {
   if (htab->relr_count >= htab->relr_alloc)
     {
@@ -13436,16 +13435,51 @@ append_relr_off (struct ppc_link_hash_ta
 	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)
+      htab->relr = bfd_realloc (htab->relr,
+				htab->relr_alloc * sizeof (*htab->relr));
+      if (htab->relr == NULL)
 	return false;
     }
-  htab->relr_addr[htab->relr_count++] = off;
+  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)
+{
+  bfd_vma a = *(bfd_vma *) arg1;
+  bfd_vma b = *(bfd_vma *) arg2;
+  return a < b ? -1 : a > b ? 1 : 0;
+}
+
+/* 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)
+{
+  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
+   input files, stashing info about needed relative relocs.  */
+
 static bool
 got_and_plt_relr_for_local_syms (struct bfd_link_info *info)
 {
@@ -13493,10 +13527,7 @@ got_and_plt_relr_for_local_syms (struct
 	      && 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;
@@ -13511,10 +13542,7 @@ got_and_plt_relr_for_local_syms (struct
 	    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);
@@ -13534,6 +13562,9 @@ got_and_plt_relr_for_local_syms (struct
   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)
 {
@@ -13565,10 +13596,7 @@ got_and_plt_relr (struct elf_link_hash_e
 	      && 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;
@@ -13580,10 +13608,7 @@ got_and_plt_relr (struct elf_link_hash_e
 	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;
@@ -13861,9 +13886,7 @@ ppc64_elf_size_stubs (struct bfd_link_in
 							  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;
 		    }
@@ -14214,9 +14237,7 @@ ppc64_elf_size_stubs (struct bfd_link_in
 	  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))
@@ -14225,28 +14246,28 @@ ppc64_elf_size_stubs (struct bfd_link_in
 	  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;
@@ -14254,6 +14275,7 @@ ppc64_elf_size_stubs (struct bfd_link_in
 		  base += 63 * 8;
 		}
 	    }
+	  free (relr_addr);
 	}
 
       for (group = htab->group; group != NULL; group = group->next)
@@ -15193,17 +15215,21 @@ ppc64_elf_build_stubs (struct bfd_link_i
       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++;
@@ -15213,10 +15239,10 @@ ppc64_elf_build_stubs (struct bfd_link_i
 	    {
 	      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)
@@ -15226,6 +15252,7 @@ ppc64_elf_build_stubs (struct bfd_link_i
 	      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)