public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* PowerPC64 DT_RELR
@ 2022-01-18  0:53 Alan Modra
  2022-01-18  1:08 ` H.J. Lu
  2022-06-09 10:57 ` Florian Weimer
  0 siblings, 2 replies; 15+ messages in thread
From: Alan Modra @ 2022-01-18  0:53 UTC (permalink / raw)
  To: binutils

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.

diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index ea9e60217bc..0f945797b49 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -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)))
diff --git a/binutils/testsuite/lib/binutils-common.exp b/binutils/testsuite/lib/binutils-common.exp
index 93603b0be68..2a2aaf4a17d 100644
--- a/binutils/testsuite/lib/binutils-common.exp
+++ b/binutils/testsuite/lib/binutils-common.exp
@@ -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
diff --git a/ld/emulparams/elf64ppc.sh b/ld/emulparams/elf64ppc.sh
index 15221b82220..a18393b7202 100644
--- a/ld/emulparams/elf64ppc.sh
+++ b/ld/emulparams/elf64ppc.sh
@@ -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"
diff --git a/ld/testsuite/ld-elf/dt-relr-2b.d b/ld/testsuite/ld-elf/dt-relr-2b.d
index cea2931e37d..b1391566a13 100644
--- a/ld/testsuite/ld-elf/dt-relr-2b.d
+++ b/ld/testsuite/ld-elf/dt-relr-2b.d
@@ -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
diff --git a/ld/testsuite/ld-elf/dt-relr-2c.d b/ld/testsuite/ld-elf/dt-relr-2c.d
index 73087a67533..c285e8707d7 100644
--- a/ld/testsuite/ld-elf/dt-relr-2c.d
+++ b/ld/testsuite/ld-elf/dt-relr-2c.d
@@ -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
diff --git a/ld/testsuite/ld-elf/dt-relr-2d.d b/ld/testsuite/ld-elf/dt-relr-2d.d
index 4987b0865a3..7fd3046a1cf 100644
--- a/ld/testsuite/ld-elf/dt-relr-2d.d
+++ b/ld/testsuite/ld-elf/dt-relr-2d.d
@@ -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
diff --git a/ld/testsuite/ld-elf/dt-relr-2e.d b/ld/testsuite/ld-elf/dt-relr-2e.d
index 24ce6cc0070..cdff8465a57 100644
--- a/ld/testsuite/ld-elf/dt-relr-2e.d
+++ b/ld/testsuite/ld-elf/dt-relr-2e.d
@@ -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

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PowerPC64 DT_RELR
  2022-01-18  0:53 PowerPC64 DT_RELR Alan Modra
@ 2022-01-18  1:08 ` H.J. Lu
  2022-01-18 13:32   ` H.J. Lu
  2022-06-09 10:57 ` Florian Weimer
  1 sibling, 1 reply; 15+ messages in thread
From: H.J. Lu @ 2022-01-18  1:08 UTC (permalink / raw)
  To: Alan Modra; +Cc: Binutils

On Mon, Jan 17, 2022 at 4:54 PM Alan Modra via Binutils
<binutils@sourceware.org> wrote:
>
> 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.
>
> diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
> index ea9e60217bc..0f945797b49 100644
> --- a/bfd/elf64-ppc.c
> +++ b/bfd/elf64-ppc.c
> @@ -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)))
> diff --git a/binutils/testsuite/lib/binutils-common.exp b/binutils/testsuite/lib/binutils-common.exp
> index 93603b0be68..2a2aaf4a17d 100644
> --- a/binutils/testsuite/lib/binutils-common.exp
> +++ b/binutils/testsuite/lib/binutils-common.exp
> @@ -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
> diff --git a/ld/emulparams/elf64ppc.sh b/ld/emulparams/elf64ppc.sh
> index 15221b82220..a18393b7202 100644
> --- a/ld/emulparams/elf64ppc.sh
> +++ b/ld/emulparams/elf64ppc.sh
> @@ -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"
> diff --git a/ld/testsuite/ld-elf/dt-relr-2b.d b/ld/testsuite/ld-elf/dt-relr-2b.d
> index cea2931e37d..b1391566a13 100644
> --- a/ld/testsuite/ld-elf/dt-relr-2b.d
> +++ b/ld/testsuite/ld-elf/dt-relr-2b.d
> @@ -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
> diff --git a/ld/testsuite/ld-elf/dt-relr-2c.d b/ld/testsuite/ld-elf/dt-relr-2c.d
> index 73087a67533..c285e8707d7 100644
> --- a/ld/testsuite/ld-elf/dt-relr-2c.d
> +++ b/ld/testsuite/ld-elf/dt-relr-2c.d
> @@ -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
> diff --git a/ld/testsuite/ld-elf/dt-relr-2d.d b/ld/testsuite/ld-elf/dt-relr-2d.d
> index 4987b0865a3..7fd3046a1cf 100644
> --- a/ld/testsuite/ld-elf/dt-relr-2d.d
> +++ b/ld/testsuite/ld-elf/dt-relr-2d.d
> @@ -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
> diff --git a/ld/testsuite/ld-elf/dt-relr-2e.d b/ld/testsuite/ld-elf/dt-relr-2e.d
> index 24ce6cc0070..cdff8465a57 100644
> --- a/ld/testsuite/ld-elf/dt-relr-2e.d
> +++ b/ld/testsuite/ld-elf/dt-relr-2e.d
> @@ -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
>
> --
> Alan Modra
> Australia Development Lab, IBM

You can test your linker wiith the glibc build using users/hjl/relr/master
branch:

https://gitlab.com/x86-glibc/glibc/-/commits/users/hjl/relr/master

by enabling -Wl,-z,pack-relative-relocs by default.

-- 
H.J.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PowerPC64 DT_RELR
  2022-01-18  1:08 ` H.J. Lu
