This is the mail archive of the binutils@sources.redhat.com 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]

fix pr 63, rewrite orphan section code


PR 63 is another place_orphan failure.  The ld-scripts/provide-1 test
uses a linker script that only specifies .data section placement:
	SECTIONS 
	{
	  .data 0x2000 :
	  {
	    LONG (foo)
	    LONG (bar)
	    . = ALIGN (0x10);
	    *(.data)
	  }
	  PROVIDE (foo = .);
	  PROVIDE (bar = .);
	}

Thus all other sections are placed as orphans.  The source assembly for
this test only specifies a .data section, but the assembler creates
.text, .data, and .bss whether you ask for them or not.  So place_orphan
had some work to do, and chose to put the .text and .bss output section
statements immediately after the .data one, before the PROVIDE
statements.  Since these sections are empty, not much happens *except*
alignment.  Unfortunately, PE sections are aligned to a page boundary.
Which puts the PROVIDEd foo and bar at rather more than 0x2020.

I started out fixing this by scanning through statements looking for
another output section statement, or the end of statements that might
go in SECTIONS.  Doing that exposed other problems, and snowballed
into this rewrite.

Of note is a completely revised scheme to place orphans according to
their section flags, and merging of elf32.em, pe.em and mmo.em code
to ldlang.c.  I probably should have put the new code in a separate
file.  Ideal would be to create a libemul.a that allows sharing of
code.  An --enable-targets=all build currently gives you 115 copies
of identical ELF place_orphan functions..

ld/
	PR 63
	* ldlang.h (lang_output_section_statement_type): Make "next" a
	struct lang_output_section_statement_struct *.
	(struct orphan_save): Move from elf32.em.  Add "name" and "flags".
	(lang_output_section_find_by_flags, lang_insert_orphan): Declare.
	* ldlang.c (lang_output_section_find_1): Adjust for changed
	output_section_statement "next".
	(strip_excluded_output_sections): Likewise.
	(lang_record_phdrs): Likewise.
	(lang_output_section_find_by_flags): New function.
	(output_prev_sec_find): Move from pe.em.  Adjust iterator.
	(lang_insert_orphan): New function.  Tail end of elf32.em's
	place_orphan merged with that from pe.em.  Allow bfd_section to
	be placed first.  New heuristic for placing new output section
	statement in existing script, and accompanying split of __start
	symbol alignment into a separate assignment to dot.
	(lang_add_section): Consistently use output->bfd_section rather than
	an alias, section->output_section.
	(map_input_to_output_sections): Rename overly long arg.  Move
	initialization of data_statement output section to here..
	(lang_check_section_addresses): ..from here.
	(print_assignment): Correct printing of etree_assert.
	(print_all_symbols): Don't bomb if userdata is NULL.
	(IGNORE_SECTION): Rearrange.
	* emultempl/elf32.em (output_rel_find): Adjust interator.
	(output_prev_sec_find): Delete.
	(struct orphan_save): Delete.
	(gld${EMULATION_NAME}_place_orphan): Cater for zero bfd_section
	flags without creating a duplicate output section statement.
	Revise code holding history of various orphan section placements.
	Allow orphan sections to place before script specified output
	sections.  Call lang_output_section_find_by_flags when placement
	by name fails.  Use lang_insert_orphan.
	* emultempl/mmo.em (output_prev_sec_find): Delete.
	(struct orphan_save): Delete.
	(mmo_place_orphan): Revise code holding history of orphan placement.
	Allow orphans to place before existing output sections.  Use
	lang_insert_orphan.
	* emultempl/pe.em (output_prev_sec_find): Delete.
	(struct orphan_save): Delete.
	(gld_${EMULATION_NAME}_place_orphan): Revise to suit use of
	lang_insert_orphan.
ld/testsuite/
	* ld-scripts/overlay-size.d: Update for changed orphan section
	placement.
	* ld-mmix/bpo-18.d: Likewise.

Index: ld/ldlang.h
===================================================================
RCS file: /cvs/src/src/ld/ldlang.h,v
retrieving revision 1.39
diff -u -p -r1.39 ldlang.h
--- ld/ldlang.h	19 Jul 2004 16:40:52 -0000	1.39
+++ ld/ldlang.h	14 Oct 2004 10:34:50 -0000
@@ -131,7 +131,7 @@ typedef struct lang_output_section_state
   union etree_union *addr_tree;
   lang_statement_list_type children;
   const char *memspec;
-  union lang_statement_union *next;
+  struct lang_output_section_statement_struct *next;
   const char *name;
 
   int processed;
@@ -410,6 +410,17 @@ struct lang_definedness_hash_entry
   int iteration;
 };
 
+/* Used by place_orphan to keep track of orphan sections and statements.  */
+
+struct orphan_save {
+  const char *name;
+  flagword flags;
+  lang_output_section_statement_type *os;
+  asection **section;
+  lang_statement_union_type **stmt;
+  lang_output_section_statement_type **os_tail;
+};
+
 extern struct unique_sections *unique_section_list;
 
 extern lang_output_section_statement_type *abs_output_section;
@@ -501,6 +512,12 @@ extern void ldlang_add_file
   (lang_input_statement_type *);
 extern lang_output_section_statement_type *lang_output_section_find
   (const char * const);
+extern lang_output_section_statement_type *lang_output_section_find_by_flags
+  (const asection *, lang_output_section_statement_type **exact);
+extern lang_output_section_statement_type *lang_insert_orphan
+  (lang_input_statement_type *, asection *, const char *,
+   lang_output_section_statement_type *, struct orphan_save *,
+   etree_type *, lang_statement_list_type *);
 extern lang_input_statement_type *lang_add_input_file
   (const char *, lang_input_file_enum_type, const char *);
 extern void lang_add_keepsyms_file
Index: ld/ldlang.c
===================================================================
RCS file: /cvs/src/src/ld/ldlang.c,v
retrieving revision 1.164
diff -u -p -r1.164 ldlang.c
--- ld/ldlang.c	4 Oct 2004 16:45:51 -0000	1.164
+++ ld/ldlang.c	14 Oct 2004 10:34:50 -0000
@@ -612,13 +612,12 @@ lang_memory_default (asection *section)
 static lang_output_section_statement_type *
 lang_output_section_find_1 (const char *const name, int constraint)
 {
-  lang_statement_union_type *u;
   lang_output_section_statement_type *lookup;
 
-  for (u = lang_output_section_statement.head; u != NULL; u = lookup->next)
+  for (lookup = &lang_output_section_statement.head->output_section_statement;
+       lookup != NULL;
+       lookup = lookup->next)
     {
-      lookup = &u->output_section_statement;
-      
       if (strcmp (name, lookup->name) == 0
 	  && lookup->constraint != -1
 	  && (constraint == 0 || constraint == lookup->constraint))
@@ -666,7 +665,7 @@ lang_output_section_statement_lookup_1 (
 
       lang_statement_append (&lang_output_section_statement,
 			     (lang_statement_union_type *) lookup,
-			     &lookup->next);
+			     (lang_statement_union_type **) &lookup->next);
     }
   return lookup;
 }
@@ -677,6 +676,389 @@ lang_output_section_statement_lookup (co
   return lang_output_section_statement_lookup_1 (name, 0);
 }
 
