PowerPC64 DT_RELR
authorAlan Modra <amodra@gmail.com>
Thu, 6 Jan 2022 21:19:16 +0000 (07:49 +1030)
committerAlan Modra <amodra@gmail.com>
Tue, 18 Jan 2022 00:48:45 +0000 (11:18 +1030)
PowerPC64 takes a more traditional approach to DT_RELR than x86.  Count
relative relocs in check_relocs, allocate space for them and output in
the usual places but not doing so when enable_dt_relr.  DT_RELR is
sized in the existing ppc stub relaxation machinery, run via the
linker's ldemul_after_allocation hook.  DT_RELR is output in the same
function that writes ppc stubs, run via ldemul_finish.

This support should be considered experimental.

bfd/
* elf64-ppc.c (struct ppc_local_dyn_relocs): Renamed from
ppc_dyn_relocs.  Add rel_count field.  Update uses.
(struct ppc_dyn_relocs): New.  Replace all uses of elf_dyn_relocs.
(struct ppc_link_hash_table): Add relr_alloc, relr_count and
relr_addr.
(ppc64_elf_copy_indirect_symbol): Merge rel_count.
(ppc64_elf_check_relocs): Init rel_count for global and local syms.
(dec_dynrel_count): Change r_info param to reloc pointer.  Update
all callers.  Handle decrementing rel_count.
(allocate_got): Don't allocate space for relative relocs when
enable_dt_relr.
(allocate_dynrelocs): Likewise.
(ppc64_elf_size_dynamic_sections): Likewise.  Handle srelrdyn.
(ppc_build_one_stub): Don't emit relative relocs on .branch_lt.
(compare_relr_address, append_relr_off): New functions.
(got_and_plt_relr_for_local_syms, got_and_plt_relr): Likewise.
(ppc64_elf_size_stubs): Size .relr.syn.
(ppc64_elf_build_stubs): Emit .relr.dyn.
(build_global_entry_stubs_and_plt): Don't output relative relocs
when enable_dt_relr.
(write_plt_relocs_for_local_syms): Likewise.
(ppc64_elf_relocate_section): Likewise.
binutils/
* testsuite/lib/binutils-common.exp (supports_dt_relr): Add
powerpc64.
ld/
* emulparams/elf64ppc.sh: Source dt-relr.sh.
* testsuite/ld-elf/dt-relr-2b.d: Adjust for powerpc.
* testsuite/ld-elf/dt-relr-2c.d: Likewise.
* testsuite/ld-elf/dt-relr-2d.d: Likewise.
* testsuite/ld-elf/dt-relr-2e.d: Likewise.

bfd/elf64-ppc.c
binutils/testsuite/lib/binutils-common.exp
ld/emulparams/elf64ppc.sh
ld/testsuite/ld-elf/dt-relr-2b.d
ld/testsuite/ld-elf/dt-relr-2c.d
ld/testsuite/ld-elf/dt-relr-2d.d
ld/testsuite/ld-elf/dt-relr-2e.d

index ea9e60217bc353bc77ad6676b669a195aa4be61c..0f945797b492ddd5ee70bfc5944e40ec884296a1 100644 (file)
@@ -3094,7 +3094,7 @@ struct ppc_branch_hash_entry
   unsigned int iter;
 };
 