@ 2022-01-18 13:32   ` H.J. Lu
  2022-01-18 23:24     ` Alan Modra
  0 siblings, 1 reply; 15+ messages in thread
From: H.J. Lu @ 2022-01-18 13:32 UTC (permalink / raw)
  To: Alan Modra; +Cc: Binutils

On Mon, Jan 17, 2022 at 5:08 PM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Mon, Jan 17, 2022 at 4:54 PM Alan Modra via Binutils
> <binutils@sourceware.org> wrote:
> >
> > 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.
> >
> > diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
> > index ea9e60217bc..0f945797b49 100644
> > --- a/bfd/elf64-ppc.c
> > +++ b/bfd/elf64-ppc.c
> > @@ -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)))
> > diff --git a/binutils/testsuite/lib/binutils-common.exp b/binutils/testsuite/lib/binutils-common.exp
> > index 93603b0be68..2a2aaf4a17d 100644
> > --- a/binutils/testsuite/lib/binutils-common.exp
> > +++ b/binutils/testsuite/lib/binutils-common.exp
s> > @@ -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
> > diff --git a/ld/emulparams/elf64ppc.sh b/ld/emulparams/elf64ppc.sh
> > index 15221b82220..a18393b7202 100644
> > --- a/ld/emulparams/elf64ppc.sh
> > +++ b/ld/emulparams/elf64ppc.sh
> > @@ -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"
> > diff --git a/ld/testsuite/ld-elf/dt-relr-2b.d b/ld/testsuite/ld-elf/dt-relr-2b.d
> > index cea2931e37d..b1391566a13 100644
> > --- a/ld/testsuite/ld-elf/dt-relr-2b.d
> > +++ b/ld/testsuite/ld-elf/dt-relr-2b.d
> > @@ -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
> > diff --git a/ld/testsuite/ld-elf/dt-relr-2c.d b/ld/testsuite/ld-elf/dt-relr-2c.d
> > index 73087a67533..c285e8707d7 100644
> > --- a/ld/testsuite/ld-elf/dt-relr-2c.d
> > +++ b/ld/testsuite/ld-elf/dt-relr-2c.d
> > @@ -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
> > diff --git a/ld/testsuite/ld-elf/dt-relr-2d.d b/ld/testsuite/ld-elf/dt-relr-2d.d
> > index 4987b0865a3..7fd3046a1cf 100644
> > --- a/ld/testsuite/ld-elf/dt-relr-2d.d
> > +++ b/ld/testsuite/ld-elf/dt-relr-2d.d
> > @@ -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
> > diff --git a/ld/testsuite/ld-elf/dt-relr-2e.d b/ld/testsuite/ld-elf/dt-relr-2e.d
> > index 24ce6cc0070..cdff8465a57 100644
> > --- a/ld/testsuite/ld-elf/dt-relr-2e.d
> > +++ b/ld/testsuite/ld-elf/dt-relr-2e.d
> > @@ -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
> >
> > --
> > Alan Modra
> > Australia Development Lab, IBM
>
> You can test your linker wiith the glibc build using users/hjl/relr/master
> branch:
>
> https://gitlab.com/x86-glibc/glibc/-/commits/users/hjl/relr/master
>
> by enabling -Wl,-z,pack-relative-relocs by default.
>

