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]

PR11378, bugs on exit from large powerpc64 binaries


I noticed when investigating PR11375 that the binary in question, cc1
from a bigtoc bootstrap, managed to have an _init function that was
pasted together from fragments all having different TOC pointers.
That in itself wasn't a problem since none of the fragments used the
TOC, but it would be if the startup files ever do so.  Fixed via
ppc64_elf_check_init_fini.

More importantly, I noticed that _init and _fini were only seen to
make calls to other functions (which might need a valid TOC pointer)
if the first fragment had such a call.  That happened to be true for
_init but not for _fini.  It's possible that some odd bugs seen on
exit from large ppc64 binaries might be caused by this.  Fixed by
looking at all fragments of .init/.fini in toc_adjusting_stub_needed.

bfd/
	PR ld/11378
	* elf64-ppc.h (ppc64_elf_check_init_fini): Declare.
	* elf64-ppc.c (call_check_done): Define.
	(ppc64_elf_add_symbol_hook): Substitute bfd_get_section_name macro.
	(ppc64_elf_check_relocs, ppc64_elf_size_dynamic_sections): Likewise.
	(ppc64_elf_finish_multitoc_partition): Remove unnecessary check.
	(toc_adjusting_stub_needed): Use call_check_done rather than toc_off.
	Simplify return logic.  Iterate over all .init and .fini fragments
	by recursion.  Set makes_toc_func_call here..
	(ppc64_elf_next_input_section): ..rather than here.
	(check_pasted_section, ppc64_elf_check_init_fini): New functions.
ld/
	PR ld/11378
	* emultempl/ppc64elf.em (gld${EMULATION_NAME}_after_allocation): Call
	ppc64_elf_check_init_fini and warn if .init/.fini use different TOCs.

Index: bfd/elf64-ppc.c
===================================================================
RCS file: /cvs/src/src/bfd/elf64-ppc.c,v
retrieving revision 1.320
diff -u -p -w -r1.320 elf64-ppc.c
--- bfd/elf64-ppc.c	12 Mar 2010 22:31:16 -0000	1.320
+++ bfd/elf64-ppc.c	14 Mar 2010 01:45:48 -0000
@@ -3820,6 +3820,7 @@ struct ppc_link_hash_table
 
 /* Recursion protection when determining above flag.  */
 #define call_check_in_progress sec_flg4
+#define call_check_done sec_flg5
 
 /* Get the ppc64 ELF linker hash table from a link_info structure.  */
 
@@ -4573,7 +4574,7 @@ ppc64_elf_add_symbol_hook (bfd *ibfd,
   else if (ELF_ST_TYPE (isym->st_info) == STT_FUNC)
     ;
   else if (*sec != NULL
-	   && strcmp (bfd_get_section_name (ibfd, *sec), ".opd") == 0)
+	   && strcmp ((*sec)->name, ".opd") == 0)
     isym->st_info = ELF_ST_INFO (ELF_ST_BIND (isym->st_info), STT_FUNC);
 
   return TRUE;