-/* Used to track dynamic relocations for local symbols.  */
+/* Used to track dynamic relocations.  */
 struct ppc_dyn_relocs
 {
   struct ppc_dyn_relocs *next;
@@ -3103,7 +3103,27 @@ struct ppc_dyn_relocs
   asection *sec;
 
   /* Total number of relocs copied for the input section.  */
-  unsigned int count : 31;
+  unsigned int count;
+
+  /* Number of pc-relative relocs copied for the input section.  */
+  unsigned int pc_count;
+
+  /* Number of relocs that might become R_PPC64_RELATIVE.  */
+  unsigned int rel_count;
+};
+
+struct ppc_local_dyn_relocs
+{
+  struct ppc_local_dyn_relocs *next;
+
+  /* The input section of the reloc.  */
+  asection *sec;
+
+  /* Total number of relocs copied for the input section.  */
+  unsigned int count;
+
+  /* Number of relocs that might become R_PPC64_RELATIVE.  */
+  unsigned int rel_count : 31;
 
   /* Whether this entry is for STT_GNU_IFUNC symbols.  */
   unsigned int ifunc : 1;
@@ -3250,6 +3270,11 @@ 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.  */
+  size_t relr_alloc;
+  size_t relr_count;
+  bfd_vma *relr_addr;
+
   /* Statistics.  */
   unsigned long stub_count[ppc_stub_save_res];
 
@@ -4068,27 +4093,32 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info,
     {
       if (dir->dyn_relocs != NULL)
        {
-         struct elf_dyn_relocs **pp;
-         struct elf_dyn_relocs *p;
+         struct ppc_dyn_relocs **pp;
+         struct ppc_dyn_relocs *p;
 
          /* Add reloc counts against the indirect sym to the direct sym
             list.  Merge any entries against the same section.  */
-         for (pp = &ind->dyn_relocs; (p = *pp) != NULL; )
+         for (pp = (struct ppc_dyn_relocs **) &ind->dyn_relocs;
+              (p = *pp) != NULL;
+              )
            {
-             struct elf_dyn_relocs *q;
+             struct ppc_dyn_relocs *q;
 
-             for (q = dir->dyn_relocs; q != NULL; q = q->next)
+             for (q = (struct ppc_dyn_relocs *) dir->dyn_relocs;
+                  q != NULL;
+                  q = q->next)
                if (q->sec == p->sec)
                  {
-                   q->pc_count += p->pc_count;
                    q->count += p->count;
+                   q->pc_count += p->pc_count;
+                   q->rel_count += p->rel_count;
                    *pp = p->next;
                    break;
                  }
              if (q == NULL)
                pp = &p->next;
            }
-         *pp = dir->dyn_relocs;
+         *pp = (struct ppc_dyn_relocs *) dir->dyn_relocs;
        }
 
       dir->dyn_relocs = ind->dyn_relocs;
@@ -5337,10 +5367,10 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                 relocations we need for this symbol.  */
              if (h != NULL)
                {
-                 struct elf_dyn_relocs *p;
-                 struct elf_dyn_relocs **head;
+                 struct ppc_dyn_relocs *p;
+                 struct ppc_dyn_relocs **head;
 
-                 head = &h->dyn_relocs;
+                 head = (struct ppc_dyn_relocs **) &h->dyn_relocs;
                  p = *head;
                  if (p == NULL || p->sec != sec)
                    {
@@ -5352,18 +5382,25 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                      p->sec = sec;
                      p->count = 0;
                      p->pc_count = 0;
+                     p->rel_count = 0;
                    }
                  p->count += 1;
                  if (!must_be_dyn_reloc (info, r_type))
                    p->pc_count += 1;
+                 if ((r_type == R_PPC64_ADDR64 || r_type == R_PPC64_TOC)
+                     && rel->r_offset % 2 == 0
+                     && sec->alignment_power != 0
+                     && ((!NO_OPD_RELOCS && is_opd)
+                         || (!ifunc && SYMBOL_REFERENCES_LOCAL (info, h))))
+                   p->rel_count += 1;
                }
              else
                {
                  /* Track dynamic relocs needed for local syms too.
                     We really need local syms available to do this
                     easily.  Oh well.  */
-                 struct ppc_dyn_relocs *p;
-                 struct ppc_dyn_relocs **head;
+                 struct ppc_local_dyn_relocs *p;
+                 struct ppc_local_dyn_relocs **head;
                  bool is_ifunc;
                  asection *s;
                  void *vpp;
@@ -5379,7 +5416,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                    s = sec;
 
                  vpp = &elf_section_data (s)->local_dynrel;
-                 head = (struct ppc_dyn_relocs **) vpp;
+                 head = (struct ppc_local_dyn_relocs **) vpp;
                  is_ifunc = ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC;
                  p = *head;
                  if (p != NULL && p->sec == sec && p->ifunc != is_ifunc)
@@ -5392,10 +5429,16 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                      p->next = *head;
                      *head = p;
                      p->sec = sec;
-                     p->ifunc = is_ifunc;
                      p->count = 0;
+                     p->rel_count = 0;
+                     p->ifunc = is_ifunc;
                    }
                  p->count += 1;
+                 if ((r_type == R_PPC64_ADDR64 || r_type == R_PPC64_TOC)
+                     && rel->r_offset % 2 == 0
+                     && sec->alignment_power != 0
+                     && ((!NO_OPD_RELOCS && is_opd) || !is_ifunc))
+                   p->rel_count += 1;
                }
            }
          break;