I tried binutils master branch on glibc master branch:

$ readelf -r libc.so.6

Relocation section '.rela.dyn' at offset 0x21d38 contains 562 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000000  000000000000 R_PPC64_NONE                         0
000000000000  000000000000 R_PPC64_NONE                         0

There are 238 R_PPC64_NONEs in libc.so.6 alone.  There is none
for i686, x32 and x86-64.

-- 
H.J.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PowerPC64 DT_RELR
  2022-01-18 13:32   ` H.J. Lu
@ 2022-01-18 23:24     ` Alan Modra
  2022-01-19 22:56       ` H.J. Lu
  0 siblings, 1 reply; 15+ messages in thread
From: Alan Modra @ 2022-01-18 23:24 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Binutils

On Tue, Jan 18, 2022 at 05:32:37AM -0800, H.J. Lu wrote:
> I tried binutils master branch on glibc master branch:
> 
> $ readelf -r libc.so.6
> 
> Relocation section '.rela.dyn' at offset 0x21d38 contains 562 entries:
>   Offset          Info           Type           Sym. Value    Sym. Name + Addend
> 000000000000  000000000000 R_PPC64_NONE                         0
> 000000000000  000000000000 R_PPC64_NONE                         0
> 
> There are 238 R_PPC64_NONEs in libc.so.6 alone.  There is none
> for i686, x32 and x86-64.

Thanks for testing and finding the problem.  I did warn that DT_RELR
on ppc64 is experimental.  :-)

I had the SYMBOL_REFERENCES_LOCAL test in the wrong place.
check_relocs is too early to know whether a symbol is dynamic in a
shared library.  Lots of glibc symbols are made local by version
script, but that doesn't happen until size_dynamic_sections.

	* elf64-ppc.c (ppc64_elf_check_relocs): Don't count relative relocs
	here depending on SYMBOL_REFERENCES_LOCAL.
	(dec_dynrel_count): Likewise.
	(allocate_dynrelocs): Do so here instead.

diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index 923c7a3b407..aeae3b7e640 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -5390,8 +5390,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 		  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))))
+		      && ((!NO_OPD_RELOCS && is_opd) || !ifunc))
 		    p->rel_count += 1;
 		}
 	      else
@@ -7287,8 +7286,7 @@ dec_dynrel_count (const Elf_Internal_Rela *rel,
 		  && 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))))
+		      || h->type != STT_GNU_IFUNC))
 		p->rel_count -= 1;
 	      p->count -= 1;
 	      if (p->count == 0)
@@ -10016,7 +10014,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 	  if (eh->elf.type == STT_GNU_IFUNC)
 	    sreloc = htab->elf.irelplt;
 	  count = p->count;
-	  if (info->enable_dt_relr)
+	  if (info->enable_dt_relr && SYMBOL_REFERENCES_LOCAL (info, h))
 	    count -= p->rel_count;
 	  sreloc->size += count * sizeof (Elf64_External_Rela);
 	}

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PowerPC64 DT_RELR
  2022-01-18 23:24     ` Alan Modra
@ 2022-01-19 22:56       ` H.J. Lu
  2022-01-22  4:28         ` Alan Modra
  0 siblings, 1 reply; 15+ messages in thread
From: H.J. Lu @ 2022-01-19 22:56 UTC (permalink / raw)
  To: Alan Modra; +Cc: Binutils

