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

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