@@ -6576,9 +6619,9 @@ alias_readonly_dynrelocs (struct elf_link_hash_entry *h)
 static bool
 pc_dynrelocs (struct ppc_link_hash_entry *eh)
 {
-  struct elf_dyn_relocs *p;
+  struct ppc_dyn_relocs *p;
 
-  for (p = eh->elf.dyn_relocs; p != NULL; p = p->next)
+  for (p = (struct ppc_dyn_relocs *) eh->elf.dyn_relocs; p != NULL; p = p->next)
     if (p->pc_count != 0)
       return true;
   return false;
@@ -7113,7 +7156,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
    have already been determined.  */
 
 static bool
-dec_dynrel_count (bfd_vma r_info,
+dec_dynrel_count (const Elf_Internal_Rela *rel,
                  asection *sec,
                  struct bfd_link_info *info,
                  Elf_Internal_Sym **local_syms,
@@ -7125,7 +7168,7 @@ dec_dynrel_count (bfd_vma r_info,
 
   /* Can this reloc be dynamic?  This switch, and later tests here
      should be kept in sync with the code in check_relocs.  */
-  r_type = ELF64_R_TYPE (r_info);
+  r_type = ELF64_R_TYPE (rel->r_info);
   switch (r_type)
     {
     default:
@@ -7199,7 +7242,7 @@ dec_dynrel_count (bfd_vma r_info,
       unsigned long r_symndx;
       bfd *ibfd = sec->owner;
 
-      r_symndx = ELF64_R_SYM (r_info);
+      r_symndx = ELF64_R_SYM (rel->r_info);
       if (!get_sym_h (&h, &sym, &sym_sec, NULL, local_syms, r_symndx, ibfd))
        return false;
     }
@@ -7222,9 +7265,9 @@ dec_dynrel_count (bfd_vma r_info,
 
   if (h != NULL)
     {
-      struct elf_dyn_relocs *p;
-      struct elf_dyn_relocs **pp;
-      pp = &h->dyn_relocs;
+      struct ppc_dyn_relocs *p;
+      struct ppc_dyn_relocs **pp;
+      pp = (struct ppc_dyn_relocs **) &h->dyn_relocs;
 
       /* elf_gc_sweep may have already removed all dyn relocs associated
         with local syms for a given section.  Also, symbol flags are
@@ -7239,6 +7282,14 @@ dec_dynrel_count (bfd_vma r_info,
            {
              if (!must_be_dyn_reloc (info, r_type))
                p->pc_count -= 1;
+             if ((r_type == R_PPC64_ADDR64 || r_type == R_PPC64_TOC)
+                 && rel->r_offset % 2 == 0
+                 && sec->alignment_power != 0
+                 && ((!NO_OPD_RELOCS
+                      && ppc64_elf_section_data (sec)->sec_type == sec_opd)
+                     || (h->type != STT_GNU_IFUNC
+                         && SYMBOL_REFERENCES_LOCAL (info, h))))
+               p->rel_count -= 1;
              p->count -= 1;
              if (p->count == 0)
                *pp = p->next;
@@ -7249,8 +7300,8 @@ dec_dynrel_count (bfd_vma r_info,
     }
   else
     {
-      struct ppc_dyn_relocs *p;
-      struct ppc_dyn_relocs **pp;
+      struct ppc_local_dyn_relocs *p;
+      struct ppc_local_dyn_relocs **pp;
       void *vpp;
       bool is_ifunc;
 
@@ -7260,7 +7311,7 @@ dec_dynrel_count (bfd_vma r_info,
        sym_sec = sec;
 
       vpp = &elf_section_data (sym_sec)->local_dynrel;
-      pp = (struct ppc_dyn_relocs **) vpp;
+      pp = (struct ppc_local_dyn_relocs **) vpp;
 
       if (*pp == NULL && info->gc_sections)
        return true;
@@ -7270,6 +7321,13 @@ dec_dynrel_count (bfd_vma r_info,
        {
          if (p->sec == sec && p->ifunc == is_ifunc)
            {
+             if ((r_type == R_PPC64_ADDR64 || r_type == R_PPC64_TOC)
+                 && rel->r_offset % 2 == 0
+                 && sec->alignment_power != 0
+                 && ((!NO_OPD_RELOCS
+                      && ppc64_elf_section_data (sec)->sec_type == sec_opd)
+                     || !is_ifunc))
+               p->rel_count -= 1;
              p->count -= 1;
              if (p->count == 0)
                *pp = p->next;
@@ -7567,7 +7625,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
                  else
                    while (1)
                      {
-                       if (!dec_dynrel_count (rel->r_info, sec, info,
+                       if (!dec_dynrel_count (rel, sec, info,
                                               NULL, h, sym))
                          goto error_ret;
 
@@ -8587,13 +8645,13 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
                    {
                      /* If we got rid of a DTPMOD/DTPREL reloc pair then
                         we'll lose one or two dyn relocs.  */
-                     if (!dec_dynrel_count (rel->r_info, sec, info,
+                     if (!dec_dynrel_count (rel, sec, info,
                                             NULL, h, sym))
                        return false;
 
                      if (tls_set == (TLS_EXPLICIT | TLS_GD))
                        {
-                         if (!dec_dynrel_count ((rel + 1)->r_info, sec, info,
+                         if (!dec_dynrel_count (rel + 1, sec, info,
                                                 NULL, h, sym))
                            return false;
                        }
@@ -9419,7 +9477,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                    wrel->r_addend = rel->r_addend;
                    ++wrel;
                  }
-               else if (!dec_dynrel_count (rel->r_info, toc, info,
+               else if (!dec_dynrel_count (rel, toc, info,
                                            &local_syms, NULL, NULL))
                  goto error_ret;
 
@@ -9720,9 +9778,10 @@ allocate_got (struct elf_link_hash_entry *h,
       htab->got_reli_size += rentsize;
     }
   else if (((bfd_link_pic (info)
-            && !(gent->tls_type != 0
-                 && bfd_link_executable (info)
-                 && SYMBOL_REFERENCES_LOCAL (info, h)))
+            && (gent->tls_type == 0
+                ? !info->enable_dt_relr
+                : !(bfd_link_executable (info)
+                    && SYMBOL_REFERENCES_LOCAL (info, h))))
            || (htab->elf.dynamic_sections_created
                && h->dynindx != -1
                && !SYMBOL_REFERENCES_LOCAL (info, h)))
@@ -9884,7 +9943,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 
   if (h->dyn_relocs != NULL)
     {
-      struct elf_dyn_relocs *p, **pp;
+      struct ppc_dyn_relocs *p, **pp;
 
       /* In the shared -Bsymbolic case, discard space allocated for
         dynamic pc-relative relocs against symbols which turn out to
@@ -9902,7 +9961,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
             avoid writing weird assembly.  */
          if (SYMBOL_CALLS_LOCAL (info, h))
            {
-             for (pp = &h->dyn_relocs; (p = *pp) != NULL; )
+             for (pp = (struct ppc_dyn_relocs **) &h->dyn_relocs;
+                  (p = *pp) != NULL;
+                  )
                {
                  p->count -= p->pc_count;
                  p->pc_count = 0;
@@ -9948,12 +10009,16 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
        }
 
       /* Finally, allocate space.  */
-      for (p = h->dyn_relocs; p != NULL; p = p->next)
+      for (p = (struct ppc_dyn_relocs *) h->dyn_relocs; p != NULL; p = p->next)
        {
+         unsigned int count;
          asection *sreloc = elf_section_data (p->sec)->sreloc;
          if (eh->elf.type == STT_GNU_IFUNC)
            sreloc = htab->elf.irelplt;
-         sreloc->size += p->count * sizeof (Elf64_External_Rela);
+         count = p->count;
+         if (info->enable_dt_relr)
+           count -= p->rel_count;
+         sreloc->size += count * sizeof (Elf64_External_Rela);
        }
     }
 
@@ -9994,7 +10059,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                    s = htab->pltlocal;
                    pent->plt.offset = s->size;
                    s->size += LOCAL_PLT_ENTRY_SIZE (htab);
-                   s = bfd_link_pic (info) ? htab->relpltlocal : NULL;
+                   s = NULL;
+                   if (bfd_link_pic (info)
+                       && !(info->enable_dt_relr && !htab->opd_abi))
+                     s = htab->relpltlocal;
                  }
              }
            else
@@ -10180,7 +10248,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
 
       for (s = ibfd->sections; s != NULL; s = s->next)
        {
-         struct ppc_dyn_relocs *p;
+         struct ppc_local_dyn_relocs *p;
 
          for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next)
            {
@@ -10194,10 +10262,16 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
                }
              else if (p->count != 0)
                {
-                 asection *srel = elf_section_data (p->sec)->sreloc;
+                 unsigned int count;
+                 asection *srel;
+
+                 count = p->count;
+                 if (info->enable_dt_relr)
+                   count -= p->rel_count;
+                 srel = elf_section_data (p->sec)->sreloc;
                  if (p->ifunc)
                    srel = htab->elf.irelplt;
-                 srel->size += p->count * sizeof (Elf64_External_Rela);
+                 srel->size += count * sizeof (Elf64_External_Rela);
                  if ((p->sec->output_section->flags & SEC_READONLY) != 0)
                    info->flags |= DF_TEXTREL;
                }
@@ -10342,7 +10416,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
       if ((s->flags & SEC_LINKER_CREATED) == 0)
        continue;
 
-      if (s == htab->brlt || s == htab->relbrlt)
+      if (s == htab->brlt || s == htab->relbrlt || s == htab->elf.srelrdyn)
        /* These haven't been allocated yet;  don't strip.  */
        continue;
       else if (s == htab->elf.sgot
@@ -11693,7 +11767,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
        {
          br_entry->iter = 0;
 
-         if (htab->relbrlt != NULL)
+         if (htab->relbrlt != NULL && !info->enable_dt_relr)
            {
              /* Create a reloc for the branch lookup table entry.  */
              Elf_Internal_Rela rela;
@@ -12198,7 +12272,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
              br_entry->offset = htab->brlt->size;
              htab->brlt->size += 8;
 
-             if (htab->relbrlt != NULL)
+             if (htab->relbrlt != NULL && !info->enable_dt_relr)
                htab->relbrlt->size += sizeof (Elf64_External_Rela);
              else if (info->emitrelocations)
                {
@@ -13293,6 +13367,174 @@ 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;
+}
+
+static bool
+append_relr_off (struct ppc_link_hash_table *htab, 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_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;
+}
+
+static bool
+got_and_plt_relr_for_local_syms (struct bfd_link_info *info)
+{
+  struct ppc_link_hash_table *htab = ppc_hash_table (info);
+  bfd *ibfd;
+
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      struct got_entry **lgot_ents, **lgot, **end_lgot_ents;
+      struct plt_entry **local_plt, **lplt, **end_local_plt;
+      Elf_Internal_Shdr *symtab_hdr;
+      bfd_size_type locsymcount;
+      Elf_Internal_Sym *local_syms = NULL;
+      struct plt_entry *pent;
+      struct got_entry *gent;
+
+      if (!is_ppc64_elf (ibfd))
+       continue;
+
+      lgot_ents = elf_local_got_ents (ibfd);
+      if (!lgot_ents)
+       continue;
+
+      symtab_hdr = &elf_symtab_hdr (ibfd);
+      locsymcount = symtab_hdr->sh_info;
+      end_lgot_ents = lgot_ents + locsymcount;
+      local_plt = (struct plt_entry **) end_lgot_ents;
+      end_local_plt = local_plt + locsymcount;
+      for (lgot = lgot_ents; lgot < end_lgot_ents; ++lgot)
+       for (gent = *lgot; gent != NULL; gent = gent->next)
+         if (!gent->is_indirect
+             && gent->tls_type == 0
+             && 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))
+               {
+                 htab->stub_error = true;
+                 return false;
+               }
+           }
+
+      if (!htab->opd_abi)
+       for (lplt = local_plt; lplt < end_local_plt; ++lplt)
+         for (pent = *lplt; pent != NULL; pent = pent->next)
+           if (pent->plt.offset != (bfd_vma) -1)
+             {
+               Elf_Internal_Sym *sym;
+
+               if (!get_sym_h (NULL, &sym, NULL, NULL, &local_syms,
+                               lplt - local_plt, ibfd))
+                 {
+                 err_exit:
+                   if (symtab_hdr->contents != (unsigned char *) local_syms)
+                     free (local_syms);
+                   return false;
+                 }
+
+               if (ELF_ST_TYPE (sym->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))
+                     goto err_exit;
+                 }
+             }
+
+      if (local_syms != NULL
+         && symtab_hdr->contents != (unsigned char *) local_syms)
+       {
+         if (!info->keep_memory)
+           free (local_syms);
+         else
+           symtab_hdr->contents = (unsigned char *) local_syms;
+       }
+    }
+  return true;
+}
+
+static bool
+got_and_plt_relr (struct elf_link_hash_entry *h, void *inf)
+{
+  struct bfd_link_info *info;
+  struct ppc_link_hash_table *htab;
+  struct plt_entry *pent;
+  struct got_entry *gent;
+
+  if (h->root.type == bfd_link_hash_indirect)
+    return true;
+
+  info = (struct bfd_link_info *) inf;
+  htab = ppc_hash_table (info);
+  if (htab == NULL)
+    return false;
+
+  if (h->type != STT_GNU_IFUNC
+      && h->def_regular
+      && (h->root.type == bfd_link_hash_defined
+         || h->root.type == bfd_link_hash_defweak))
+    {
+      if (!htab->elf.dynamic_sections_created
+         || h->dynindx == -1
+         || SYMBOL_REFERENCES_LOCAL (info, h))
+       for (gent = h->got.glist; gent != NULL; gent = gent->next)
+         if (!gent->is_indirect
+             && gent->tls_type == 0
+             && 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))
+               {
+                 htab->stub_error = true;
+                 return false;
+               }
+           }
+
+      if (!htab->opd_abi
+         && use_local_plt (info, h))
+       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))
+               {
+                 htab->stub_error = true;
+                 return false;
+               }
+           }
+    }
+  return true;
+}
+
 /* Determine and set the size of the stub section for a final link.
 
    The basic idea here is to examine all the relocations looking for
@@ -13413,6 +13655,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
       struct map_stub *group;
 
       htab->stub_iteration += 1;
+      htab->relr_count = 0;
 
       for (input_bfd = info->input_bfds, bfd_indx = 0;
           input_bfd != NULL;
@@ -13436,16 +13679,20 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
               section = section->next)
            {
              Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
+             bool is_opd;
 
              /* If there aren't any relocs, then there's nothing more
                 to do.  */
              if ((section->flags & SEC_RELOC) == 0
                  || (section->flags & SEC_ALLOC) == 0
                  || (section->flags & SEC_LOAD) == 0
-                 || (section->flags & SEC_CODE) == 0
                  || section->reloc_count == 0)
                continue;
 
+             if (!info->enable_dt_relr
+                 && (section->flags & SEC_CODE) == 0)
+               continue;
+
              /* If this section is a link-once section that will be
                 discarded, then don't create any stubs.  */
              if (section->output_section == NULL
@@ -13459,6 +13706,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
              if (internal_relocs == NULL)
                goto error_ret_free_local;
 
+             is_opd = ppc64_elf_section_data (section)->sec_type == sec_opd;
+
              /* Now examine each relocation.  */
              irela = internal_relocs;
              irelaend = irela + section->reloc_count;
@@ -13492,21 +13741,76 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                    }
 
                  /* Only look for stubs on branch instructions.  */
-                 if (r_type != R_PPC64_REL24
-                     && r_type != R_PPC64_REL24_NOTOC
-                     && r_type != R_PPC64_REL24_P9NOTOC
-                     && r_type != R_PPC64_REL14
-                     && r_type != R_PPC64_REL14_BRTAKEN
-                     && r_type != R_PPC64_REL14_BRNTAKEN)
-                   continue;
+                 switch (r_type)
+                   {
+                   default:
+                     continue;
+
+                   case R_PPC64_REL24:
+                   case R_PPC64_REL24_NOTOC:
+                   case R_PPC64_REL24_P9NOTOC:
+                   case R_PPC64_REL14:
+                   case R_PPC64_REL14_BRTAKEN:
+                   case R_PPC64_REL14_BRNTAKEN:
+                     if ((section->flags & SEC_CODE) != 0)
+                       break;
+                     continue;
+
+                   case R_PPC64_ADDR64:
+                   case R_PPC64_TOC:
+                     if (info->enable_dt_relr
+                         && irela->r_offset % 2 == 0
+                         && section->alignment_power != 0)
+                       break;
+                     continue;
+                   }
 
                  /* Now determine the call target, its name, value,
                     section.  */
                  if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
                                  r_indx, input_bfd))
                    goto error_ret_free_internal;
-                 hash = ppc_elf_hash_entry (h);
 
+                 if (r_type == R_PPC64_ADDR64 || r_type == R_PPC64_TOC)
+                   {
+                     /* Only locally defined symbols can possibly use
+                        relative relocations.  */
+                     bfd_vma r_offset;
+                     if ((sym_sec == NULL
+                          || sym_sec->output_section == NULL)
+                         /* No symbol is OK too.  */
+                         && !(sym != NULL && sym->st_shndx == 0)
+                         /* Hack for __ehdr_start, which is undefined
+                            at this point.  */
+                         && !(h != NULL && h->root.linker_def))
+                       continue;
+                     if (NO_OPD_RELOCS && is_opd)
+                       continue;
+                     if (!is_opd
+                         && r_type == R_PPC64_ADDR64)
+                       {
+                         if (h != NULL
+                             ? h->type == STT_GNU_IFUNC
+                             : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+                           continue;
+                         if (h != NULL
+                             && !SYMBOL_REFERENCES_LOCAL (info, h))
+                           continue;
+                       }
+                     r_offset = _bfd_elf_section_offset (info->output_bfd,
+                                                         info,
+                                                         section,
+                                                         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))
+                       goto error_ret_free_internal;
+                     continue;
+                   }
+
+                 hash = ppc_elf_hash_entry (h);
                  ok_dest = false;
                  fdh = NULL;
                  sym_value = 0;
@@ -13804,6 +14108,14 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
       if (htab->relbrlt != NULL)
        htab->relbrlt->size = 0;
 
+      if (htab->elf.srelrdyn != NULL)
+       {
+         if (htab->stub_iteration <= STUB_SHRINK_ITER
+             || htab->elf.srelrdyn->rawsize < htab->elf.srelrdyn->size)
+           htab->elf.srelrdyn->rawsize = htab->elf.srelrdyn->size;
+         htab->elf.srelrdyn->size = 0;
+       }
+
       bfd_hash_traverse (&htab->stub_hash_table, ppc_size_one_stub, info);
 
       for (group = htab->group; group != NULL; group = group->next)
@@ -13845,6 +14157,53 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                = (group->stub_sec->size + (1 << align) - 1) & -(1 << align);
            }
 
+      if (htab->elf.srelrdyn != NULL)
+       {
+         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)))
+             return false;
+
+         if (!got_and_plt_relr_for_local_syms (info))
+           return false;
+         elf_link_hash_traverse (&htab->elf, got_and_plt_relr, 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);
+
+         size_t i = 0;
+         while (i < htab->relr_count)
+           {
+             bfd_vma base = htab->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)
+               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)
+                   i++;
+                 if (i == start_i)
+                   break;
+                 htab->elf.srelrdyn->size += 8;
+                 base += 63 * 8;
+               }
+           }
+       }
+
       for (group = htab->group; group != NULL; group = group->next)
        if (group->stub_sec != NULL
            && group->stub_sec->rawsize != group->stub_sec->size
@@ -13856,6 +14215,10 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
          && (htab->brlt->rawsize == htab->brlt->size
              || (htab->stub_iteration > STUB_SHRINK_ITER
                  && htab->brlt->rawsize > htab->brlt->size))
+         && (htab->elf.srelrdyn == NULL
+             || htab->elf.srelrdyn->rawsize == htab->elf.srelrdyn->size
+             || (htab->stub_iteration > STUB_SHRINK_ITER
+                 && htab->elf.srelrdyn->rawsize > htab->elf.srelrdyn->size))
          && (htab->glink_eh_frame == NULL
              || htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size)
          && (htab->tga_group == NULL
@@ -13959,6 +14322,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
     maybe_strip_output (info, htab->relbrlt);
   if (htab->glink_eh_frame != NULL)
     maybe_strip_output (info, htab->glink_eh_frame);
+  if (htab->elf.srelrdyn != NULL)
+    maybe_strip_output (info, htab->elf.srelrdyn);
 
   return true;
 }
@@ -14120,7 +14485,9 @@ build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
            else
              {
                plt = htab->pltlocal;
-               if (bfd_link_pic (info))
+               relplt = NULL;
+               if (bfd_link_pic (info)
+                   && !(info->enable_dt_relr && !htab->opd_abi))
                  {
                    relplt = htab->relpltlocal;
                    if (htab->opd_abi)
@@ -14128,8 +14495,6 @@ build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
                    else
                      rela.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
                  }
-               else
-                 relplt = NULL;
              }
            rela.r_addend = defined_sym_val (h) + ent->addend;
 
@@ -14311,7 +14676,10 @@ write_plt_relocs_for_local_syms (struct bfd_link_info *info)
              else
                {
                  plt = htab->pltlocal;
-                 relplt = bfd_link_pic (info) ? htab->relpltlocal : NULL;
+                 relplt = NULL;
+                 if (bfd_link_pic (info)
+                     && !(info->enable_dt_relr && !htab->opd_abi))
+                   relplt = htab->relpltlocal;
                }
 
              if (relplt == NULL)
@@ -14749,6 +15117,55 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
        }
     }
 