On Tue, Jan 18, 2022 at 3:24 PM Alan Modra <amodra@gmail.com> wrote:
>
> On Tue, Jan 18, 2022 at 05:32:37AM -0800, H.J. Lu wrote:
> > I tried binutils master branch on glibc master branch:
> >
> > $ readelf -r libc.so.6
> >
> > Relocation section '.rela.dyn' at offset 0x21d38 contains 562 entries:
> >   Offset          Info           Type           Sym. Value    Sym. Name + Addend
> > 000000000000  000000000000 R_PPC64_NONE                         0
> > 000000000000  000000000000 R_PPC64_NONE                         0
> >
> > There are 238 R_PPC64_NONEs in libc.so.6 alone.  There is none
> > for i686, x32 and x86-64.
>
> Thanks for testing and finding the problem.  I did warn that DT_RELR
> on ppc64 is experimental.  :-)
>
> I had the SYMBOL_REFERENCES_LOCAL test in the wrong place.
> check_relocs is too early to know whether a symbol is dynamic in a
> shared library.  Lots of glibc symbols are made local by version
> script, but that doesn't happen until size_dynamic_sections.
>
>         * elf64-ppc.c (ppc64_elf_check_relocs): Don't count relative relocs
>         here depending on SYMBOL_REFERENCES_LOCAL.
>         (dec_dynrel_count): Likewise.
>         (allocate_dynrelocs): Do so here instead.
>
> diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
> index 923c7a3b407..aeae3b7e640 100644
> --- a/bfd/elf64-ppc.c
> +++ b/bfd/elf64-ppc.c
> @@ -5390,8 +5390,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
>                   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))))
> +                     && ((!NO_OPD_RELOCS && is_opd) || !ifunc))
>                     p->rel_count += 1;
>                 }
>               else
> @@ -7287,8 +7286,7 @@ dec_dynrel_count (const Elf_Internal_Rela *rel,
>                   && 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))))
> +                     || h->type != STT_GNU_IFUNC))
>                 p->rel_count -= 1;
>               p->count -= 1;
>               if (p->count == 0)
> @@ -10016,7 +10014,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
>           if (eh->elf.type == STT_GNU_IFUNC)
>             sreloc = htab->elf.irelplt;
>           count = p->count;
> -         if (info->enable_dt_relr)
> +         if (info->enable_dt_relr && SYMBOL_REFERENCES_LOCAL (info, h))
>             count -= p->rel_count;
>           sreloc->size += count * sizeof (Elf64_External_Rela);
>         }
>
> --
> Alan Modra
> Australia Development Lab, IBM

Does PowerPC64 support static PIE?  If yes, it may need more linker changes.

-- 
H.J.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PowerPC64 DT_RELR
  2022-01-19 22:56       ` H.J. Lu
@ 2022-01-22  4:28         ` Alan Modra
  2022-01-22 13:04           ` H.J. Lu
  2022-01-23 13:51           ` H.J. Lu
  0 siblings, 2 replies; 15+ messages in thread
From: Alan Modra @ 2022-01-22  4:28 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Binutils

On Wed, Jan 19, 2022 at 02:56:07PM -0800, H.J. Lu wrote:
> Does PowerPC64 support static PIE?  If yes, it may need more linker changes.

No.  There are things in glibc that need fixing.  We have small-model
toc relocs being used in assembly.  This results in the linker
refusing to do toc/got indirect to toc pointer relative code
optimisation for those sequences.  So we get toc/got entries with
relative relocations that result in segfaults when static-pie binaries
are trying to relocate themselves.