+/* A variant of lang_output_section_find used by place_orphan.
+   Returns the output statement that should precede a new output
+   statement for SEC.  If an exact match is found on certain flags,
+   sets *EXACT too.  */
+
+lang_output_section_statement_type *
+lang_output_section_find_by_flags (const asection *sec,
+				   lang_output_section_statement_type **exact)
+{
+  lang_output_section_statement_type *first, *look, *found;
+  flagword flags;
+
+  /* We know the first statement on this list is *ABS*.  May as well
+     skip it.  */
+  first = &lang_output_section_statement.head->output_section_statement;
+  first = first->next;
+
+  /* First try for an exact match.  */
+  found = NULL;
+  for (look = first; look; look = look->next)
+    {
+      flags = look->flags;
+      if (look->bfd_section != NULL)
+	flags = look->bfd_section->flags;
+      flags ^= sec->flags;
+      if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY
+		     | SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
+	found = look;
+    }
+  if (found != NULL)
+    {
+      *exact = found;
+      return found;
+    }
+
+  if (sec->flags & SEC_CODE)
+    {
+      /* Try for a rw code section.  */
+      for (look = first; look; look = look->next)
+	{
+	  flags = look->flags;
+	  if (look->bfd_section != NULL)
+	    flags = look->bfd_section->flags;
+	  flags ^= sec->flags;
+	  if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+			 | SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
+	    found = look;
+	}
+      return found;
+    }
+
+  if (sec->flags & (SEC_READONLY | SEC_THREAD_LOCAL))
+    {
+      /* .rodata can go after .text, .sdata2 after .rodata.  */
+      for (look = first; look; look = look->next)
+	{
+	  flags = look->flags;
+	  if (look->bfd_section != NULL)
+	    flags = look->bfd_section->flags;
+	  flags ^= sec->flags;
+	  if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+			 | SEC_READONLY))
+	      && !(look->flags & (SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
+	    found = look;
+	}
+      return found;
+    }
+
+  if (sec->flags & SEC_SMALL_DATA)
+    {
+      /* .sdata goes after .data, .sbss after .sdata.  */
+      for (look = first; look; look = look->next)
+	{
+	  flags = look->flags;
+	  if (look->bfd_section != NULL)
+	    flags = look->bfd_section->flags;
+	  flags ^= sec->flags;
+	  if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+			 | SEC_THREAD_LOCAL))
+	      || ((look->flags & SEC_SMALL_DATA)
+		  && !(sec->flags & SEC_HAS_CONTENTS)))
+	    found = look;
+	}
+      return found;
+    }
+
+  if (sec->flags & SEC_HAS_CONTENTS)
+    {
+      /* .data goes after .rodata.  */
+      for (look = first; look; look = look->next)
+	{
+	  flags = look->flags;
+	  if (look->bfd_section != NULL)
+	    flags = look->bfd_section->flags;
+	  flags ^= sec->flags;
+	  if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+			 | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
+	    found = look;
+	}
+      return found;
+    }
+
+  /* .bss goes last.  */
+  for (look = first; look; look = look->next)
+    {
+      flags = look->flags;
+      if (look->bfd_section != NULL)
+	flags = look->bfd_section->flags;
+      flags ^= sec->flags;
+      if (!(flags & SEC_ALLOC))
+	found = look;
+    }
+
+  return found;
+}
+
+/* Find the last output section before given output statement.
+   Used by place_orphan.  */
+
+static asection *
+output_prev_sec_find (lang_output_section_statement_type *os)
+{
+  asection *s = (asection *) NULL;
+  lang_output_section_statement_type *lookup;
+
+  for (lookup = &lang_output_section_statement.head->output_section_statement;
+       lookup != NULL;
+       lookup = lookup->next)
+    {
+      if (lookup->constraint == -1)
+	continue;
+      if (lookup == os)
+	return s;
+
+      if (lookup->bfd_section != NULL && lookup->bfd_section->owner != NULL)
+	s = lookup->bfd_section;
+    }
+
+  return NULL;
+}
+
+lang_output_section_statement_type *
+lang_insert_orphan (lang_input_statement_type *file,
+		    asection *s,
+		    const char *secname,
+		    lang_output_section_statement_type *after,
+		    struct orphan_save *place,
+		    etree_type *address,
+		    lang_statement_list_type *add_child)
+{
+  lang_statement_list_type *old;
+  lang_statement_list_type add;
+  const char *ps;
+  etree_type *load_base;
+  lang_output_section_statement_type *os;
+  lang_output_section_statement_type **os_tail;
+
+  /* Start building a list of statements for this section.
+     First save the current statement pointer.  */
+  old = stat_ptr;
+
+  /* If we have found an appropriate place for the output section
+     statements for this orphan, add them to our own private list,
+     inserting them later into the global statement list.  */
+  if (after != NULL)
+    {
+      stat_ptr = &add;
+      lang_list_init (stat_ptr);
+    }
+
+  ps = NULL;
+  if (config.build_constructors)
+    {
+      /* If the name of the section is representable in C, then create
+	 symbols to mark the start and the end of the section.  */
+      for (ps = secname; *ps != '\0'; ps++)
+	if (! ISALNUM ((unsigned char) *ps) && *ps != '_')
+	  break;
+      if (*ps == '\0')
+	{
+	  char *symname;
+	  etree_type *e_align;
+
+	  symname = (char *) xmalloc (ps - secname + sizeof "__start_" + 1);
+	  symname[0] = bfd_get_symbol_leading_char (output_bfd);
+	  sprintf (symname + (symname[0] != 0), "__start_%s", secname);
+	  e_align = exp_unop (ALIGN_K,
+			      exp_intop ((bfd_vma) 1 << s->alignment_power));
+	  lang_add_assignment (exp_assop ('=', ".", e_align));
+	  lang_add_assignment (exp_assop ('=', symname,
+					  exp_nameop (NAME, ".")));
+	}
+    }
+
+  if (link_info.relocatable || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0)
+    address = exp_intop (0);
+
+  load_base = NULL;
+  if (after != NULL && after->load_base != NULL)
+    {
+      etree_type *lma_from_vma;
+      lma_from_vma = exp_binop ('-', after->load_base,
+				exp_nameop (ADDR, after->name));
+      load_base = exp_binop ('+', lma_from_vma,
+			     exp_nameop (ADDR, secname));
+    }
+
+  os_tail = ((lang_output_section_statement_type **)
+	     lang_output_section_statement.tail);
+  os = lang_enter_output_section_statement (secname, address, 0, NULL, NULL,
+					    load_base, 0);
+
+  if (add_child == NULL)
+    add_child = &os->children;
+  lang_add_section (add_child, s, os, file);
+
+  lang_leave_output_section_statement (0, "*default*", NULL, NULL);
+
+  if (config.build_constructors && *ps == '\0')
+    {
+      char *symname;
+
+      /* lang_leave_ouput_section_statement resets stat_ptr.
+	 Put stat_ptr back where we want it.  */
+      if (after != NULL)
+	stat_ptr = &add;
+
+      symname = (char *) xmalloc (ps - secname + sizeof "__stop_" + 1);
+      symname[0] = bfd_get_symbol_leading_char (output_bfd);
+      sprintf (symname + (symname[0] != 0), "__stop_%s", secname);
+      lang_add_assignment (exp_assop ('=', symname,
+				      exp_nameop (NAME, ".")));
+    }
+
+  /* Restore the global list pointer.  */
+  if (after != NULL)
+    stat_ptr = old;
+
+  if (after != NULL && os->bfd_section != NULL)
+    {
+      asection *snew, **pps;
+
+      snew = os->bfd_section;
+
+      /* Shuffle the bfd section list to make the output file look
+	 neater.  This is really only cosmetic.  */
+      if (place->section == NULL
+	  && after != (&lang_output_section_statement.head
+		       ->output_section_statement))
+	{
+	  asection *bfd_section = after->bfd_section;
+
+	  /* If the output statement hasn't been used to place any input
+	     sections (and thus doesn't have an output bfd_section),
+	     look for the closest prior output statement having an
+	     output section.  */
+	  if (bfd_section == NULL)
+	    bfd_section = output_prev_sec_find (after);
+
+	  if (bfd_section != NULL && bfd_section != snew)
+	    place->section = &bfd_section->next;
+	}
+
+      if (place->section == NULL)
+	place->section = &output_bfd->sections;
+
+      /* Unlink the section.  */
+      for (pps = &output_bfd->sections; *pps != snew; pps = &(*pps)->next)
+	continue;
+      bfd_section_list_remove (output_bfd, pps);
+
+      /* Now tack it back on in the right place.  */
+      bfd_section_list_insert (output_bfd, place->section, snew);
+
+      /* Save the end of this list.  Further ophans of this type will
+	 follow the one we've just added.  */
+      place->section = &snew->next;
+
+      /* The following is non-cosmetic.  We try to put the output
+	 statements in some sort of reasonable order here, because they
+	 determine the final load addresses of the orphan sections.
+	 In addition, placing output statements in the wrong order may
+	 require extra segments.  For instance, given a typical
+	 situation of all read-only sections placed in one segment and
+	 following that a segment containing all the read-write
+	 sections, we wouldn't want to place an orphan read/write
+	 section before or amongst the read-only ones.  */
+      if (add.head != NULL)
+	{
+	  lang_output_section_statement_type *newly_added_os;
+
+	  if (place->stmt == NULL)
+	    {
+	      lang_statement_union_type **where;
+	      lang_statement_union_type **assign = NULL;
+
+	      /* Look for a suitable place for the new statement list.
+		 The idea is to skip over anything that might be inside
+		 a SECTIONS {} statement in a script, before we find
+		 another output_section_statement.  Assignments to "dot"
+		 before an output section statement are assumed to
+		 belong to it.  */
+	      for (where = &after->header.next;
+		   *where != NULL;
+		   where = &(*where)->header.next)
+		{
+		  switch ((*where)->header.type)
+		    {
+		    case lang_assignment_statement_enum:
+		      if (assign == NULL)
+			{
+			  lang_assignment_statement_type *ass;
+			  ass = &(*where)->assignment_statement;
+			  if (ass->exp->type.node_class != etree_assert
+			      && ass->exp->assign.dst[0] == '.'
+			      && ass->exp->assign.dst[1] == 0)
+			    assign = where;
+			}
+		      continue;
+		    case lang_wild_statement_enum:
+		    case lang_input_section_enum:
+		    case lang_object_symbols_statement_enum:
+		    case lang_fill_statement_enum:
+		    case lang_data_statement_enum:
+		    case lang_reloc_statement_enum:
+		    case lang_padding_statement_enum:
+		    case lang_constructors_statement_enum:
+		      assign = NULL;
+		      continue;
+		    case lang_output_section_statement_enum:
+		      if (assign != NULL)
+			where = assign;
+		    case lang_input_statement_enum:
+		    case lang_address_statement_enum:
+		    case lang_target_statement_enum:
+		    case lang_output_statement_enum:
+		    case lang_group_statement_enum:
+		    case lang_afile_asection_pair_statement_enum:
+		      break;
+		    }
+		  break;
+		}
+
+	      *add.tail = *where;
+	      *where = add.head;
+
+	      place->os_tail = &after->next;
+	    }
+	  else
+	    {
+	      /* Put it after the last orphan statement we added.  */
+	      *add.tail = *place->stmt;
+	      *place->stmt = add.head;
+	    }
+
+	  /* Fix the global list pointer if we happened to tack our
+	     new list at the tail.  */
+	  if (*old->tail == add.head)
+	    old->tail = add.tail;
+
+	  /* Save the end of this list.  */
+	  place->stmt = add.tail;
+
+	  /* Do the same for the list of output section statements.  */
+	  newly_added_os = *os_tail;
+	  *os_tail = NULL;
+	  newly_added_os->next = *place->os_tail;
+	  *place->os_tail = newly_added_os;
+	  place->os_tail = &newly_added_os->next;
+
+	  /* Fixing the global list pointer here is a little different.
+	     We added to the list in lang_enter_output_section_statement, 
+	     trimmed off the new output_section_statment above when
+	     assigning *os_tail = NULL, but possibly added it back in
+	     the same place when assigning *place->os_tail.  */
+	  if (*os_tail == NULL)
+	    lang_output_section_statement.tail
+	      = (lang_statement_union_type **) os_tail;
+	}
+    }
+  return os;
+}
+
 static void
 lang_map_flags (flagword flag)
 {
@@ -994,29 +1376,29 @@ lang_add_section (lang_statement_list_ty
          flag is not currently set, then don't set it just because the
          input section has it set.  */
 
-      if (! first && (section->output_section->flags & SEC_READONLY) == 0)
+      if (! first && (output->bfd_section->flags & SEC_READONLY) == 0)
 	flags &= ~ SEC_READONLY;
 
       /* Keep SEC_MERGE and SEC_STRINGS only if they are the same.  */
       if (! first
-	  && ((section->output_section->flags & (SEC_MERGE | SEC_STRINGS))
+	  && ((output->bfd_section->flags & (SEC_MERGE | SEC_STRINGS))
 	      != (flags & (SEC_MERGE | SEC_STRINGS))
 	      || ((flags & SEC_MERGE)
-		  && section->output_section->entsize != section->entsize)))
+		  && output->bfd_section->entsize != section->entsize)))
 	{
-	  section->output_section->flags &= ~ (SEC_MERGE | SEC_STRINGS);
+	  output->bfd_section->flags &= ~ (SEC_MERGE | SEC_STRINGS);
 	  flags &= ~ (SEC_MERGE | SEC_STRINGS);
 	}
 
