This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

ppc64_elf_edit_opd revamp


This patch sorts .opd relocs (see pr17666) and allows .opd sections
with a mix of 16 and 24 byte entries to be edited.

	* elf64-ppc.c (OPD_NDX): Define.  Use throughout for sizing/indexing
	_opd_sec_data array, halving required memory.
	(sort_r_offset): New function.
	(ppc64_elf_edit_opd): Sort incoming relocs.  Accept .opd
	sections with a mix of 16 and 24 byte OPD entries.  Don't
	attempt to honour --non-overlapping-opd for .opd sections with
	unexpected relocs.  Simplify opd entry size calculations by
	first finding the reloc for the next entry.  Make edit loop
	handle one opd entry per iteration, with an inner loop
	handling relocs per entry.

diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index c1029df..2e9d35a 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -2954,7 +2954,9 @@ struct _ppc64_elf_section_data
 
   union
   {
-    /* An array with one entry for each opd function descriptor.  */
+    /* An array with one entry for each opd function descriptor,
+       and some spares since opd entries may be either 16 or 24 bytes.  */
+#define OPD_NDX(OFF) ((OFF) >> 4)
     struct _opd_sec_data
     {
       /* Points to the function code section for local opd entries.  */
@@ -5005,7 +5007,7 @@ ppc64_elf_before_check_relocs (bfd *ibfd, struct bfd_link_info *info)
 	  bfd_size_type amt;
 	  asection **opd_sym_map;
 
-	  amt = opd->size * sizeof (*opd_sym_map) / 8;
+	  amt = OPD_NDX (opd->size) * sizeof (*opd_sym_map);
 	  opd_sym_map = bfd_zalloc (ibfd, amt);
 	  if (opd_sym_map == NULL)
 	    return FALSE;
@@ -5676,7 +5678,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 		  s = bfd_section_from_elf_index (abfd, isym->st_shndx);
 		  if (s != NULL && s != sec)
-		    opd_sym_map[rel->r_offset / 8] = s;
+		    opd_sym_map[OPD_NDX (rel->r_offset)] = s;
 		}
 	    }
 	  /* Fall through.  */
@@ -6343,7 +6345,7 @@ ppc64_elf_gc_mark_hook (asection *sec,
 	{
 	  rsec->gc_mark = 1;
 
-	  rsec = opd->func_sec[(sym->st_value + rel->r_addend) / 8];
+	  rsec = opd->func_sec[OPD_NDX (sym->st_value + rel->r_addend)];
 	}
     }
 