What were the linker changes you thought would be necessary?

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PowerPC64 DT_RELR
  2022-01-22  4:28         ` Alan Modra
@ 2022-01-22 13:04           ` H.J. Lu
  2022-01-23 13:51           ` H.J. Lu
  1 sibling, 0 replies; 15+ messages in thread
From: H.J. Lu @ 2022-01-22 13:04 UTC (permalink / raw)
  To: Alan Modra; +Cc: Binutils

On Fri, Jan 21, 2022 at 8:28 PM Alan Modra <amodra@gmail.com> wrote:
>
> On Wed, Jan 19, 2022 at 02:56:07PM -0800, H.J. Lu wrote:
> > Does PowerPC64 support static PIE?  If yes, it may need more linker changes.
>
> No.  There are things in glibc that need fixing.  We have small-model
> toc relocs being used in assembly.  This results in the linker
> refusing to do toc/got indirect to toc pointer relative code
> optimisation for those sequences.  So we get toc/got entries with
> relative relocations that result in segfaults when static-pie binaries
> are trying to relocate themselves.
>
> What were the linker changes you thought would be necessary?
>

The main issue is symbols generated by linker and linker scripts.
I enabled DT_RELR in glibc and binutls by default

$ readelf -r elf/ld.so
...
Relocation section '.relr.dyn' at offset 0xd78 contains 6 entries:
  142 offsets
0000000000033000
...

diff --git a/ld/ldmain.c b/ld/ldmain.c
index ea72b14a301..6ed71a74d87 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -359,6 +359,7 @@ main (int argc, char **argv)
 #endif
   link_info.start_stop_gc = false;
   link_info.start_stop_visibility = STV_PROTECTED;
+  link_info.enable_dt_relr = 1;

   ldfile_add_arch ("");
   emulation = get_emulation (argc, argv);

These triggered many run-time and link-time failures.

-- 
H.J.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PowerPC64 DT_RELR
  2022-01-22  4:28         ` Alan Modra
  2022-01-22 13:04           ` H.J. Lu
@ 2022-01-23 13:51           ` H.J. Lu
  2022-03-02  8:41             ` Alan Modra
  1 sibling, 1 reply; 15+ messages in thread
From: H.J. Lu @ 2022-01-23 13:51 UTC (permalink / raw)
  To: Alan Modra; +Cc: Binutils

On Fri, Jan 21, 2022 at 8:28 PM Alan Modra <amodra@gmail.com> wrote:
>
> On Wed, Jan 19, 2022 at 02:56:07PM -0800, H.J. Lu wrote:
> > Does PowerPC64 support static PIE?  If yes, it may need more linker changes.
>
> No.  There are things in glibc that need fixing.  We have small-model
> toc relocs being used in assembly.  This results in the linker
> refusing to do toc/got indirect to toc pointer relative code
> optimisation for those sequences.  So we get toc/got entries with
> relative relocations that result in segfaults when static-pie binaries
> are trying to relocate themselves.

If you just want to get static PIE working, you can simply assume that any
files in libc.a may be used in ld.so, which is almost a static PIE.   This was
how I implemented static PIE in glibc.  Of course, this may expose linker
bugs:

https://sourceware.org/bugzilla/show_bug.cgi?id=21782
https://sourceware.org/bugzilla/show_bug.cgi?id=22269
https://sourceware.org/bugzilla/show_bug.cgi?id=22263

-- 
H.J.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PowerPC64 DT_RELR
  2022-01-23 13:51           ` H.J. Lu
@ 2022-03-02  8:41             ` Alan Modra
  2022-03-03  2:33               ` PowerPC64 DT_RELR relative reloc addresses Alan Modra
                                 ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: Alan Modra @ 2022-03-02  8:41 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Binutils