+  if (htab->elf.srelrdyn != NULL && htab->elf.srelrdyn->size != 0)
+    {
+      htab->elf.srelrdyn->contents
+       = bfd_alloc (htab->elf.dynobj, htab->elf.srelrdyn->size);
+      if (htab->elf.srelrdyn->contents == 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_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)
+           {
+             htab->stub_error = true;
+             i++;
+           }
+         base += 8;
+         while (1)
+           {
+             bfd_vma bits = 0;
+             while (i < htab->relr_count
+                    && htab->relr_addr[i] - base < 63 * 8
+                    && (htab->relr_addr[i] - base) % 8 == 0)
+               {
+                 bits |= (bfd_vma) 1 << ((htab->relr_addr[i] - base) / 8);
+                 i++;
+               }
+             if (bits == 0)
+               break;
+             bfd_put_64 (htab->elf.dynobj, (bits << 1) | 1, loc);
+             loc += 8;
+             base += 63 * 8;
+           }
+       }
+      /* Pad any excess with 1's, a do-nothing encoding.  */
+      while ((size_t) (loc - htab->elf.srelrdyn->contents)
+            < htab->elf.srelrdyn->size)
+       {
+         bfd_put_64 (htab->elf.dynobj, 1, loc);
+         loc += 8;
+       }
+    }
+
   for (group = htab->group; group != NULL; group = group->next)
     if ((stub_sec = group->stub_sec) != NULL)
       {
@@ -14760,14 +15177,14 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
       }
 
   if (group != NULL)
