PATCH: Fix removed sections (Re: 2.12.90.0.12 Kernel Miscompile)

Alan Modra amodra@bigpond.net.au
Wed Jul 3 22:24:00 GMT 2002


On Wed, Jul 03, 2002 at 07:10:01PM +0100, Richard Sandiford wrote:
> Leonard, thanks for reducing the test case.
> 
> The problem was that we didn't set os->region->current for the removed
> sections, but left os->bfd_section non-null.  So this code in
> lang_do_assignments() still triggered:
> 
> 	    if (os->bfd_section != NULL)
> 	      {
> 		dot = os->bfd_section->vma;
> 		(void) lang_do_assignments (os->children.head, os,
> 					    os->fill, dot);
> 		dot = os->bfd_section->vma + os->bfd_section->_raw_size / opb;
> 
> 	      }
> 
> effectively setting "." to 0.  The patch below fixes the test case,
> and shows no regressions on i686-pc-linux-gnu.  FWIW, I don't mind
> whether we go with this patch or HJ's.

Ah ha!  I'm glad I insisted on understanding the problem before
applying HJ's patch.  I think it may be better to clean up these
dangling os->bfd_section entries a little earlier, since
lang_record_phdrs may have similar problems.  Testing the following,
which cleans up _bfd_strip_section_from_output too, making it safe
(though pointless) to call _bfd_strip_section_from_output later in
the link.  I've been bitten by _bfd_strip_section_from_output a
number of times, and now add comments like this one from elf64-ppc.c:

  /* It would be nice to strip .branch_lt from the output if the
     section is empty, but it's too late.  If we strip sections here,
     the dynamic symbol table is corrupted since the section symbol
     for the stripped section isn't written.  */

bfd/ChangeLog
	* section.c (_bfd_strip_section_from_output): Remove unnecessary
	link order code.  Don't actually remove the output section here;
	Just set a flag for the linker to do so.

ld/ChangeLog
	* ldlang.c (strip_excluded_output_sections): New function.
	(lang_process): Call it.
	(lang_size_sections_1): Revert 2002-06-10 change.

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