[-- Attachment #1: Type: text/plain, Size: 570 bytes --]

I had a look today at DT_RELR again, and found I need to do some work
in the linker.  Just keeping addresses of relative relocs isn't
enough, because data addresses can change between ppc64_elf_size_stubs
and ppc64_elf_build_stubs.  Anyway, the reason for the email is to
inform anyone playing with DT_RELR on powerpc64 that the current
support is definitely experimental!  Also, you might like to apply the
attached patch to gitlab.com/x86-glibc/glibc users/hjl/relr/master
branch.  glibc regressions went from 574 to 17.

-- 
Alan Modra
Australia Development Lab, IBM

[-- Attachment #2: 0006-elf-ELF_DYNAMIC_RELOCATE-order-relr-before-rel-rela.patch --]
[-- Type: text/x-diff, Size: 1192 bytes --]

From 59265b9b769a0423700bd5a34bc9933e859e51c2 Mon Sep 17 00:00:00 2001
From: Alan Modra <amodra@gmail.com>
Date: Wed, 2 Mar 2022 15:34:58 +1030
Subject: [PATCH 6/6] elf: ELF_DYNAMIC_RELOCATE: order relr before rel/rela

ld goes to some trouble to sort relative relocs first, so that ifunc
resolvers that require relocated got entries have them relocated.
Preserve that ordering.

diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h
index 320a9029f2..252f407a12 100644
--- a/elf/dynamic-link.h
+++ b/elf/dynamic-link.h
@@ -188,10 +188,10 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
   do {									      \
     int edr_lazy = elf_machine_runtime_setup ((map), (scope), (lazy),	      \
 					      (consider_profile));	      \
-    ELF_DYNAMIC_DO_REL ((map), (scope), edr_lazy, skip_ifunc);		      \
-    ELF_DYNAMIC_DO_RELA ((map), (scope), edr_lazy, skip_ifunc);		      \
     if (((map) != &GL(dl_rtld_map) || DO_RTLD_BOOTSTRAP))		      \
       ELF_DYNAMIC_DO_RELR (map);					      \
+    ELF_DYNAMIC_DO_REL ((map), (scope), edr_lazy, skip_ifunc);		      \
+    ELF_DYNAMIC_DO_RELA ((map), (scope), edr_lazy, skip_ifunc);		      \
   } while (0)
 
 #endif

^ permalink raw reply	[flat|nested] 15+ messages in thread

* PowerPC64 DT_RELR relative reloc addresses
  2022-03-02  8:41             ` Alan Modra
@ 2022-03-03  2:33               ` Alan Modra
  2022-03-09  1:25               ` PowerPC64 DT_RELR Fangrui Song
       [not found]               ` <MWHPR1201MB0110441CD99C43F8215D15EBCB0A9@MWHPR1201MB0110.namprd12.prod.outlook.com>
  2 siblings, 0 replies; 15+ messages in thread
From: Alan Modra @ 2022-03-03  2:33 UTC (permalink / raw)
  To: binutils

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 dd79c05bb61..89ce4cfba0d 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -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)

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PowerPC64 DT_RELR
  2022-03-02  8:41             ` Alan Modra
  2022-03-03  2:33               ` PowerPC64 DT_RELR relative reloc addresses Alan Modra
@ 2022-03-09  1:25               ` Fangrui Song
       [not found]               ` <MWHPR1201MB0110441CD99C43F8215D15EBCB0A9@MWHPR1201MB0110.namprd12.prod.outlook.com>
  2 siblings, 0 replies; 15+ messages in thread
From: Fangrui Song @ 2022-03-09  1:25 UTC (permalink / raw)
  To: Alan Modra; +Cc: H.J. Lu, Binutils

On Wed, Mar 2, 2022 at 12:41 AM Alan Modra via Binutils
<binutils@sourceware.org> wrote:
>
> I had a look today at DT_RELR again, and found I need to do some work
> in the linker.  Just keeping addresses of relative relocs isn't
> enough, because data addresses can change between ppc64_elf_size_stubs
> and ppc64_elf_build_stubs.  Anyway, the reason for the email is to
> inform anyone playing with DT_RELR on powerpc64 that the current
> support is definitely experimental!  Also, you might like to apply the
> attached patch to gitlab.com/x86-glibc/glibc users/hjl/relr/master
> branch.  glibc regressions went from 574 to 17.
>
> --
> Alan Modra
> Australia Development Lab, IBM

Thanks for testing it on PowerPC. It seems cleaner to fold
0006-elf-ELF_DYNAMIC_RELOCATE-order-relr-before-rel-rela.patch  into
patch 1.
I adjusted my https://sourceware.org/git/?p=glibc.git;a=shortlog;h=refs/heads/maskray/relr

H.J. may adjust
https://sourceware.org/pipermail/libc-alpha/2022-March/136783.html
("[PATCH v5 1/5] elf: Support DT_RELR relative relocation format [BZ #27924]")

HJ's series looks good to me
(https://sourceware.org/pipermail/libc-alpha/2022-March/136784.html).

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PowerPC64 DT_RELR
       [not found]               ` <MWHPR1201MB0110441CD99C43F8215D15EBCB0A9@MWHPR1201MB0110.namprd12.prod.outlook.com>
@ 2022-03-09 21:07                 ` Alan Modra
  0 siblings, 0 replies; 15+ messages in thread
From: Alan Modra @ 2022-03-09 21:07 UTC (permalink / raw)
  To: Fangrui Song; +Cc: H.J. Lu, Binutils

On Tue, Mar 08, 2022 at 05:25:08PM -0800, Fangrui Song wrote:
> Thanks for testing it on PowerPC. It seems cleaner to fold
> 0006-elf-ELF_DYNAMIC_RELOCATE-order-relr-before-rel-rela.patch  into
> patch 1.

Yes, of course that makes sense for the commit upstream.

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PowerPC64 DT_RELR
  2022-01-18  0:53 PowerPC64 DT_RELR Alan Modra
  2022-01-18  1:08 ` H.J. Lu
@ 2022-06-09 10:57 ` Florian Weimer
  2022-06-09 22:57   ` Alan Modra
  1 sibling, 1 reply; 15+ messages in thread
From: Florian Weimer @ 2022-06-09 10:57 UTC (permalink / raw)
  To: Alan Modra via Binutils

* Alan Modra via Binutils:

> 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.

Upstream glibc enables DT_RELR for its ELF files by default (including
tests), and we seem to hit something DT_RELR-related:

  Some glibc tests crash in DT_RELR relocation on powerpc64le
  <https://sourceware.org/bugzilla/show_bug.cgi?id=29235>

(I'm working on attaching the test binary to the bug.)

Maybe we should not automatically DT_RELR for glibc itself if BFD ld
supports it?

Thanks,
Florian


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PowerPC64 DT_RELR
  2022-06-09 10:57 ` Florian Weimer
@ 2022-06-09 22:57   ` Alan Modra
  2022-06-09 23:53     ` Alan Modra
  0 siblings, 1 reply; 15+ messages in thread
From: Alan Modra @ 2022-06-09 22:57 UTC (permalink / raw)
  To: Florian Weimer; +Cc: Alan Modra via Binutils, Nick Clifton

On Thu, Jun 09, 2022 at 12:57:32PM +0200, Florian Weimer wrote:
> * Alan Modra via Binutils:
> 
> > 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.
> 
> Upstream glibc enables DT_RELR for its ELF files by default (including
> tests), and we seem to hit something DT_RELR-related:
> 
>   Some glibc tests crash in DT_RELR relocation on powerpc64le
>   <https://sourceware.org/bugzilla/show_bug.cgi?id=29235>
> 
> (I'm working on attaching the test binary to the bug.)

I'll investigate.  My guess would be .eh_frame editing is the trigger.

> Maybe we should not automatically DT_RELR for glibc itself if BFD ld
> supports it?

Yes, at least until I can fix this.

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PowerPC64 DT_RELR
  2022-06-09 22:57   ` Alan Modra
@ 2022-06-09 23:53     ` Alan Modra
  0 siblings, 0 replies; 15+ messages in thread
From: Alan Modra @ 2022-06-09 23:53 UTC (permalink / raw)
  To: Florian Weimer; +Cc: Alan Modra via Binutils, Nick Clifton

On Fri, Jun 10, 2022 at 08:27:25AM +0930, Alan Modra wrote:
> On Thu, Jun 09, 2022 at 12:57:32PM +0200, Florian Weimer wrote:
> > * Alan Modra via Binutils:
> > 
> > > 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.
> > 
> > Upstream glibc enables DT_RELR for its ELF files by default (including
> > tests), and we seem to hit something DT_RELR-related:
> > 
> >   Some glibc tests crash in DT_RELR relocation on powerpc64le
> >   <https://sourceware.org/bugzilla/show_bug.cgi?id=29235>
> > 
> > (I'm working on attaching the test binary to the bug.)
> 
> I'll investigate.  My guess would be .eh_frame editing is the trigger.
> 
> > Maybe we should not automatically DT_RELR for glibc itself if BFD ld
> > supports it?
> 
> Yes, at least until I can fix this.

binutils-2.38-14.fc37 is missing binutils commit e4a35c731962.

-- 
Alan Modra
Australia Development Lab, IBM

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2022-06-09 23:53 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-18  0:53 PowerPC64 DT_RELR Alan Modra
2022-01-18  1:08 ` H.J. Lu
2022-01-18 13:32   ` H.J. Lu
2022-01-18 23:24     ` Alan Modra
2022-01-19 22:56       ` H.J. Lu
2022-01-22  4:28         ` Alan Modra
2022-01-22 13:04           ` H.J. Lu
2022-01-23 13:51           ` H.J. Lu
2022-03-02  8:41             ` Alan Modra
2022-03-03  2:33               ` PowerPC64 DT_RELR relative reloc addresses Alan Modra
2022-03-09  1:25               ` PowerPC64 DT_RELR Fangrui Song
     [not found]               ` <MWHPR1201MB0110441CD99C43F8215D15EBCB0A9@MWHPR1201MB0110.namprd12.prod.outlook.com>
2022-03-09 21:07                 ` Alan Modra
2022-06-09 10:57 ` Florian Weimer
2022-06-09 22:57   ` Alan Modra
2022-06-09 23:53     ` Alan Modra

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).