@@ -4880,7 +4881,7 @@ ppc64_elf_check_relocs (bfd *abfd, struc
 
   sreloc = NULL;
   opd_sym_map = NULL;
-  if (strcmp (bfd_get_section_name (abfd, sec), ".opd") == 0)
+  if (strcmp (sec->name, ".opd") == 0)
     {
       /* Garbage collection needs some extra help with .opd sections.
 	 We don't want to necessarily keep everything referenced by
@@ -8811,7 +8812,7 @@ ppc64_elf_size_dynamic_sections (bfd *ou
 	  /* Strip this section if we don't need it; see the
 	     comment below.  */
 	}
-      else if (CONST_STRNEQ (bfd_get_section_name (dynobj, s), ".rela"))
+      else if (CONST_STRNEQ (s->name, ".rela"))
 	{
 	  if (s->size != 0)
 	    {
@@ -10125,9 +10126,6 @@ ppc64_elf_finish_multitoc_partition (str
 {
   struct ppc_link_hash_table *htab = ppc_hash_table (info);
 
-  if (htab == NULL)
-    return;
-
   /* After the second pass, toc_curr tracks the TOC offset used
      for code sections below in ppc64_elf_next_input_section.  */
   htab->toc_curr = TOC_BASE_OFF;
@@ -10144,10 +10142,10 @@ ppc64_elf_finish_multitoc_partition (str
 static int
 toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
 {
-  Elf_Internal_Rela *relstart, *rel;
-  Elf_Internal_Sym *local_syms;
   int ret;
-  struct ppc_link_hash_table *htab;
+
+  /* Mark this section as checked.  */
+  isec->call_check_done = 1;
 
   /* We know none of our code bearing sections will need toc stubs.  */
   if ((isec->flags & SEC_LINKER_CREATED) != 0)
@@ -10159,8 +10157,12 @@ toc_adjusting_stub_needed (struct bfd_li
   if (isec->output_section == NULL)
     return 0;
 
-  if (isec->reloc_count == 0)
-    return 0;
+  ret = 0;
+  if (isec->reloc_count != 0)
+    {
+      Elf_Internal_Rela *relstart, *rel;
+      Elf_Internal_Sym *local_syms;
+      struct ppc_link_hash_table *htab;
 
   relstart = _bfd_elf_link_read_relocs (isec->owner, isec, NULL, NULL,
 					info->keep_memory);
@@ -10169,7 +10171,6 @@ toc_adjusting_stub_needed (struct bfd_li
 
   /* Look for branches to outside of this section.  */
   local_syms = NULL;
-  ret = 0;
   htab = ppc_hash_table (info);
   if (htab == NULL)
     return -1;
@@ -10217,8 +10218,8 @@ toc_adjusting_stub_needed (struct bfd_li
 	/* Ignore other undefined symbols.  */
 	continue;
 
-      /* Assume branches to other sections not included in the link need
-	 stubs too, to cover -R and absolute syms.  */
+	  /* Assume branches to other sections not included in the
+	     link need stubs too, to cover -R and absolute syms.  */
       if (sym_sec->output_section == NULL)
 	{
 	  ret = 1;
@@ -10282,16 +10283,15 @@ toc_adjusting_stub_needed (struct bfd_li
 	  break;
 	}
 
-      /* If calling back to a section in the process of being tested, we
-	 can't say for sure that no toc adjusting stubs are needed, so
-	 don't return zero.  */
+	  /* If calling back to a section in the process of being
+	     tested, we can't say for sure that no toc adjusting stubs
+	     are needed, so don't return zero.  */
       else if (sym_sec->call_check_in_progress)
 	ret = 2;
 
       /* Branches to another section that itself doesn't have any TOC
 	 references are OK.  Recursively call ourselves to check.  */
-      else if (sym_sec->id <= htab->top_id
-	       && htab->stub_group[sym_sec->id].toc_off == 0)
+	  else if (!sym_sec->call_check_done)
 	{
 	  int recur;
 
@@ -10302,36 +10302,44 @@ toc_adjusting_stub_needed (struct bfd_li
 	  recur = toc_adjusting_stub_needed (info, sym_sec);
 	  isec->call_check_in_progress = 0;
 
-	  if (recur < 0)
-	    {
-	      /* An error.  Exit.  */
-	      ret = -1;
-	      break;
-	    }
-	  else if (recur <= 1)
-	    {
-	      /* Known result.  Mark as checked and set section flag.  */
-	      htab->stub_group[sym_sec->id].toc_off = 1;
 	      if (recur != 0)
 		{
-		  sym_sec->makes_toc_func_call = 1;
-		  ret = 1;
+		  ret = recur;
+		  if (recur != 2)
 		  break;
 		}
 	    }
-	  else
-	    {
-	      /* Unknown result.  Continue checking.  */
-	      ret = 2;
-	    }
-	}
     }
 
   if (local_syms != NULL
-      && (elf_symtab_hdr (isec->owner).contents != (unsigned char *) local_syms))
+	  && (elf_symtab_hdr (isec->owner).contents
+	      != (unsigned char *) local_syms))
     free (local_syms);
   if (elf_section_data (isec)->relocs != relstart)
     free (relstart);
+    }
+
+  if ((ret & 1) == 0
+      && isec->map_head.s != NULL
+      && (strcmp (isec->output_section->name, ".init") == 0
+	  || strcmp (isec->output_section->name, ".fini") == 0))
+    {
+      if (isec->map_head.s->has_toc_reloc
+	  || isec->map_head.s->makes_toc_func_call)
+	ret = 1;
+      else if (!isec->map_head.s->call_check_done)
+	{
+	  int recur;
+	  isec->call_check_in_progress = 1;
+	  recur = toc_adjusting_stub_needed (info, isec->map_head.s);
+	  isec->call_check_in_progress = 0;
+	  if (recur != 0)
+	    ret = recur;
+	}
+    }
+
+  if (ret == 1)
+    isec->makes_toc_func_call = 1;
 
   return ret;
 }
@@ -10377,23 +10385,55 @@ ppc64_elf_next_input_section (struct bfd
 	  if (elf_gp (isec->owner) != 0)
 	    htab->toc_curr = elf_gp (isec->owner);
 	}
-      else if (htab->stub_group[isec->id].toc_off == 0)
-	{
-	  int ret = toc_adjusting_stub_needed (info, isec);
-	  if (ret < 0)
+      else if (!isec->call_check_done
+	       && toc_adjusting_stub_needed (info, isec) < 0)
 	    return FALSE;
-	  else
-	    isec->makes_toc_func_call = ret & 1;
-	}
     }
 
   /* Functions that don't use the TOC can belong in any TOC group.
      Use the last TOC base.  This happens to make _init and _fini
-     pasting work.  */
+     pasting work, because the fragments generally don't use the TOC.  */
   htab->stub_group[isec->id].toc_off = htab->toc_curr;
   return TRUE;
 }
 
+/* Check that all .init and .fini sections use the same toc, if they
+   have toc relocs.  */
+
+static bfd_boolean
+check_pasted_section (struct bfd_link_info *info, const char *name)
+{
+  asection *o = bfd_get_section_by_name (info->output_bfd, name);
+
+  if (o != NULL)
+    {
+      struct ppc_link_hash_table *htab = ppc_hash_table (info);
+      bfd_vma toc_off = 0;
+      asection *i;
+
+      for (i = o->map_head.s; i != NULL; i = i->map_head.s)
+	if (i->has_toc_reloc)
+	  {
+	    if (toc_off == 0)
+	      toc_off = htab->stub_group[i->id].toc_off;
+	    else if (toc_off != htab->stub_group[i->id].toc_off)
+	      return FALSE;
+	  }
+      /* Make sure the whole pasted function uses the same toc offset.  */
+      if (toc_off != 0)
+	for (i = o->map_head.s; i != NULL; i = i->map_head.s)
+	  htab->stub_group[i->id].toc_off = toc_off;
+    }
+  return TRUE;
+}
+
+bfd_boolean
+ppc64_elf_check_init_fini (struct bfd_link_info *info)
+{
+  return (check_pasted_section (info, ".init")
+	  & check_pasted_section (info, ".fini"));
+}
+
 /* See whether we can group stub sections together.  Grouping stub
    sections may result in fewer stubs.  More importantly, we need to
    put all .init* and .fini* stubs at the beginning of the .init or
Index: bfd/elf64-ppc.h
===================================================================
RCS file: /cvs/src/src/bfd/elf64-ppc.h,v
retrieving revision 1.28
diff -u -p -r1.28 elf64-ppc.h
--- bfd/elf64-ppc.h	8 Feb 2010 13:50:16 -0000	1.28
+++ bfd/elf64-ppc.h	14 Mar 2010 01:45:57 -0000
@@ -42,6 +42,8 @@ bfd_boolean ppc64_elf_layout_multitoc
   (struct bfd_link_info *);
 void ppc64_elf_finish_multitoc_partition
   (struct bfd_link_info *);
+bfd_boolean ppc64_elf_check_init_fini
+  (struct bfd_link_info *);
 bfd_boolean ppc64_elf_next_input_section
   (struct bfd_link_info *, asection *);
 bfd_boolean ppc64_elf_size_stubs
Index: ld/emultempl/ppc64elf.em
===================================================================
RCS file: /cvs/src/src/ld/emultempl/ppc64elf.em,v
retrieving revision 1.68
diff -u -p -r1.68 ppc64elf.em
--- ld/emultempl/ppc64elf.em	8 Feb 2010 13:50:17 -0000	1.68
+++ ld/emultempl/ppc64elf.em	14 Mar 2010 01:45:57 -0000
@@ -346,6 +346,9 @@ gld${EMULATION_NAME}_after_allocation (v
 
 	  lang_for_each_statement (build_section_lists);
 
+	  if (!ppc64_elf_check_init_fini (&link_info))
+	    einfo ("%P: .init/.fini fragments use differing TOC pointers\n");
+
 	  /* Call into the BFD backend to do the real work.  */
 	  if (!ppc64_elf_size_stubs (&link_info, group_size))
 	    einfo ("%X%P: can not size stub section: %E\n");

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