-      section->output_section->flags |= flags;
+      output->bfd_section->flags |= flags;
 
       if (flags & SEC_MERGE)
-	section->output_section->entsize = section->entsize;
+	output->bfd_section->entsize = section->entsize;
 
       /* If SEC_READONLY is not set in the input section, then clear
          it from the output section.  */
       if ((section->flags & SEC_READONLY) == 0)
-	section->output_section->flags &= ~SEC_READONLY;
+	output->bfd_section->flags &= ~SEC_READONLY;
 
       switch (output->sectype)
 	{
@@ -1036,7 +1418,7 @@ lang_add_section (lang_statement_list_ty
 
       /* Copy over SEC_SMALL_DATA.  */
       if (section->flags & SEC_SMALL_DATA)
-	section->output_section->flags |= SEC_SMALL_DATA;
+	output->bfd_section->flags |= SEC_SMALL_DATA;
 
       if (section->alignment_power > output->bfd_section->alignment_power)
 	output->bfd_section->alignment_power = section->alignment_power;
@@ -1047,7 +1429,7 @@ lang_add_section (lang_statement_list_ty
 
       if (section->flags & SEC_BLOCK)
 	{
-	  section->output_section->flags |= SEC_BLOCK;
+	  output->bfd_section->flags |= SEC_BLOCK;
 	  /* FIXME: This value should really be obtained from the bfd...  */
 	  output->block_value = 128;
 	}
@@ -2125,19 +2515,19 @@ update_wild_statements (lang_statement_u
 static void
 map_input_to_output_sections
   (lang_statement_union_type *s, const char *target,
-   lang_output_section_statement_type *output_section_statement)
+   lang_output_section_statement_type *os)
 {
   for (; s != NULL; s = s->header.next)
     {
       switch (s->header.type)
 	{
 	case lang_wild_statement_enum:
-	  wild (&s->wild_statement, target, output_section_statement);
+	  wild (&s->wild_statement, target, os);
 	  break;
 	case lang_constructors_statement_enum:
 	  map_input_to_output_sections (constructor_list.head,
 					target,
-					output_section_statement);
+					os);
 	  break;
 	case lang_output_section_statement_enum:
 	  if (s->output_section_statement.constraint)
@@ -2169,27 +2559,32 @@ map_input_to_output_sections
 	case lang_group_statement_enum:
 	  map_input_to_output_sections (s->group_statement.children.head,
 					target,
-					output_section_statement);
+					os);
 	  break;
 	case lang_data_statement_enum:
 	  /* Make sure that any sections mentioned in the expression
 	     are initialized.  */
-	  exp_init_os (s->data_statement.exp);
-	  /* FALLTHROUGH */
+	  exp_init_os (s->data_statement.exp);	
+	  if (os != NULL && os->bfd_section == NULL)
+	    init_os (os);
+	  /* The output section gets contents, and then we inspect for
+	     any flags set in the input script which override any ALLOC.  */
+	  os->bfd_section->flags |= SEC_HAS_CONTENTS;
+	  if (!(os->flags & SEC_NEVER_LOAD))
+	    os->bfd_section->flags |= SEC_ALLOC | SEC_LOAD;
+	  break;
 	case lang_fill_statement_enum:
 	case lang_input_section_enum:
 	case lang_object_symbols_statement_enum:
 	case lang_reloc_statement_enum:
 	case lang_padding_statement_enum:
 	case lang_input_statement_enum:
-	  if (output_section_statement != NULL
-	      && output_section_statement->bfd_section == NULL)
-	    init_os (output_section_statement);
+	  if (os != NULL && os->bfd_section == NULL)
+	    init_os (os);
 	  break;
 	case lang_assignment_statement_enum:
-	  if (output_section_statement != NULL
-	      && output_section_statement->bfd_section == NULL)
-	    init_os (output_section_statement);
+	  if (os != NULL && os->bfd_section == NULL)
+	    init_os (os);
 
 	  /* Make sure that any sections mentioned in the assignment
 	     are initialized.  */
@@ -2201,13 +2596,13 @@ map_input_to_output_sections
 	case lang_address_statement_enum:
 	  /* Mark the specified section with the supplied address.  */
 	  {
-	    lang_output_section_statement_type *os =
-	      lang_output_section_statement_lookup
-		(s->address_statement.section_name);
-
-	    if (os->bfd_section == NULL)
-	      init_os (os);
-	    os->addr_tree = s->address_statement.address;
+	    lang_output_section_statement_type *aos
+	      = (lang_output_section_statement_lookup
+		 (s->address_statement.section_name));
+
+	    if (aos->bfd_section == NULL)
+	      init_os (aos);
+	    aos->addr_tree = s->address_statement.address;
 	  }
 	  break;
 	}
@@ -2221,16 +2616,14 @@ map_input_to_output_sections
 static void
 strip_excluded_output_sections (void)
 {
-  lang_statement_union_type *u;
+  lang_output_section_statement_type *os;
 
-  for (u = lang_output_section_statement.head;
-       u != NULL;
-       u = u->output_section_statement.next)
+  for (os = &lang_output_section_statement.head->output_section_statement;
+       os != NULL;
+       os = os->next)
     {
-      lang_output_section_statement_type *os;
       asection *s;
 
-      os = &u->output_section_statement;
       if (os->constraint == -1)
 	continue;
       s = os->bfd_section;
@@ -2302,23 +2695,35 @@ print_assignment (lang_assignment_statem
 		  lang_output_section_statement_type *output_section)
 {
   int i;
+  int is_dot;
+  etree_type *tree;
   etree_value_type result;
 
   for (i = 0; i < SECTION_NAME_MAP_LENGTH; i++)
     print_space ();
 
-  result = exp_fold_tree (assignment->exp->assign.src, output_section,
-			  lang_final_phase_enum, print_dot, &print_dot);
+  if (assignment->exp->type.node_class == etree_assert)
+    {
+      is_dot = 0;
+      tree = assignment->exp->assert_s.child;
+    }
+  else
+    {
+      const char *dst = assignment->exp->assign.dst;
+      is_dot = dst[0] == '.' && dst[1] == 0;
+      tree = assignment->exp->assign.src;
+    }
+
+  result = exp_fold_tree (tree, output_section, lang_final_phase_enum,
+			  print_dot, &print_dot);
   if (result.valid_p)
     {
-      const char *dst;
       bfd_vma value;
 
       value = result.value + result.section->bfd_section->vma;
-      dst = assignment->exp->assign.dst;
 
       minfo ("0x%V", value);
-      if (dst[0] == '.' && dst[1] == 0)
+      if (is_dot)
 	print_dot = value;
     }
   else
@@ -2330,9 +2735,7 @@ print_assignment (lang_assignment_statem
     }
 
   minfo ("                ");
-
   exp_print_tree (assignment->exp);
-
   print_nl ();
 }
 
@@ -2379,6 +2782,9 @@ print_all_symbols (sec)
   struct fat_user_section_struct *ud = get_userdata (sec);
   struct map_symbol_def *def;
 
+  if (!ud)
+    return;
+
   *ud->map_symbol_def_tail = 0;
   for (def = ud->map_symbol_def_head; def; def = def->next)
     print_one_symbol (def->entry, sec);
@@ -2863,14 +3269,15 @@ size_input_section
 }
 
 #define IGNORE_SECTION(s) \
-  (((s->flags & SEC_THREAD_LOCAL) != 0				\
-    ? (s->flags & (SEC_LOAD | SEC_NEVER_LOAD)) != SEC_LOAD	\
-    : (s->flags & (SEC_ALLOC | SEC_NEVER_LOAD)) != SEC_ALLOC)	\
+  ((s->flags & SEC_NEVER_LOAD) != 0				\
+   || (s->flags & SEC_ALLOC) == 0				\
+   || ((s->flags & SEC_THREAD_LOCAL) != 0			\
+	&& (s->flags & SEC_LOAD) == 0)				\
    || s->size == 0)
 
 /* Check to see if any allocated sections overlap with other allocated
-   sections.  This can happen when the linker script specifically specifies
-   the output section addresses of the two sections.  */
+   sections.  This can happen if a linker script specifies the output
+   section addresses of the two sections.  */
 
 static void
 lang_check_section_addresses (void)
@@ -3220,14 +3627,6 @@ lang_size_sections_1
 	      size = TO_SIZE ((unsigned) 1);
 	    dot += TO_ADDR (size);
 	    output_section_statement->bfd_section->size += size;
-	    /* The output section gets contents, and then we inspect for
-	       any flags set in the input script which override any ALLOC.  */
-	    output_section_statement->bfd_section->flags |= SEC_HAS_CONTENTS;
-	    if (!(output_section_statement->flags & SEC_NEVER_LOAD))
-	      {
-		output_section_statement->bfd_section->flags |=
-		  SEC_ALLOC | SEC_LOAD;
-	      }
 	  }
 	  break;
 
@@ -4855,7 +5254,7 @@ lang_record_phdrs (void)
   asection **secs;
   lang_output_section_phdr_list *last;
   struct lang_phdr *l;
-  lang_statement_union_type *u;
+  lang_output_section_statement_type *os;
 
   alc = 10;
   secs = xmalloc (alc * sizeof (asection *));
@@ -4867,14 +5266,12 @@ lang_record_phdrs (void)
       bfd_vma at;
 
       c = 0;
-      for (u = lang_output_section_statement.head;
-	   u != NULL;
-	   u = u->output_section_statement.next)
+      for (os = &lang_output_section_statement.head->output_section_statement;
+	   os != NULL;
+	   os = os->next)
 	{
-	  lang_output_section_statement_type *os;
 	  lang_output_section_phdr_list *pl;
 
-	  os = &u->output_section_statement;
 	  if (os->constraint == -1)
 	    continue;
 
@@ -4930,22 +5327,22 @@ lang_record_phdrs (void)
   free (secs);
 
   /* Make sure all the phdr assignments succeeded.  */
-  for (u = lang_output_section_statement.head;
-       u != NULL;
-       u = u->output_section_statement.next)
+  for (os = &lang_output_section_statement.head->output_section_statement;
+       os != NULL;
+       os = os->next)
     {
       lang_output_section_phdr_list *pl;
 
-      if (u->output_section_statement.constraint == -1
-	  || u->output_section_statement.bfd_section == NULL)
+      if (os->constraint == -1
+	  || os->bfd_section == NULL)
 	continue;
 
-      for (pl = u->output_section_statement.phdrs;
+      for (pl = os->phdrs;
 	   pl != NULL;
 	   pl = pl->next)
 	if (! pl->used && strcmp (pl->name, "NONE") != 0)
 	  einfo (_("%X%P: section `%s' assigned to non-existent phdr `%s'\n"),
-		 u->output_section_statement.name, pl->name);
+		 os->name, pl->name);
     }
 }
 
Index: ld/emultempl/elf32.em
===================================================================
RCS file: /cvs/src/src/ld/emultempl/elf32.em,v
retrieving revision 1.123
diff -u -p -r1.123 elf32.em
--- ld/emultempl/elf32.em	11 Oct 2004 14:42:30 -0000	1.123
+++ ld/emultempl/elf32.em	14 Oct 2004 10:34:51 -0000
@@ -1172,12 +1172,11 @@ fi
 if test x"$LDEMUL_PLACE_ORPHAN" != xgld"$EMULATION_NAME"_place_orphan; then
 cat >>e${EMULATION_NAME}.c <<EOF
 
-/* A variant of lang_output_section_find.  Used by place_orphan.  */
+/* A variant of lang_output_section_find used by place_orphan.  */
 
 static lang_output_section_statement_type *
 output_rel_find (asection *sec, int isdyn)
 {
-  lang_statement_union_type *u;
   lang_output_section_statement_type *lookup;
   lang_output_section_statement_type *last = NULL;
   lang_output_section_statement_type *last_alloc = NULL;
@@ -1185,9 +1184,10 @@ output_rel_find (asection *sec, int isdy
   lang_output_section_statement_type *last_rel_alloc = NULL;
   int rela = sec->name[4] == 'a';
 
-  for (u = lang_output_section_statement.head; u; u = lookup->next)
+  for (lookup = &lang_output_section_statement.head->output_section_statement;
+       lookup != NULL;
+       lookup = lookup->next)
     {
-      lookup = &u->output_section_statement;
       if (lookup->constraint != -1
 	  && strncmp (".rel", lookup->name, 4) == 0)
 	{
@@ -1229,63 +1229,52 @@ output_rel_find (asection *sec, int isdy
   return last;
 }
 
-/* Find the last output section before given output statement.
-   Used by place_orphan.  */
-
-static asection *
-output_prev_sec_find (lang_output_section_statement_type *os)
-{
-  asection *s = (asection *) NULL;
-  lang_statement_union_type *u;
-  lang_output_section_statement_type *lookup;
-
-  for (u = lang_output_section_statement.head;
-       u != (lang_statement_union_type *) NULL;
-       u = lookup->next)
-    {
-      lookup = &u->output_section_statement;
-      if (lookup == os)
-	return s;
-
-      if (lookup->bfd_section != NULL && lookup->bfd_section->owner != NULL)
-	s = lookup->bfd_section;
-    }
-
-  return NULL;
-}
-
 /* Place an orphan section.  We use this to put random SHF_ALLOC
    sections in the right segment.  */
 
-struct orphan_save {
-  lang_output_section_statement_type *os;
-  asection **section;
-  lang_statement_union_type **stmt;
-  lang_statement_union_type **os_tail;
-};
-
 static bfd_boolean
 gld${EMULATION_NAME}_place_orphan (lang_input_statement_type *file, asection *s)
 {
-  static struct orphan_save hold_text;
-  static struct orphan_save hold_rodata;
-  static struct orphan_save hold_data;
-  static struct orphan_save hold_bss;
-  static struct orphan_save hold_rel;
-  static struct orphan_save hold_interp;
-  static struct orphan_save hold_sdata;
-  static int count = 1;
+  static struct orphan_save hold[] =
+    {
+      { ".text",
+	SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE,
+	0, 0, 0, 0 },
+      { ".rodata",
+	SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA,
+	0, 0, 0, 0 },
+      { ".data",
+	SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA,
+	0, 0, 0, 0 },
+      { ".bss",
+	SEC_ALLOC,
+	0, 0, 0, 0 },
+      { 0,
+	SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA,
+	0, 0, 0, 0 },
+      { ".interp",
+	SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA,
+	0, 0, 0, 0 },
+      { ".sdata",
+	SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_SMALL_DATA,
+	0, 0, 0, 0 }
+    };
+  enum orphan_save_index
+    {
+      orphan_text = 0,
+      orphan_rodata,
+      orphan_data,
+      orphan_bss,
+      orphan_rel,
+      orphan_interp,
+      orphan_sdata
+    };
+  static int orphan_init_done = 0;
   struct orphan_save *place;
-  lang_statement_list_type *old;
-  lang_statement_list_type add;
-  etree_type *address;
   const char *secname;
-  const char *ps = NULL;
+  lang_output_section_statement_type *after;
   lang_output_section_statement_type *os;
-  lang_statement_union_type **os_tail;
-  etree_type *load_base;
   int isdyn = 0;
-  asection *sec;
 
   secname = bfd_get_section_name (s->owner, s);
 
@@ -1308,27 +1297,42 @@ gld${EMULATION_NAME}_place_orphan (lang_
 
       if (os != NULL
 	  && (os->bfd_section == NULL
+	      || os->bfd_section->flags == 0
 	      || ((s->flags ^ os->bfd_section->flags)
 		  & (SEC_LOAD | SEC_ALLOC)) == 0))
 	{
 	  /* We already have an output section statement with this
-	     name, and its bfd section, if any, has compatible flags.  */
+	     name, and its bfd section, if any, has compatible flags.
+	     If the section already exists but does not have any flags
+	     set, then it has been created by the linker, probably as a
+	     result of a --section-start command line switch.  */
 	  lang_add_section (&os->children, s, os, file);
 	  return TRUE;
 	}
     }
 
-  if (hold_text.os == NULL)
-    hold_text.os = lang_output_section_find (".text");
+  if (!orphan_init_done)
+    {
+      struct orphan_save *ho;
+      for (ho = hold; ho < hold + sizeof (hold) / sizeof (hold[0]); ++ho)
+	if (ho->name != NULL)
+	  {
+	    ho->os = lang_output_section_find (ho->name);
+	    if (ho->os != NULL && ho->os->flags == 0)
+	      ho->os->flags = ho->flags;
+	  }
+      orphan_init_done = 1;
+    }
 
   /* If this is a final link, then always put .gnu.warning.SYMBOL
      sections into the .text section to get them out of the way.  */
   if (link_info.executable
       && ! link_info.relocatable
       && strncmp (secname, ".gnu.warning.", sizeof ".gnu.warning." - 1) == 0
-      && hold_text.os != NULL)
+      && hold[orphan_text].os != NULL)
     {
-      lang_add_section (&hold_text.os->children, s, hold_text.os, file);
+      lang_add_section (&hold[orphan_text].os->children, s,
+			hold[orphan_text].os, file);
       return TRUE;
     }
 
@@ -1337,220 +1341,57 @@ gld${EMULATION_NAME}_place_orphan (lang_
      right after the .interp section, so that the PT_NOTE segment is
      stored right after the program headers where the OS can read it
      in the first page.  */
-#define HAVE_SECTION(hold, name) \
-(hold.os != NULL || (hold.os = lang_output_section_find (name)) != NULL)
 
   place = NULL;
   if ((s->flags & SEC_ALLOC) == 0)
     ;
   else if ((s->flags & SEC_LOAD) != 0
-	   && strncmp (secname, ".note", 5) == 0
-	   && HAVE_SECTION (hold_interp, ".interp"))
-    place = &hold_interp;
-  else if ((s->flags & SEC_HAS_CONTENTS) == 0
-	   && HAVE_SECTION (hold_bss, ".bss"))
-    place = &hold_bss;
-  else if ((s->flags & SEC_SMALL_DATA) != 0
-	   && HAVE_SECTION (hold_sdata, ".sdata"))
-    place = &hold_sdata;
-  else if ((s->flags & SEC_READONLY) == 0
-	   && HAVE_SECTION (hold_data, ".data"))
-    place = &hold_data;
+	   && strncmp (secname, ".note", 5) == 0)
+    place = &hold[orphan_interp];
+  else if ((s->flags & (SEC_LOAD | SEC_HAS_CONTENTS)) == 0)
+    place = &hold[orphan_bss];
+  else if ((s->flags & SEC_SMALL_DATA) != 0)
+    place = &hold[orphan_sdata];
+  else if ((s->flags & SEC_READONLY) == 0)
+    place = &hold[orphan_data];
   else if (strncmp (secname, ".rel", 4) == 0
-	   && (s->flags & SEC_LOAD) != 0
-	   && (hold_rel.os != NULL
-	       || (hold_rel.os = output_rel_find (s, isdyn)) != NULL))
-    place = &hold_rel;
-  else if ((s->flags & (SEC_CODE | SEC_READONLY)) == SEC_READONLY
-	   && HAVE_SECTION (hold_rodata, ".rodata"))
-    place = &hold_rodata;
-  else if ((s->flags & (SEC_CODE | SEC_READONLY)) == (SEC_CODE | SEC_READONLY)
-	   && hold_text.os != NULL)
-    place = &hold_text;
+	   && (s->flags & SEC_LOAD) != 0)
+    place = &hold[orphan_rel];
+  else if ((s->flags & SEC_CODE) == 0)
+    place = &hold[orphan_rodata];
+  else
+    place = &hold[orphan_text];
 
-#undef HAVE_SECTION
+  after = NULL;
+  if (place != NULL)
+    {
+      if (place->os == NULL)
+	{
+	  if (place->name != NULL)
+	    place->os = lang_output_section_find (place->name);
+	  else
+	    place->os = output_rel_find (s, isdyn);
+	}
+      after = place->os;
+      if (after == NULL)
+	after = lang_output_section_find_by_flags (s, &place->os);
+      if (after == NULL)
+	/* *ABS* is always the first output section statement.  */
+	after = &lang_output_section_statement.head->output_section_statement;
+    }
 
   /* Choose a unique name for the section.  This will be needed if the
      same section name appears in the input file with different
-     loadable or allocatable characteristics.  But if the section
-     already exists but does not have any flags set, then it has been
-     created by the linker, probably as a result of a --section-start
-     command line switch.  */
-  if ((sec = bfd_get_section_by_name (output_bfd, secname)) != NULL
-      && bfd_get_section_flags (output_bfd, sec) != 0)
+     loadable or allocatable characteristics.  */
+  if (bfd_get_section_by_name (output_bfd, secname) != NULL)
     {
+      static int count = 1;
       secname = bfd_get_unique_section_name (output_bfd, secname, &count);
       if (secname == NULL)
 	einfo ("%F%P: place_orphan failed: %E\n");
     }
 
-  /* Start building a list of statements for this section.
-     First save the current statement pointer.  */
-  old = stat_ptr;
-
-  /* If we have found an appropriate place for the output section
-     statements for this orphan, add them to our own private list,
-     inserting them later into the global statement list.  */
-  if (place != NULL)
-    {
-      stat_ptr = &add;
-      lang_list_init (stat_ptr);
-    }
-
-  if (config.build_constructors)
-    {
-      /* If the name of the section is representable in C, then create
-	 symbols to mark the start and the end of the section.  */
-      for (ps = secname; *ps != '\0'; ps++)
-	if (! ISALNUM (*ps) && *ps != '_')
-	  break;
-      if (*ps == '\0')
-	{
-	  char *symname;
-	  etree_type *e_align;
-
-	  symname = (char *) xmalloc (ps - secname + sizeof "__start_");
-	  sprintf (symname, "__start_%s", secname);
-	  e_align = exp_unop (ALIGN_K,
-			      exp_intop ((bfd_vma) 1 << s->alignment_power));
-	  lang_add_assignment (exp_assop ('=', symname, e_align));
-	}
-    }
-
-  address = NULL;
-  if (link_info.relocatable || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0)
-    address = exp_intop ((bfd_vma) 0);
-
-  load_base = NULL;
-  if (place != NULL && place->os->load_base != NULL)
-    {
-      etree_type *lma_from_vma;
-      lma_from_vma = exp_binop ('-', place->os->load_base,
-				exp_nameop (ADDR, place->os->name));
-      load_base = exp_binop ('+', lma_from_vma,
-			     exp_nameop (ADDR, secname));
-    }
-
-  os_tail = lang_output_section_statement.tail;
-  os = lang_enter_output_section_statement (secname, address, 0,
-					    (etree_type *) NULL,
-					    (etree_type *) NULL,
-					    load_base, 0);
-
-  lang_add_section (&os->children, s, os, file);
-
-  lang_leave_output_section_statement
-    ((bfd_vma) 0, "*default*",
-     (struct lang_output_section_phdr_list *) NULL, NULL);
-
-  if (config.build_constructors && *ps == '\0')
-    {
-      char *symname;
-
-      /* lang_leave_ouput_section_statement resets stat_ptr.  Put
-	 stat_ptr back where we want it.  */
-      if (place != NULL)
-	stat_ptr = &add;
-
-      symname = (char *) xmalloc (ps - secname + sizeof "__stop_");
-      sprintf (symname, "__stop_%s", secname);
-      lang_add_assignment (exp_assop ('=', symname,
-				      exp_nameop (NAME, ".")));
-    }
-
-  /* Restore the global list pointer.  */
-  stat_ptr = old;
-
-  if (place != NULL && os->bfd_section != NULL)
-    {
-      asection *snew, **pps;
-
-      snew = os->bfd_section;
-
-      /* Shuffle the bfd section list to make the output file look
-	 neater.  This is really only cosmetic.  */
-      if (place->section == NULL)
-	{
-	  asection *bfd_section = place->os->bfd_section;
-
-	  /* If the output statement hasn't been used to place
-	     any input sections (and thus doesn't have an output
-	     bfd_section), look for the closest prior output statement
-	     having an output section.  */
-	  if (bfd_section == NULL)
-	    bfd_section = output_prev_sec_find (place->os);
-
-	  if (bfd_section != NULL && bfd_section != snew)
-	    place->section = &bfd_section->next;
-	}
-
-      if (place->section != NULL)
-	{
-	  /* Unlink the section.  */
-	  for (pps = &output_bfd->sections; *pps != snew; pps = &(*pps)->next)
-	    ;
-	  bfd_section_list_remove (output_bfd, pps);
-
-	  /* Now tack it on to the "place->os" section list.  */
-	  bfd_section_list_insert (output_bfd, place->section, snew);
-	}
-
-      /* Save the end of this list.  Further ophans of this type will
-	 follow the one we've just added.  */
-      place->section = &snew->next;
-
-      /* The following is non-cosmetic.  We try to put the output
-	 statements in some sort of reasonable order here, because
-	 they determine the final load addresses of the orphan
-	 sections.  In addition, placing output statements in the
-	 wrong order may require extra segments.  For instance,
-	 given a typical situation of all read-only sections placed
-	 in one segment and following that a segment containing all
-	 the read-write sections, we wouldn't want to place an orphan
-	 read/write section before or amongst the read-only ones.  */
-      if (add.head != NULL)
-	{
-	  lang_statement_union_type *newly_added_os;
-
-	  if (place->stmt == NULL)
-	    {
-	      /* Put the new statement list right at the head.  */
-	      *add.tail = place->os->header.next;
-	      place->os->header.next = add.head;
-
-	      place->os_tail = &place->os->next;
-	    }
-	  else
-	    {
-	      /* Put it after the last orphan statement we added.  */
-	      *add.tail = *place->stmt;
-	      *place->stmt = add.head;
-	    }
-
-	  /* Fix the global list pointer if we happened to tack our
-	     new list at the tail.  */
-	  if (*old->tail == add.head)
-	    old->tail = add.tail;
-
-	  /* Save the end of this list.  */
-	  place->stmt = add.tail;
-
-	  /* Do the same for the list of output section statements.  */
-	  newly_added_os = *os_tail;
-	  *os_tail = NULL;
-	  newly_added_os->output_section_statement.next = *place->os_tail;
-	  *place->os_tail = newly_added_os;
-	  place->os_tail = &newly_added_os->output_section_statement.next;
-
-	  /* Fixing the global list pointer here is a little different.
-	     We added to the list in lang_enter_output_section_statement,
-	     trimmed off the new output_section_statment above when
-	     assigning *os_tail = NULL, but possibly added it back in
-	     the same place when assigning *place->os_tail.  */
-	  if (*os_tail == NULL)
-	    lang_output_section_statement.tail = os_tail;
-	}
-    }
+  lang_insert_orphan (file, s, secname, after, place, NULL, NULL);
 
   return TRUE;
 }
Index: ld/emultempl/mmo.em
===================================================================
RCS file: /cvs/src/src/ld/emultempl/mmo.em,v
retrieving revision 1.13
diff -u -p -r1.13 mmo.em
--- ld/emultempl/mmo.em	19 May 2004 14:01:14 -0000	1.13
+++ ld/emultempl/mmo.em	14 Oct 2004 10:34:51 -0000
@@ -32,47 +32,6 @@ EOF
 
 cat >>e${EMULATION_NAME}.c <<EOF
 
-/* Find the last output section before given output statement.
-   Used by place_orphan.  */
-
-static asection *
-output_prev_sec_find (lang_output_section_statement_type *os)
-{
-  asection *s = NULL;
-  lang_statement_union_type *u;
-  lang_output_section_statement_type *lookup;
-
-  for (u = lang_output_section_statement.head;
-       u != (lang_statement_union_type *) NULL;
-       u = lookup->next)
-    {
-      lookup = &u->output_section_statement;
-      if (lookup->constraint == -1)
-	continue;
-      if (lookup == os)
-	break;
-      if (lookup->bfd_section != NULL
-	  && lookup->bfd_section != bfd_abs_section_ptr
-	  && lookup->bfd_section != bfd_com_section_ptr
-	  && lookup->bfd_section != bfd_und_section_ptr)
-	s = lookup->bfd_section;
-    }
-
-  if (u == NULL)
-    return NULL;
-
-  return s;
-}
-
-struct orphan_save {
-  lang_output_section_statement_type *os;
-  asection **section;
-  lang_statement_union_type **stmt;
-};
-
-#define HAVE_SECTION(hold, name) \
-(hold.os != NULL || (hold.os = lang_output_section_find (name)) != NULL)
-
 /* Place an orphan section.  We use this to put random SEC_CODE or
    SEC_READONLY sections right after MMO_TEXT_SECTION_NAME.  Much borrowed
    from elf32.em.  */
@@ -80,21 +39,25 @@ struct orphan_save {
 static bfd_boolean
 mmo_place_orphan (lang_input_statement_type *file, asection *s)
 {
-  static struct orphan_save hold_text;
+  static struct orphan_save hold_text =
+    {
+      MMO_TEXT_SECTION_NAME,
+      SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE,
+      0, 0, 0, 0
+    };
   struct orphan_save *place;
+  const char *secname;
+  lang_output_section_statement_type *after;
   lang_output_section_statement_type *os;
-  lang_statement_list_type *old;
-  lang_statement_list_type add;
-  asection *snew, **pps, *bfd_section;
 
   /* We have nothing to say for anything other than a final link.  */
   if (link_info.relocatable
-      || (bfd_get_section_flags (s->owner, s)
-	  & (SEC_EXCLUDE | SEC_LOAD)) != SEC_LOAD)
+      || (s->flags & (SEC_EXCLUDE | SEC_LOAD)) != SEC_LOAD)
     return FALSE;
 
   /* Only care for sections we're going to load.  */
-  os = lang_output_section_find (bfd_get_section_name (s->owner, s));
+  secname = s->name;
+  os = lang_output_section_find (secname);
 
   /* We have an output section by this name.  Place the section inside it
      (regardless of whether the linker script lists it as input).  */
@@ -106,108 +69,28 @@ mmo_place_orphan (lang_input_statement_t
 
   /* If this section does not have .text-type section flags or there's no
      MMO_TEXT_SECTION_NAME, we don't have anything to say.  */
-  if ((bfd_get_section_flags (s->owner, s) & (SEC_CODE | SEC_READONLY)) == 0)
+  if ((s->flags & (SEC_CODE | SEC_READONLY)) == 0)
     return FALSE;
 
   if (hold_text.os == NULL)
-    hold_text.os = lang_output_section_find (MMO_TEXT_SECTION_NAME);
+    hold_text.os = lang_output_section_find (hold_text.name);
 
   place = &hold_text;
+  if (hold_text.os != NULL)
+    after = hold_text.os;
+  else
+    after = &lang_output_section_statement.head->output_section_statement;
 
   /* If there's an output section by this name, we'll use it, regardless
      of section flags, in contrast to what's done in elf32.em.  */
-
-  /* Start building a list of statements for this section.
-     First save the current statement pointer.  */
-  old = stat_ptr;
-
-  /* Add the output section statements for this orphan to our own private
-     list, inserting them later into the global statement list.  */
-  stat_ptr = &add;
-  lang_list_init (stat_ptr);
-
-  os = lang_enter_output_section_statement (bfd_get_section_name (s->owner,
-								  s),
-					    NULL, 0,
-					    (etree_type *) NULL,
-					    (etree_type *) NULL,
-					    (etree_type *) NULL, 0);
-
-  lang_add_section (&os->children, s, os, file);
-
-  lang_leave_output_section_statement
-    ((bfd_vma) 0, "*default*",
-     (struct lang_output_section_phdr_list *) NULL, NULL);
-
-  /* Restore the global list pointer.  */
-  stat_ptr = old;
-
-  snew = os->bfd_section;
-  if (snew == NULL)
-    /* /DISCARD/ section.  */
-    return TRUE;
+  os = lang_insert_orphan (file, s, secname, after, place, NULL, NULL);
 
   /* We need an output section for .text as a root, so if there was none
      (might happen with a peculiar linker script such as in "map
      addresses", map-address.exp), we grab the output section created
      above.  */
   if (hold_text.os == NULL)
-    {
-      if (os == NULL)
-	return FALSE;
-      hold_text.os = os;
-    }
-
-  bfd_section = place->os->bfd_section;
-  if (place->section == NULL && bfd_section == NULL)
-    bfd_section = output_prev_sec_find (place->os);
-
-  if (place->section != NULL
-      || (bfd_section != NULL
-	  && bfd_section != snew))
-    {
-      /* Shuffle the section to make the output file look neater.  This is
-	 really only cosmetic.  */
-      if (place->section == NULL)
-	/* Put orphans after the first section on the list.  */
-	place->section = &bfd_section->next;
-
-      /* Unlink the section.  */
-      for (pps = &output_bfd->sections; *pps != snew; pps = &(*pps)->next)
-	;
-      bfd_section_list_remove (output_bfd, pps);
-
-      /* Now tack it on to the "place->os" section list.  */
-      bfd_section_list_insert (output_bfd, place->section, snew);
-    }
-  place->section = &snew->next;	/* Save the end of this list.  */
-
-  if (add.head != NULL)
-    {
-      /* We try to put the output statements in some sort of reasonable
-	 order here, because they determine the final load addresses of
-	 the orphan sections.  */
-      if (place->stmt == NULL)
-	{
-	  /* Put the new statement list right at the head.  */
-	  *add.tail = place->os->header.next;
-	  place->os->header.next = add.head;
-	}
-      else
-	{
-	  /* Put it after the last orphan statement we added.  */
-	  *add.tail = *place->stmt;
-	  *place->stmt = add.head;
-	}
-
-      /* Fix the global list pointer if we happened to tack our new list
-	 at the tail.  */
-      if (*old->tail == add.head)
-	old->tail = add.tail;
-
-      /* Save the end of this list.  */
-      place->stmt = add.tail;
-    }
+    hold_text.os = os;
 
   return TRUE;
 }
Index: ld/emultempl/pe.em
===================================================================
RCS file: /cvs/src/src/ld/emultempl/pe.em,v
retrieving revision 1.97
diff -u -p -r1.97 pe.em
--- ld/emultempl/pe.em	12 Oct 2004 23:44:57 -0000	1.97
+++ ld/emultempl/pe.em	14 Oct 2004 10:34:52 -0000
@@ -1485,33 +1485,6 @@ gld_${EMULATION_NAME}_finish (void)
 }
 
 
-/* Find the last output section before given output statement.
-   Used by place_orphan.  */
-
-static asection *
-output_prev_sec_find (lang_output_section_statement_type *os)
-{
-  asection *s = (asection *) NULL;
-  lang_statement_union_type *u;
-  lang_output_section_statement_type *lookup;
-
-  for (u = lang_output_section_statement.head;
-       u != (lang_statement_union_type *) NULL;
-       u = lookup->next)
-    {
-      lookup = &u->output_section_statement;
-      if (lookup->constraint == -1)
-	continue;
-      if (lookup == os)
-	return s;
-
-      if (lookup->bfd_section != NULL && lookup->bfd_section->owner != NULL)
-	s = lookup->bfd_section;
-    }
-
-  return NULL;
-}
-
 /* Place an orphan section.
 
    We use this to put sections in a reasonable place in the file, and
@@ -1525,280 +1498,132 @@ output_prev_sec_find (lang_output_sectio
    default linker script using wildcards, and are sorted by
    sort_sections.  */
 
-struct orphan_save
-{
-  lang_output_section_statement_type *os;
-  asection **section;
-  lang_statement_union_type **stmt;
-  lang_statement_union_type **os_tail;
-};
-
 static bfd_boolean
 gld_${EMULATION_NAME}_place_orphan (lang_input_statement_type *file, asection *s)
 {
   const char *secname;
-  char *hold_section_name;
+  const char *orig_secname;
   char *dollar = NULL;
-  const char *ps = NULL;
   lang_output_section_statement_type *os;
   lang_statement_list_type add_child;
 
   secname = bfd_get_section_name (s->owner, s);
 
   /* Look through the script to see where to place this section.  */
-  hold_section_name = xstrdup (secname);
-  if (!link_info.relocatable)
+  orig_secname = secname;
+  if (!link_info.relocatable
+      && (dollar = strchr (secname, '$')) != NULL)
     {
-      dollar = strchr (hold_section_name, '$');
-      if (dollar != NULL)
-	*dollar = '\0';
+      size_t len = dollar - orig_secname;
+      char *newname = xmalloc (len + 1);
+      memcpy (newname, orig_secname, len);
+      newname[len] = '\0';
+      secname = newname;
     }
 
-  os = lang_output_section_find (hold_section_name);
+  os = lang_output_section_find (secname);
 
   lang_list_init (&add_child);
 
   if (os != NULL
       && (os->bfd_section == NULL
+	  || os->bfd_section->flags == 0
 	  || ((s->flags ^ os->bfd_section->flags)
 	      & (SEC_LOAD | SEC_ALLOC)) == 0))
     {
       /* We already have an output section statement with this
-	 name, and its bfd section, if any, has compatible flags.  */
+	 name, and its bfd section, if any, has compatible flags.
+	 If the section already exists but does not have any flags set,
+	 then it has been created by the linker, probably as a result of
+	 a --section-start command line switch.  */
       lang_add_section (&add_child, s, os, file);
     }
   else
     {
+      static struct orphan_save hold[] =
+	{
+	  { ".text",
+	    SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE,
+	    0, 0, 0, 0 },
+	  { ".rdata",
+	    SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA,
+	    0, 0, 0, 0 },
+	  { ".data",
+	    SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_DATA,
+	    0, 0, 0, 0 },
+	  { ".bss",
+	    SEC_ALLOC,
+	    0, 0, 0, 0 }
+	};
+      enum orphan_save_index
+	{
+	  orphan_text = 0,
+	  orphan_rodata,
+	  orphan_data,
+	  orphan_bss
+	};
+      static int orphan_init_done = 0;
       struct orphan_save *place;
-      static struct orphan_save hold_text;
-      static struct orphan_save hold_rdata;
-      static struct orphan_save hold_data;
-      static struct orphan_save hold_bss;
-      static int count = 1;
-      char *outsecname;
-      lang_statement_list_type *old;
-      lang_statement_list_type add;
-      lang_statement_union_type **os_tail;
+      lang_output_section_statement_type *after;
       etree_type *address;
-      etree_type *load_base;
-      asection *sec;
+
+      if (!orphan_init_done)
+	{
+	  struct orphan_save *ho;
+	  for (ho = hold; ho < hold + sizeof (hold) / sizeof (hold[0]); ++ho)
+	    if (ho->name != NULL)
+	      {
+		ho->os = lang_output_section_find (ho->name);
+		if (ho->os != NULL && ho->os->flags == 0)
+		  ho->os->flags = ho->flags;
+	      }
+	  orphan_init_done = 1;
+	}
 
       /* Try to put the new output section in a reasonable place based
 	 on the section name and section flags.  */
-#define HAVE_SECTION(hold, name) \
-(hold.os != NULL || (hold.os = lang_output_section_find (name)) != NULL)
 
       place = NULL;
       if ((s->flags & SEC_ALLOC) == 0)
 	;
-      else if ((s->flags & SEC_HAS_CONTENTS) == 0
-	       && HAVE_SECTION (hold_bss, ".bss"))
-	place = &hold_bss;
-      else if ((s->flags & SEC_READONLY) == 0
-	       && HAVE_SECTION (hold_data, ".data"))
-	place = &hold_data;
-      else if ((s->flags & SEC_CODE) == 0
-	       && (s->flags & SEC_READONLY) != 0
-	       && HAVE_SECTION (hold_rdata, ".rdata"))
-	place = &hold_rdata;
-      else if ((s->flags & SEC_CODE) != 0
-	       && (s->flags & SEC_READONLY) != 0
-	       && HAVE_SECTION (hold_text, ".text"))
-	place = &hold_text;
-
-#undef HAVE_SECTION
-
-      /* Choose a unique name for the section.  This will be needed if the
-	 same section name appears in the input file with different
-	 loadable or allocatable characteristics.  But if the section
-	 already exists but does not have any flags set, then it has been
-	 created by the linker, probably as a result of a --section-start
-	 command line switch.  */
-      sec = bfd_get_section_by_name (output_bfd, hold_section_name);
-      if (sec != NULL
-	  && bfd_get_section_flags (output_bfd, sec) != 0)
-	{
-	  outsecname = bfd_get_unique_section_name (output_bfd,
-						    hold_section_name, &count);
-	  if (outsecname == NULL)
-	    einfo ("%F%P: place_orphan failed: %E\n");
-	}
+      else if ((s->flags & (SEC_LOAD | SEC_HAS_CONTENTS)) == 0)
+	place = &hold[orphan_bss];
+      else if ((s->flags & SEC_READONLY) == 0)
+	place = &hold[orphan_data];
+      else if ((s->flags & SEC_CODE) == 0)
+	place = &hold[orphan_rodata];
       else
-	outsecname = xstrdup (hold_section_name);
-
-      /* Start building a list of statements for this section.  */
-      old = stat_ptr;
+	place = &hold[orphan_text];
 
-      /* If we have found an appropriate place for the output section
-	 statements for this orphan, add them to our own private list,
-	 inserting them later into the global statement list.  */
+      after = NULL;
       if (place != NULL)
 	{
-	  stat_ptr = &add;
-	  lang_list_init (stat_ptr);
-	}
-
-      if (config.build_constructors)
-	{
-	  /* If the name of the section is representable in C, then create
-	     symbols to mark the start and the end of the section.  */
-	  for (ps = outsecname; *ps != '\0'; ps++)
-	    if (! ISALNUM ((unsigned char) *ps) && *ps != '_')
-	      break;
-	  if (*ps == '\0')
-	    {
-	      char *symname;
-	      etree_type *e_align;
-
-	      symname = (char *) xmalloc (ps - outsecname + sizeof "___start_");
-	      sprintf (symname, "___start_%s", outsecname);
-	      e_align = exp_unop (ALIGN_K,
-				  exp_intop ((bfd_vma) 1 << s->alignment_power));
-	      lang_add_assignment (exp_assop ('=', symname, e_align));
-	    }
-	}
-
-      if (link_info.relocatable || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0)
-	address = exp_intop ((bfd_vma) 0);
-      else
-	{
-	  /* All sections in an executable must be aligned to a page
-	     boundary.  */
-	  address = exp_unop (ALIGN_K,
-			      exp_nameop (NAME, "__section_alignment__"));
+	  if (place->os == NULL)
+	    place->os = lang_output_section_find (place->name);
+	  after = place->os;
+	  if (after == NULL)
+	    after = lang_output_section_find_by_flags (s, &place->os);
+	  if (after == NULL)
+	    /* *ABS* is always the first output section statement.  */
+	    after = (&lang_output_section_statement.head
+		     ->output_section_statement);
 	}
 
-      load_base = NULL;
-      if (place != NULL && place->os->load_base != NULL)
-	{
-	  etree_type *lma_from_vma;
-	  lma_from_vma = exp_binop ('-', place->os->load_base,
-				    exp_nameop (ADDR, place->os->name));
-	  load_base = exp_binop ('+', lma_from_vma,
-				 exp_nameop (ADDR, secname));
-	}
-
-      os_tail = lang_output_section_statement.tail;
-      os = lang_enter_output_section_statement (outsecname, address, 0,
-						(etree_type *) NULL,
-						(etree_type *) NULL,
-						load_base, 0);
-
-      lang_add_section (&add_child, s, os, file);
-
-      lang_leave_output_section_statement
-	((bfd_vma) 0, "*default*",
-	 (struct lang_output_section_phdr_list *) NULL, NULL);
-
-      if (config.build_constructors && *ps == '\0')
+      /* Choose a unique name for the section.  This will be needed if the
+	 same section name appears in the input file with different
+	 loadable or allocatable characteristics.  */
+      if (bfd_get_section_by_name (output_bfd, secname) != NULL)
 	{
-	  char *symname;
-
-	  /* lang_leave_ouput_section_statement resets stat_ptr.
-	     Put stat_ptr back where we want it.  */
-	  if (place != NULL)
-	    stat_ptr = &add;
-
-	  symname = (char *) xmalloc (ps - outsecname + sizeof "___stop_");
-	  sprintf (symname, "___stop_%s", outsecname);
-	  lang_add_assignment (exp_assop ('=', symname,
-					  exp_nameop (NAME, ".")));
+	  static int count = 1;
+	  secname = bfd_get_unique_section_name (output_bfd, secname, &count);
+	  if (secname == NULL)
+	    einfo ("%F%P: place_orphan failed: %E\n");
 	}
 
-      stat_ptr = old;
-
-      if (place != NULL && os->bfd_section != NULL)
-	{
-	  asection *snew, **pps;
-
-	  snew = os->bfd_section;
-
-	  /* Shuffle the bfd section list to make the output file look
-	     neater.  This is really only cosmetic.  */
-	  if (place->section == NULL)
-	    {
-	      asection *bfd_section = place->os->bfd_section;
-
-	      /* If the output statement hasn't been used to place
-		 any input sections (and thus doesn't have an output
-		 bfd_section), look for the closest prior output statement
-		 having an output section.  */
-	      if (bfd_section == NULL)
-		bfd_section = output_prev_sec_find (place->os);
-
-	      if (bfd_section != NULL && bfd_section != snew)
-		place->section = &bfd_section->next;
-	    }
-
-	  if (place->section != NULL)
-	    {
-	      /* Unlink the section.  */
-	      for (pps = &output_bfd->sections;
-		   *pps != snew;
-		   pps = &(*pps)->next)
-		;
-	      bfd_section_list_remove (output_bfd, pps);
-
-	      /* Now tack it on to the "place->os" section list.  */
-	      bfd_section_list_insert (output_bfd, place->section, snew);
-	    }
-
-	  /* Save the end of this list.  Further ophans of this type will
-	     follow the one we've just added.  */
-	  place->section = &snew->next;
-
-	  /* The following is non-cosmetic.  We try to put the output
-	     statements in some sort of reasonable order here, because
-	     they determine the final load addresses of the orphan
-	     sections.  In addition, placing output statements in the
-	     wrong order may require extra segments.  For instance,
-	     given a typical situation of all read-only sections placed
-	     in one segment and following that a segment containing all
-	     the read-write sections, we wouldn't want to place an orphan
-	     read/write section before or amongst the read-only ones.  */
-	  if (add.head != NULL)
-	    {
-	      lang_statement_union_type *newly_added_os;
-
-	      if (place->stmt == NULL)
-		{
-		  /* Put the new statement list right at the head.  */
-		  *add.tail = place->os->header.next;
-		  place->os->header.next = add.head;
-
-		  place->os_tail = &place->os->next;
-		}
-	      else
-		{
-		  /* Put it after the last orphan statement we added.  */
-		  *add.tail = *place->stmt;
-		  *place->stmt = add.head;
-		}
-
-	      /* Fix the global list pointer if we happened to tack our
-		 new list at the tail.  */
-	      if (*old->tail == add.head)
-		old->tail = add.tail;
-
-	      /* Save the end of this list.  */
-	      place->stmt = add.tail;
-
-	      /* Do the same for the list of output section statements.  */
-	      newly_added_os = *os_tail;
-	      *os_tail = NULL;
-	      newly_added_os->output_section_statement.next = *place->os_tail;
-	      *place->os_tail = newly_added_os;
-	      place->os_tail = &newly_added_os->output_section_statement.next;
-
-	      /* Fixing the global list pointer here is a little different.
-		 We added to the list in lang_enter_output_section_statement,
-		 trimmed off the new output_section_statment above when
-		 assigning *os_tail = NULL, but possibly added it back in
-		 the same place when assigning *place->os_tail.  */
-	      if (*os_tail == NULL)
-		lang_output_section_statement.tail = os_tail;
-	    }
-	}
+      /* All sections in an executable must be aligned to a page boundary.  */
+      address = exp_unop (ALIGN_K, exp_nameop (NAME, "__section_alignment__"));
+      os = lang_insert_orphan (file, s, secname, after, place, address,
+			       &add_child);
     }
 
   {
@@ -1830,7 +1655,7 @@ gld_${EMULATION_NAME}_place_orphan (lang
 	    else
 	      {
 		found_dollar = TRUE;
-		if (strcmp (secname, lname) < 0)
+		if (strcmp (orig_secname, lname) < 0)
 		  break;
 	      }
 	  }
@@ -1843,8 +1668,6 @@ gld_${EMULATION_NAME}_place_orphan (lang
       }
   }
 
-  free (hold_section_name);
-
   return TRUE;
 }
 
Index: ld/testsuite/ld-scripts/overlay-size.d
===================================================================
RCS file: /cvs/src/src/ld/testsuite/ld-scripts/overlay-size.d,v
retrieving revision 1.2
diff -u -p -r1.2 overlay-size.d
--- ld/testsuite/ld-scripts/overlay-size.d	22 Sep 2004 06:45:39 -0000	1.2
+++ ld/testsuite/ld-scripts/overlay-size.d	14 Oct 2004 10:34:54 -0000
@@ -5,25 +5,25 @@
 # The .bss[123] LMAs are deliberately blanked out.  We can't
 # reliably map overlaid sections to segments.
 #...
-  0 \.bss1 +0+010 +0+20000 .*
+ .. \.bss1 +0+010 +0+20000 .*
 #...
-  1 \.bss2 +0+030 +0+20000 .*
+ .. \.bss2 +0+030 +0+20000 .*
 #...
-  2 \.bss3 +0+020 +0+20000 .*
+ .. \.bss3 +0+020 +0+20000 .*
 #...
-  3 \.mtext +0+020 +0+10000 +0+30000 .*
+ .. \.mtext +0+020 +0+10000 +0+30000 .*
 #...
-  4 \.mbss +0+230 +0+20030 .*
+ .. \.mbss +0+230 +0+20030 .*
 #...
-  5 \.text1 +0+080 +0+10020 +0+30020 .*
+ .. \.text1 +0+080 +0+10020 +0+30020 .*
 #...
-  6 \.text2 +0+040 +0+10020 +0+300a0 .*
+ .. \.text2 +0+040 +0+10020 +0+300a0 .*
 #...
-  7 \.text3 +0+020 +0+10020 +0+300e0 .*
+ .. \.text3 +0+020 +0+10020 +0+300e0 .*
 #...
-  8 \.data1 +0+030 +0+20260 +0+30100 .*
+ .. \.data1 +0+030 +0+20260 +0+30100 .*
 #...
-  9 \.data2 +0+040 +0+20260 +0+30130 .*
+ .. \.data2 +0+040 +0+20260 +0+30130 .*
 #...
- 10 \.data3 +0+050 +0+20260 +0+30170 .*
+ .. \.data3 +0+050 +0+20260 +0+30170 .*
 #pass
Index: ld/testsuite/ld-mmix/bpo-18.d
===================================================================
RCS file: /cvs/src/src/ld/testsuite/ld-mmix/bpo-18.d,v
retrieving revision 1.4
diff -u -p -r1.4 bpo-18.d
--- ld/testsuite/ld-mmix/bpo-18.d	14 Sep 2003 14:10:55 -0000	1.4
+++ ld/testsuite/ld-mmix/bpo-18.d	14 Oct 2004 12:27:13 -0000
@@ -12,9 +12,9 @@
 SYMBOL TABLE:
 0+100 l    d  \.text	0+ 
 4000000000001060 l    d  \.text\.away	0+ 
+400000000000106c l    d  \.data	0+ 
+400000000000106c l    d  \.bss	0+ 
 0+7e0 l    d  \.MMIX\.reg_contents	0+ 
-4000000000001088 l    d  \.data	0+ 
-4000000000001088 l    d  \.bss	0+ 
 0+ l    d  \*ABS\*	0+ 
 0+ l    d  \*ABS\*	0+ 
 0+ l    d  \*ABS\*	0+ 

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre


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