Index: bfd/section.c
===================================================================
RCS file: /cvs/src/src/bfd/section.c,v
retrieving revision 1.49
diff -c -p -r1.49 section.c
*** bfd/section.c	25 Jun 2002 09:40:44 -0000	1.49
--- bfd/section.c	4 Jul 2002 02:46:42 -0000
*************** DESCRIPTION
*** 1304,1380 ****
  	becomes empty, remove it from the output bfd.  @var{info} may
  	be NULL; if it is not, it is used to decide whether the output
  	section is empty.
  */
  void
  _bfd_strip_section_from_output (info, s)
       struct bfd_link_info *info;
       asection *s;
  {
!   asection **spp, *os;
!   struct bfd_link_order *p, *pp;
!   boolean keep_os;
! 
!   /* Excise the input section from the link order.
! 
!      FIXME: For all calls that I can see to this function, the link
!      orders have not yet been set up.  So why are we checking them? --
!      Ian */
!   os = s->output_section;
! 
!   /* Handle a section that wasn't output.  */
!   if (os == NULL)
!     return;
  
!   for (p = os->link_order_head, pp = NULL; p != NULL; pp = p, p = p->next)
!     if (p->type == bfd_indirect_link_order
! 	&& p->u.indirect.section == s)
!       {
! 	if (pp)
! 	  pp->next = p->next;
! 	else
! 	  os->link_order_head = p->next;
! 	if (!p->next)
! 	  os->link_order_tail = pp;
! 	break;
!       }
  
!   keep_os = os->link_order_head != NULL;
  
!   if (! keep_os && info != NULL)
      {
        bfd *abfd;
!       for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
! 	{
! 	  asection *is;
! 	  for (is = abfd->sections; is != NULL; is = is->next)
! 	    {
! 	      if (is != s && is->output_section == os
! 		  && (is->flags & SEC_EXCLUDE) == 0)
! 		break;
! 	    }
! 	  if (is != NULL)
! 	    break;
! 	}
!       if (abfd != NULL)
! 	keep_os = true;
!     }
  
!   /* If the output section is empty, remove it too.  Careful about sections
!      that have been discarded in the link script -- they are mapped to
!      bfd_abs_section, which has no owner.  */
!   if (!keep_os && os->owner != NULL)
!     {
!       for (spp = &os->owner->sections; *spp; spp = &(*spp)->next)
! 	if (*spp == os)
! 	  {
! 	    bfd_section_list_remove (os->owner, spp);
! 	    os->flags |= SEC_EXCLUDE;
! 	    os->owner->section_count--;
! 	    break;
! 	  }
      }
  
!   s->flags |= SEC_EXCLUDE;
  }
  
  /*
--- 1304,1346 ----
  	becomes empty, remove it from the output bfd.  @var{info} may
  	be NULL; if it is not, it is used to decide whether the output
  	section is empty.
+ 
+ 	This function won't actually do anything except twiddle flags
+ 	if called too late in the linking process, when it's not safe
+ 	to remove sections.
  */
  void
  _bfd_strip_section_from_output (info, s)
       struct bfd_link_info *info;
       asection *s;
  {
!   asection *os;
  
!   s->flags |= SEC_EXCLUDE;
  
!   /* If the section wasn't assigned to an output section, or the
!      section has been discarded by the linker script, there's nothing
!      more to do.  */
!   os = s->output_section;
!   if (os == NULL || os->owner == NULL)
!     return;
  
!   /* If the output section has other (non-excluded) input sections, we
!      can't remove the output section.  */
!   if (info != NULL)
      {
        bfd *abfd;
!       asection *is;
  
!       for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
! 	for (is = abfd->sections; is != NULL; is = is->next)
! 	  if (is->output_section == os && (is->flags & SEC_EXCLUDE) == 0)
! 	    return;
      }
  
!   /* If the output section is empty, flag it for removal too.
!      See ldlang.c:strip_excluded_output_sections for the action.  */
!   os->flags |= SEC_EXCLUDE;
  }
  
  /*
Index: ld/ldlang.c
===================================================================
RCS file: /cvs/src/src/ld/ldlang.c,v
retrieving revision 1.91
diff -u -p -r1.91 ldlang.c
--- ld/ldlang.c	1 Jul 2002 08:07:29 -0000	1.91
+++ ld/ldlang.c	4 Jul 2002 02:44:23 -0000
@@ -97,6 +97,7 @@ static void lang_place_undefineds PARAMS
 static void map_input_to_output_sections
   PARAMS ((lang_statement_union_type *, const char *,
 	   lang_output_section_statement_type *));
+static void strip_excluded_output_sections PARAMS ((void));
 static void print_output_section_statement
   PARAMS ((lang_output_section_statement_type *));
 static void print_assignment
@@ -2161,6 +2162,41 @@ map_input_to_output_sections (s, target,
     }
 }
 
+/* An output section might have been removed after its statement was
+   added.  For example, ldemul_before_allocation can remove dynamic
+   sections if they turn out to be not needed.  Clean them up here.  */
+
+static void
+strip_excluded_output_sections ()
+{
+  lang_statement_union_type *u;
+
+  for (u = lang_output_section_statement.head;
+       u != NULL;
+       u = u->output_section_statement.next)
+    {
+      lang_output_section_statement_type *os;
+      asection *s;
+
+      os = &u->output_section_statement;
+      s = os->bfd_section;
+      if (s != NULL && (s->flags & SEC_EXCLUDE) != 0)
+	{
+	  asection **p;
+
+	  os->bfd_section = NULL;
+
+	  for (p = &output_bfd->sections; *p; p = &(*p)->next)
+	    if (*p == s)
+	      {
+		bfd_section_list_remove (output_bfd, p);
+		output_bfd->section_count--;
+		break;
+	      }
+	}
+    }
+}
+
 static void
 print_output_section_statement (output_section_statement)
      lang_output_section_statement_type *output_section_statement;
@@ -2903,14 +2939,6 @@ lang_size_sections_1 (s, output_section_
 	      /* This section was never actually created.  */
 	      break;
 
-	    /* The section might have been removed after its statement was
-	       added.  For example, ldemul_before_allocation can remove
-	       dynamic sections if they turn out not to be needed.  */
-	    if (!link_info.relocateable
-		&& (bfd_get_section_flags (output_bfd, os->bfd_section)
-		    & SEC_EXCLUDE) != 0)
-	      break;
-
 	    /* If this is a COFF shared library section, use the size and
 	       address from the input section.  FIXME: This is COFF
 	       specific; it would be cleaner if there were some other way
@@ -4251,6 +4279,9 @@ lang_process ()
   /* Do anything special before sizing sections.  This is where ELF
      and other back-ends size dynamic sections.  */
   ldemul_before_allocation ();
+
+  if (!link_info.relocateable)
+    strip_excluded_output_sections ();
 
   /* We must record the program headers before we try to fix the
      section positions, since they will affect SIZEOF_HEADERS.  */



More information about the Binutils mailing list