@@ -7404,7 +7406,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
   opd = get_opd_info (sym_sec);
   if (opd != NULL && opd->adjust != NULL)
     {
-      long adjust = opd->adjust[eh->elf.root.u.def.value / 8];
+      long adjust = opd->adjust[OPD_NDX (eh->elf.root.u.def.value)];
       if (adjust == -1)
 	{
 	  /* This entry has been deleted.  */
@@ -7587,6 +7589,21 @@ dec_dynrel_count (bfd_vma r_info,
   return FALSE;
 }
 
+/* qsort comparison function sorting relocs by r_offset.  */
+
+static int
+sort_r_offset (const void *p, const void *q)
+{
+  const Elf_Internal_Rela *a = (const Elf_Internal_Rela *) p;
+  const Elf_Internal_Rela *b = (const Elf_Internal_Rela *) q;
+
+  if (a->r_offset < b->r_offset)
+    return -1;
+  else if (a->r_offset > b->r_offset)
+    return 1;
+  return 0;
+}
+
 /* Remove unused Official Procedure Descriptor entries.  Currently we
    only remove those associated with functions in discarded link-once
    sections, or weakly defined functions that have been overridden.  It
@@ -7611,9 +7628,8 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
       Elf_Internal_Rela *relstart, *rel, *relend;
       Elf_Internal_Shdr *symtab_hdr;
       Elf_Internal_Sym *local_syms;
-      bfd_vma offset;
       struct _opd_sec_data *opd;
-      bfd_boolean need_edit, add_aux_fields;
+      bfd_boolean need_edit, add_aux_fields, broken;
       bfd_size_type cnt_16b = 0;
 
       if (!is_ppc64_elf (ibfd))
@@ -7641,12 +7657,13 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
 					    info->keep_memory);
       if (relstart == NULL)
 	return FALSE;
+      qsort (relstart, sec->reloc_count, sizeof (*relstart), sort_r_offset);
 
       /* First run through the relocs to check they are sane, and to
 	 determine whether we need to edit this opd section.  */
       need_edit = FALSE;
+      broken = FALSE;
       need_pad = sec;
-      offset = 0;
       relend = relstart + sec->reloc_count;
       for (rel = relstart; rel < relend; )
 	{
@@ -7655,13 +7672,14 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
 	  asection *sym_sec;
 	  struct elf_link_hash_entry *h;
 	  Elf_Internal_Sym *sym;
+	  bfd_vma offset;
 
-	  /* .opd contains a regular array of 16 or 24 byte entries.  We're
+	  /* .opd contains an array of 16 or 24 byte entries.  We're
 	     only interested in the reloc pointing to a function entry
 	     point.  */
-	  if (rel->r_offset != offset
-	      || rel + 1 >= relend
-	      || (rel + 1)->r_offset != offset + 8)
+	  offset = rel->r_offset;
+	  if (rel + 1 == relend
+	      || rel[1].r_offset != offset + 8)
 	    {
 	      /* If someone messes with .opd alignment then after a
 		 "ld -r" we might have padding in the middle of .opd.
@@ -7671,7 +7689,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
 	    broken_opd:
 	      (*_bfd_error_handler)
 		(_("%B: .opd is not a regular array of opd entries"), ibfd);
-	      need_edit = FALSE;
+	      broken = TRUE;
 	      break;
 	    }
 
@@ -7681,7 +7699,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
 	      (*_bfd_error_handler)
 		(_("%B: unexpected reloc type %u in .opd section"),
 		 ibfd, r_type);
-	      need_edit = FALSE;
+	      broken = TRUE;
 	      break;
 	    }
 
@@ -7702,7 +7720,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
 	      (*_bfd_error_handler)
 		(_("%B: undefined sym `%s' in .opd section"),
 		 ibfd, sym_name);
-	      need_edit = FALSE;
+	      broken = TRUE;
 	      break;
 	    }
 
@@ -7718,39 +7736,33 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
 	    need_edit = TRUE;
 
 	  rel += 2;
-	  if (rel == relend
-	      || (rel + 1 == relend && rel->r_offset == offset + 16))
+	  if (rel + 1 == relend
+	      || (rel + 2 < relend
+		  && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC))
+	    ++rel;
+
+	  if (rel == relend)
 	    {
 	      if (sec->size == offset + 24)
 		{
 		  need_pad = NULL;
 		  break;
 		}
-	      if (rel == relend && sec->size == offset + 16)
+	      if (sec->size == offset + 16)
 		{
 		  cnt_16b++;
 		  break;
 		}
 	      goto broken_opd;
 	    }
-
-	  if (rel->r_offset == offset + 24)
-	    offset += 24;
-	  else if (rel->r_offset != offset + 16)
-	    goto broken_opd;
 	  else if (rel + 1 < relend
 		   && ELF64_R_TYPE (rel[0].r_info) == R_PPC64_ADDR64
 		   && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOC)
 	    {
-	      offset += 16;
-	      cnt_16b++;
-	    }
-	  else if (rel + 2 < relend
-		   && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_ADDR64
-		   && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC)
-	    {
-	      offset += 24;
-	      rel += 1;
+	      if (rel[0].r_offset == offset + 16)
+		cnt_16b++;
+	      else if (rel[0].r_offset != offset + 24)
+		goto broken_opd;
 	    }
 	  else
 	    goto broken_opd;
@@ -7758,18 +7770,16 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
 
       add_aux_fields = htab->params->non_overlapping_opd && cnt_16b > 0;
 
-      if (need_edit || add_aux_fields)
+      if (!broken && (need_edit || add_aux_fields))
 	{
 	  Elf_Internal_Rela *write_rel;
 	  Elf_Internal_Shdr *rel_hdr;
 	  bfd_byte *rptr, *wptr;
 	  bfd_byte *new_contents;
-	  bfd_boolean skip;
-	  long opd_ent_size;
 	  bfd_size_type amt;
 
 	  new_contents = NULL;
-	  amt = sec->size * sizeof (long) / 8;
+	  amt = OPD_NDX (sec->size) * sizeof (long);
 	  opd = &ppc64_elf_section_data (sec)->u.opd;
 	  opd->adjust = bfd_zalloc (sec->owner, amt);
 	  if (opd->adjust == NULL)
@@ -7807,123 +7817,134 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
 	      new_contents = bfd_malloc (sec->size + cnt_16b * 8);
 	      if (new_contents == NULL)
 		return FALSE;
-	      need_pad = FALSE;
+	      need_pad = NULL;
 	    }
 	  wptr = new_contents;
 	  rptr = sec->contents;
-
 	  write_rel = relstart;
-	  skip = FALSE;
-	  offset = 0;
-	  opd_ent_size = 0;
-	  for (rel = relstart; rel < relend; rel++)
+	  for (rel = relstart; rel < relend; )
 	    {
 	      unsigned long r_symndx;
 	      asection *sym_sec;
 	      struct elf_link_hash_entry *h;
+	      struct ppc_link_hash_entry *fdh = NULL;
 	      Elf_Internal_Sym *sym;
+	      long opd_ent_size;
+	      Elf_Internal_Rela *next_rel;
+	      bfd_boolean skip;
 
 	      r_symndx = ELF64_R_SYM (rel->r_info);
 	      if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
 			      r_symndx, ibfd))
 		goto error_ret;
 
-	      if (rel->r_offset == offset)
+	      next_rel = rel + 2;
+	      if (next_rel + 1 == relend
+		  || (next_rel + 2 < relend
+		      && ELF64_R_TYPE (next_rel[2].r_info) == R_PPC64_TOC))
+		++next_rel;
+
+	      /* See if the .opd entry is full 24 byte or
+		 16 byte (with fd_aux entry overlapped with next
+		 fd_func).  */
+	      opd_ent_size = 24;
+	      if (next_rel == relend)
 		{
-		  struct ppc_link_hash_entry *fdh = NULL;
-
-		  /* See if the .opd entry is full 24 byte or
-		     16 byte (with fd_aux entry overlapped with next
-		     fd_func).  */
-		  opd_ent_size = 24;
-		  if ((rel + 2 == relend && sec->size == offset + 16)
-		      || (rel + 3 < relend
-			  && rel[2].r_offset == offset + 16
-			  && rel[3].r_offset == offset + 24
-			  && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_ADDR64
-			  && ELF64_R_TYPE (rel[3].r_info) == R_PPC64_TOC))
+		  if (sec->size == rel->r_offset + 16)
 		    opd_ent_size = 16;
+		}
+	      else if (next_rel->r_offset == rel->r_offset + 16)
+		opd_ent_size = 16;
 
-		  if (h != NULL
-		      && h->root.root.string[0] == '.')
-		    {
-		      fdh = lookup_fdh ((struct ppc_link_hash_entry *) h, htab);
-		      if (fdh != NULL
-			  && fdh->elf.root.type != bfd_link_hash_defined
-			  && fdh->elf.root.type != bfd_link_hash_defweak)
-			fdh = NULL;
-		    }
+	      if (h != NULL
+		  && h->root.root.string[0] == '.')
+		{
+		  fdh = lookup_fdh ((struct ppc_link_hash_entry *) h, htab);
+		  if (fdh != NULL
+		      && fdh->elf.root.type != bfd_link_hash_defined
+		      && fdh->elf.root.type != bfd_link_hash_defweak)
+		    fdh = NULL;
+		}
 
-		  skip = (sym_sec->owner != ibfd
-			  || sym_sec->output_section == bfd_abs_section_ptr);
-		  if (skip)
+	      skip = (sym_sec->owner != ibfd
+		      || sym_sec->output_section == bfd_abs_section_ptr);
+	      if (skip)
+		{
+		  if (fdh != NULL && sym_sec->owner == ibfd)
 		    {
-		      if (fdh != NULL && sym_sec->owner == ibfd)
-			{
-			  /* Arrange for the function descriptor sym
-			     to be dropped.  */
-			  fdh->elf.root.u.def.value = 0;
-			  fdh->elf.root.u.def.section = sym_sec;
-			}
-		      opd->adjust[rel->r_offset / 8] = -1;
+		      /* Arrange for the function descriptor sym
+			 to be dropped.  */
+		      fdh->elf.root.u.def.value = 0;
+		      fdh->elf.root.u.def.section = sym_sec;
 		    }
-		  else
-		    {
-		      /* We'll be keeping this opd entry.  */
+		  opd->adjust[OPD_NDX (rel->r_offset)] = -1;
 
-		      if (fdh != NULL)
-			{
-			  /* Redefine the function descriptor symbol to
-			     this location in the opd section.  It is
-			     necessary to update the value here rather
-			     than using an array of adjustments as we do
-			     for local symbols, because various places
-			     in the generic ELF code use the value
-			     stored in u.def.value.  */
-			  fdh->elf.root.u.def.value = wptr - new_contents;
-			  fdh->adjust_done = 1;
-			}
+		  if (NO_OPD_RELOCS || info->relocatable)
+		    rel = next_rel;
+		  else
+		    while (1)
+		      {
+			if (!dec_dynrel_count (rel->r_info, sec, info,
+					       NULL, h, sym))
+			  goto error_ret;
 
-		      /* Local syms are a bit tricky.  We could
-			 tweak them as they can be cached, but
-			 we'd need to look through the local syms
-			 for the function descriptor sym which we
-			 don't have at the moment.  So keep an
-			 array of adjustments.  */
-		      opd->adjust[rel->r_offset / 8]
-			= (wptr - new_contents) - (rptr - sec->contents);
-
-		      if (wptr != rptr)
-			memcpy (wptr, rptr, opd_ent_size);
-		      wptr += opd_ent_size;
-		      if (add_aux_fields && opd_ent_size == 16)
-			{
-			  memset (wptr, '\0', 8);
-			  wptr += 8;
-			}
-		    }
-		  rptr += opd_ent_size;
-		  offset += opd_ent_size;
-		}
+			if (++rel == next_rel)
+			  break;
 
-	      if (skip)
-		{
-		  if (!NO_OPD_RELOCS
-		      && !info->relocatable
-		      && !dec_dynrel_count (rel->r_info, sec, info,
-					    NULL, h, sym))
-		    goto error_ret;
+			r_symndx = ELF64_R_SYM (rel->r_info);
+			if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
+					r_symndx, ibfd))
+			  goto error_ret;
+		      }
 		}
 	      else
 		{
+		  /* We'll be keeping this opd entry.  */
+		  long adjust;
+
+		  if (fdh != NULL)
+		    {
+		      /* Redefine the function descriptor symbol to
+			 this location in the opd section.  It is
+			 necessary to update the value here rather
+			 than using an array of adjustments as we do
+			 for local symbols, because various places
+			 in the generic ELF code use the value
+			 stored in u.def.value.  */
+		      fdh->elf.root.u.def.value = wptr - new_contents;
+		      fdh->adjust_done = 1;
+		    }
+
+		  /* Local syms are a bit tricky.  We could
+		     tweak them as they can be cached, but
+		     we'd need to look through the local syms
+		     for the function descriptor sym which we
+		     don't have at the moment.  So keep an
+		     array of adjustments.  */
+		  adjust = (wptr - new_contents) - (rptr - sec->contents);
+		  opd->adjust[OPD_NDX (rel->r_offset)] = adjust;
+
+		  if (wptr != rptr)
+		    memcpy (wptr, rptr, opd_ent_size);
+		  wptr += opd_ent_size;
+		  if (add_aux_fields && opd_ent_size == 16)
+		    {
+		      memset (wptr, '\0', 8);
+		      wptr += 8;
+		    }
+
 		  /* We need to adjust any reloc offsets to point to the
-		     new opd entries.  While we're at it, we may as well
-		     remove redundant relocs.  */
-		  rel->r_offset += opd->adjust[(offset - opd_ent_size) / 8];
-		  if (write_rel != rel)
-		    memcpy (write_rel, rel, sizeof (*rel));
-		  ++write_rel;
+		     new opd entries.  */
+		  for ( ; rel != next_rel; ++rel)
+		    {
+		      rel->r_offset += adjust;
+		      if (write_rel != rel)
+			memcpy (write_rel, rel, sizeof (*rel));
+		      ++write_rel;
+		    }
 		}
+
+	      rptr += opd_ent_size;
 	    }
 
 	  sec->size = wptr - new_contents;
@@ -11589,7 +11610,7 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
 		{
 		  long adjust;
 
-		  adjust = opd->adjust[sym->st_value / 8];
+		  adjust = opd->adjust[OPD_NDX (sym->st_value)];
 		  if (adjust == -1)
 		    /* Assume deleted functions won't ever be called.  */
 		    continue;
@@ -12165,7 +12186,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
 
 		      if (hash == NULL && opd->adjust != NULL)
 			{
-			  long adjust = opd->adjust[sym_value / 8];
+			  long adjust = opd->adjust[OPD_NDX (sym_value)];
 			  if (adjust == -1)
 			    continue;
 			  code_value += adjust;
@@ -13077,7 +13098,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 	  opd = get_opd_info (sec);
 	  if (opd != NULL && opd->adjust != NULL)
 	    {
-	      long adjust = opd->adjust[(sym->st_value + rel->r_addend) / 8];
+	      long adjust = opd->adjust[OPD_NDX (sym->st_value
+						 + rel->r_addend)];
 	      if (adjust == -1)
 		relocation = 0;
 	      else
@@ -14844,7 +14866,7 @@ ppc64_elf_output_symbol_hook (struct bfd_link_info *info,
   if (!info->relocatable)
     value -= input_sec->output_section->vma;
 
-  adjust = opd->adjust[value / 8];
+  adjust = opd->adjust[OPD_NDX (value)];
   if (adjust == -1)
     return 2;
 

-- 
Alan Modra
Australia Development Lab, IBM


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]