PowerPC64 DT_RELR relative reloc addresses
authorAlan Modra <amodra@gmail.com>
Wed, 2 Mar 2022 13:34:57 +0000 (00:04 +1030)
committerAlan Modra <amodra@gmail.com>
Thu, 3 Mar 2022 02:17:40 +0000 (12:47 +1030)
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.

bfd/elf64-ppc.c

index dd79c05bb6161ef3bc5194a4dc043af27ceea5a4..89ce4cfba0dd07d4988558ca7f73b9e95ff32c6a 100644 (file)
@@ -3269,10 +3269,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];
@@ -13410,16 +13414,11 @@ maybe_strip_output (struct bfd_link_info *info, asection *isec)
     }
 }
 
-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)
     {
@@ -13427,16 +13426,51 @@ append_relr_off (struct ppc_link_hash_table *htab, bfd_vma off)
        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
+   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 @@ got_and_plt_relr_for_local_syms (struct bfd_link_info *info)
              && 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 @@ got_and_plt_relr_for_local_syms (struct bfd_link_info *info)
            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 @@ got_and_plt_relr_for_local_syms (struct bfd_link_info *info)
   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 @@ got_and_plt_relr (struct elf_link_hash_entry *h, void *inf)
              && 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 @@ got_and_plt_relr (struct elf_link_hash_entry *h, void *inf)
        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 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                                                          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 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
          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 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
          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 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                  base += 63 * 8;
                }
            }
+         free (relr_addr);
        }
 
       for (group = htab->group; group != NULL; group = group->next)
@@ -15184,17 +15206,21 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
       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 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
            {
              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 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
              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)