+    htab->stub_error = true;
+
+  if (htab->stub_error)
     {
-      htab->stub_error = true;
       _bfd_error_handler (_("stubs don't match calculated size"));
+      return false;
     }
 
-  if (htab->stub_error)
-    return false;
-
   if (stats != NULL)
     {
       char *groupmsg;
@@ -16462,10 +16879,14 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                              outrel.r_addend -= htab->elf.tls_sec->vma;
                          }
                      }
-                   loc = relgot->contents;
-                   loc += (relgot->reloc_count++
-                           * sizeof (Elf64_External_Rela));
-                   bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
+                   if (!(info->enable_dt_relr
+                         && ELF64_R_TYPE (outrel.r_info) == R_PPC64_RELATIVE))
+                     {
+                       loc = relgot->contents;
+                       loc += (relgot->reloc_count++
+                               * sizeof (Elf64_External_Rela));
+                       bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
+                     }
                  }
 
                /* Init the .got section contents here if we're not
@@ -16924,24 +17345,31 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                    }
                }
 
-             sreloc = elf_section_data (input_section)->sreloc;
-             if (h != NULL
-                 ? h->elf.type == STT_GNU_IFUNC
-                 : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+             if (!(info->enable_dt_relr
+                   && ELF64_R_TYPE (outrel.r_info) == R_PPC64_RELATIVE
+                   && rel->r_offset % 2 == 0
+                   && input_section->alignment_power != 0
+                   && ELF64_R_TYPE (orig_rel.r_info) != R_PPC64_UADDR64))
                {
-                 sreloc = htab->elf.irelplt;
-                 if (indx == 0 || is_static_defined (&h->elf))
-                   htab->elf.ifunc_resolvers = true;
+                 sreloc = elf_section_data (input_section)->sreloc;
+                 if (h != NULL
+                     ? h->elf.type == STT_GNU_IFUNC
+                     : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+                   {
+                     sreloc = htab->elf.irelplt;
+                     if (indx == 0 || is_static_defined (&h->elf))
+                       htab->elf.ifunc_resolvers = true;
+                   }
+                 if (sreloc == NULL)
+                   abort ();
+
+                 if (sreloc->reloc_count * sizeof (Elf64_External_Rela)
+                     >= sreloc->size)
+                   abort ();
+                 loc = sreloc->contents;
+                 loc += sreloc->reloc_count++ * sizeof (Elf64_External_Rela);
+                 bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
                }
-             if (sreloc == NULL)
-               abort ();
-
-             if (sreloc->reloc_count * sizeof (Elf64_External_Rela)
-                 >= sreloc->size)
-               abort ();
-             loc = sreloc->contents;
-             loc += sreloc->reloc_count++ * sizeof (Elf64_External_Rela);
-             bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
 
              if (!warned_dynamic
                  && !ppc64_glibc_dynamic_reloc (ELF64_R_TYPE (outrel.r_info)))
index 93603b0be68518d789844ba5e39019a94204ce8a..2a2aaf4a17d175b684daccf3eef56d8635cfc04b 100644 (file)
@@ -429,7 +429,9 @@ proc supports_persistent_section {} {
 
 # Whether a target support DT_RELR sections.
 proc supports_dt_relr {} {
-    if { ([istarget x86_64-*-*] || [istarget i?86-*-*])
+    if { ([istarget x86_64-*-*]
+         || [istarget i?86-*-*]
+         || [istarget powerpc64*-*-*])
         && ([istarget *-*-linux*]
             || [istarget *-*-gnu*]) } {
        return 1
index 15221b82220ead59b35fe2d1733971a954744608..a18393b7202f441bc87c5828ffb232db826812f6 100644 (file)
@@ -1,5 +1,6 @@
 source_sh ${srcdir}/emulparams/elf32ppccommon.sh
 source_sh ${srcdir}/emulparams/plt_unwind.sh
+source_sh ${srcdir}/emulparams/dt-relr.sh
 EXTRA_EM_FILE=ppc64elf
 ELFSIZE=64
 OUTPUT_FORMAT="elf64-powerpc"
index cea2931e37de3fb8b6d1ed9e98c3c99b698fdbaa..b1391566a13fa08aca38247defcc6eef56326443 100644 (file)
@@ -10,7 +10,7 @@
 #...
 Relocation section '\.rel(a|)\.dyn' at offset 0x[0-9a-f]+ contains 1 entry:
 #...
-[0-9a-f]+ +[0-9a-f]+ +R_.*_RELATIVE .*
+[0-9a-f]+ +[0-9a-f]+ +R_.*_(RELATIVE|UADDR.*) .*
 #...
 Relocation section '\.relr\.dyn' at offset 0x[0-9a-f]+ contains 2 entries:
   4 offsets
index 73087a67533ecd5944b8dd4e55f7ca52b10a83d4..c285e8707d79446c4a1385cc204994582fd395d1 100644 (file)
@@ -10,7 +10,7 @@
 #...
 Relocation section '\.rel(a|)\.dyn' at offset 0x[0-9a-f]+ contains 2 entries:
 #...
-[0-9a-f]+ +[0-9a-f]+ +R_.*_RELATIVE .*
+[0-9a-f]+ +[0-9a-f]+ +R_.*_(RELATIVE|UADDR.*) .*
 #...
 Relocation section '\.relr\.dyn' at offset 0x[0-9a-f]+ contains 2 entries:
   3 offsets
index 4987b0865a3e727d2e8dd22764c510089f63e09f..7fd3046a1cf0720d8985fe764b6f2596135175cc 100644 (file)
@@ -10,7 +10,7 @@
 #...
 Relocation section '\.rel(a|)\.dyn' at offset 0x[0-9a-f]+ contains 1 entry:
 #...
-[0-9a-f]+ +[0-9a-f]+ +R_.*_RELATIVE .*
+[0-9a-f]+ +[0-9a-f]+ +R_.*_(RELATIVE|UADDR.*) .*
 #...
 Relocation section '\.relr\.dyn' at offset 0x[0-9a-f]+ contains 2 entries:
   4 offsets
index 24ce6cc007051e9cc97acdc7a7af281e16f9fc87..cdff8465a57d9597ec85604d7e41cc94748527ac 100644 (file)
@@ -10,7 +10,7 @@
 #...
 Relocation section '\.rel(a|)\.data' at offset 0x[0-9a-f]+ contains 1 entry:
 #...
-[0-9a-f]+ +[0-9a-f]+ +R_.*_RELATIVE .*
+[0-9a-f]+ +[0-9a-f]+ +R_.*_(RELATIVE|UADDR.*) .*
 #...
 Relocation section '\.relr\.dyn' at offset 0x[0-9a-f]+ contains 2 entries:
   4 offsets