daily update
[deliverable/binutils-gdb.git] / ld / ldlang.c
index a19b46a4ba4961d8e4e4cc2f93255b3c3b793a8f..b82a7e55cf6de2502c4fd7c31baecc18d3bd7ace 100644 (file)
@@ -1,6 +1,6 @@
 /* Linker command language support.
    Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-   2001, 2002, 2003, 2004
+   2001, 2002, 2003, 2004, 2005
    Free Software Foundation, Inc.
 
    This file is part of GLD, the Gnu Linker.
@@ -17,8 +17,8 @@
 
    You should have received a copy of the GNU General Public License
    along with GLD; see the file COPYING.  If not, write to the Free
-   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA.  */
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
 
 #include "bfd.h"
 #include "sysdep.h"
@@ -54,6 +54,7 @@ static struct obstack map_obstack;
 static const char *startup_file;
 static lang_statement_list_type input_file_chain;
 static bfd_boolean placed_commons = FALSE;
+static bfd_boolean stripped_excluded_sections = FALSE;
 static lang_output_section_statement_type *default_common_section;
 static bfd_boolean map_option_f;
 static bfd_vma print_dot;
@@ -67,7 +68,6 @@ static struct bfd_hash_table lang_definedness_table;
 /* Forward declarations.  */
 static void exp_init_os (etree_type *);
 static void init_map_userdata (bfd *, asection *, void *);
-static bfd_boolean wildcardp (const char *);
 static lang_input_statement_type *lookup_name (const char *);
 static bfd_boolean load_symbols (lang_input_statement_type *,
                                 lang_statement_list_type *);
@@ -85,15 +85,13 @@ static bfd_boolean lang_one_common (struct bfd_link_hash_entry *, void *);
 static void lang_record_phdrs (void);
 static void lang_do_version_exports_section (void);
 
-typedef void (*callback_t) (lang_wild_statement_type *, struct wildcard_list *,
-                           asection *, lang_input_statement_type *, void *);
-
 /* Exported variables.  */
 lang_output_section_statement_type *abs_output_section;
 lang_statement_list_type lang_output_section_statement;
 lang_statement_list_type *stat_ptr = &statement_list;
 lang_statement_list_type file_chain = { NULL, NULL };
 struct bfd_sym_chain entry_symbol = { NULL, NULL };
+static const char *entry_symbol_default = "start";
 const char *entry_section = ".text";
 bfd_boolean entry_from_cmdline;
 bfd_boolean lang_has_input_file = FALSE;
@@ -101,12 +99,22 @@ bfd_boolean had_output_filename = FALSE;
 bfd_boolean lang_float_flag = FALSE;
 bfd_boolean delete_output_file_on_failure = FALSE;
 struct lang_nocrossrefs *nocrossref_list;
-struct unique_sections *unique_section_list;
+static struct unique_sections *unique_section_list;
 static bfd_boolean ldlang_sysrooted_script = FALSE;
+
+ /* Functions that traverse the linker script and might evaluate
+    DEFINED() need to increment this.  */
 int lang_statement_iteration = 0;
 
 etree_type *base; /* Relocation base - or null */
 
+/* Return TRUE if the PATTERN argument is a wildcard pattern.
+   Although backslashes are treated specially if a pattern contains
+   wildcards, we do not consider the mere presence of a backslash to
+   be enough to cause the pattern to be treated as a wildcard.
+   That lets us handle DOS filenames more naturally.  */
+#define wildcardp(pattern) (strpbrk ((pattern), "?*[") != NULL)
+
 #define new_stat(x, y) \
   (x##_type *) new_statement (x##_enum, sizeof (x##_type), y)
 
@@ -149,21 +157,71 @@ unique_section_p (const asection *sec)
 
 /* Generic traversal routines for finding matching sections.  */
 
+/* Try processing a section against a wildcard.  This just calls
+   the callback unless the filename exclusion list is present
+   and excludes the file.  It's hardly ever present so this
+   function is very fast.  */
+
 static void
-walk_wild_section (lang_wild_statement_type *ptr,
-                  lang_input_statement_type *file,
-                  callback_t callback,
-                  void *data)
+walk_wild_consider_section (lang_wild_statement_type *ptr,
+                           lang_input_statement_type *file,
+                           asection *s,
+                           struct wildcard_list *sec,
+                           callback_t callback,
+                           void *data)
+{
+  bfd_boolean skip = FALSE;
+  struct name_list *list_tmp;
+
+  /* Don't process sections from files which were
+     excluded.  */
+  for (list_tmp = sec->spec.exclude_name_list;
+       list_tmp;
+       list_tmp = list_tmp->next)
+    {
+      bfd_boolean is_wildcard = wildcardp (list_tmp->name);
+      if (is_wildcard)
+       skip = fnmatch (list_tmp->name, file->filename, 0) == 0;
+      else
+       skip = strcmp (list_tmp->name, file->filename) == 0;
+
+      /* If this file is part of an archive, and the archive is
+        excluded, exclude this file.  */
+      if (! skip && file->the_bfd != NULL
+         && file->the_bfd->my_archive != NULL
+         && file->the_bfd->my_archive->filename != NULL)
+       {
+         if (is_wildcard)
+           skip = fnmatch (list_tmp->name,
+                           file->the_bfd->my_archive->filename,
+                           0) == 0;
+         else
+           skip = strcmp (list_tmp->name,
+                          file->the_bfd->my_archive->filename) == 0;
+       }
+
+      if (skip)
+       break;
+    }
+
+  if (!skip)
+    (*callback) (ptr, sec, s, file, data);
+}
+
+/* Lowest common denominator routine that can handle everything correctly,
+   but slowly.  */
+
+static void
+walk_wild_section_general (lang_wild_statement_type *ptr,
+                          lang_input_statement_type *file,
+                          callback_t callback,
+                          void *data)
 {
   asection *s;
-
-  if (file->just_syms_flag)
-    return;
+  struct wildcard_list *sec;
 
   for (s = file->the_bfd->sections; s != NULL; s = s->next)
     {
-      struct wildcard_list *sec;
-
       sec = ptr->section_list;
       if (sec == NULL)
        (*callback) (ptr, sec, s, file, data);
@@ -171,39 +229,8 @@ walk_wild_section (lang_wild_statement_type *ptr,
       while (sec != NULL)
        {
          bfd_boolean skip = FALSE;
-         struct name_list *list_tmp;
-
-         /* Don't process sections from files which were
-            excluded.  */
-         for (list_tmp = sec->spec.exclude_name_list;
-              list_tmp;
-              list_tmp = list_tmp->next)
-           {
-             if (wildcardp (list_tmp->name))
-               skip = fnmatch (list_tmp->name, file->filename, 0) == 0;
-             else
-               skip = strcmp (list_tmp->name, file->filename) == 0;
-
-             /* If this file is part of an archive, and the archive is
-                excluded, exclude this file.  */
-             if (! skip && file->the_bfd != NULL
-                 && file->the_bfd->my_archive != NULL
-                 && file->the_bfd->my_archive->filename != NULL)
-               {
-                 if (wildcardp (list_tmp->name))
-                   skip = fnmatch (list_tmp->name,
-                                   file->the_bfd->my_archive->filename,
-                                   0) == 0;
-                 else
-                   skip = strcmp (list_tmp->name,
-                                  file->the_bfd->my_archive->filename) == 0;
-               }
-
-             if (skip)
-               break;
-           }
 
-         if (!skip && sec->spec.name != NULL)
+         if (sec->spec.name != NULL)
            {
              const char *sname = bfd_get_section_name (file->the_bfd, s);
 
@@ -214,13 +241,381 @@ walk_wild_section (lang_wild_statement_type *ptr,
            }
 
          if (!skip)
-           (*callback) (ptr, sec, s, file, data);
+           walk_wild_consider_section (ptr, file, s, sec, callback, data);
 
          sec = sec->next;
        }
     }
 }
 
+/* Routines to find a single section given its name.  If there's more
+   than one section with that name, we report that.  */
+
+typedef struct
+{
+  asection *found_section;
+  bfd_boolean multiple_sections_found;
+} section_iterator_callback_data;
+
+static bfd_boolean
+section_iterator_callback (bfd *bfd ATTRIBUTE_UNUSED, asection *s, void *data)
+{
+  section_iterator_callback_data *d = data;
+
+  if (d->found_section != NULL)
+    {
+      d->multiple_sections_found = TRUE;
+      return TRUE;
+    }
+
+  d->found_section = s;
+  return FALSE;
+}
+
+static asection *
+find_section (lang_input_statement_type *file,
+             struct wildcard_list *sec,
+             bfd_boolean *multiple_sections_found)
+{
+  section_iterator_callback_data cb_data = { NULL, FALSE };
+
+  bfd_get_section_by_name_if (file->the_bfd, sec->spec.name, 
+                             section_iterator_callback, &cb_data);
+  *multiple_sections_found = cb_data.multiple_sections_found;
+  return cb_data.found_section;
+}
+
+/* Code for handling simple wildcards without going through fnmatch,
+   which can be expensive because of charset translations etc.  */
+
+/* A simple wild is a literal string followed by a single '*',
+   where the literal part is at least 4 characters long.  */
+
+static bfd_boolean
+is_simple_wild (const char *name)
+{
+  size_t len = strcspn (name, "*?[");
+  return len >= 4 && name[len] == '*' && name[len + 1] == '\0';
+}
+
+static bfd_boolean
+match_simple_wild (const char *pattern, const char *name)
+{
+  /* The first four characters of the pattern are guaranteed valid
+     non-wildcard characters.  So we can go faster.  */
+  if (pattern[0] != name[0] || pattern[1] != name[1]
+      || pattern[2] != name[2] || pattern[3] != name[3])
+    return FALSE;
+
+  pattern += 4;
+  name += 4;
+  while (*pattern != '*')
+    if (*name++ != *pattern++)
+      return FALSE;
+
+  return TRUE;
+}
+
+/* Specialized, optimized routines for handling different kinds of
+   wildcards */
+
+static void
+walk_wild_section_specs1_wild0 (lang_wild_statement_type *ptr,
+                               lang_input_statement_type *file,
+                               callback_t callback,
+                               void *data)
+{
+  /* We can just do a hash lookup for the section with the right name.
+     But if that lookup discovers more than one section with the name
+     (should be rare), we fall back to the general algorithm because
+     we would otherwise have to sort the sections to make sure they
+     get processed in the bfd's order.  */
+  bfd_boolean multiple_sections_found;
+  struct wildcard_list *sec0 = ptr->handler_data[0];
+  asection *s0 = find_section (file, sec0, &multiple_sections_found);
+
+  if (multiple_sections_found)
+    walk_wild_section_general (ptr, file, callback, data);
+  else if (s0)
+    walk_wild_consider_section (ptr, file, s0, sec0, callback, data);
+}
+
+static void
+walk_wild_section_specs1_wild1 (lang_wild_statement_type *ptr,
+                               lang_input_statement_type *file,
+                               callback_t callback,
+                               void *data)
+{
+  asection *s;
+  struct wildcard_list *wildsec0 = ptr->handler_data[0];
+
+  for (s = file->the_bfd->sections; s != NULL; s = s->next)
+    {
+      const char *sname = bfd_get_section_name (file->the_bfd, s);
+      bfd_boolean skip = !match_simple_wild (wildsec0->spec.name, sname);
+
+      if (!skip)
+       walk_wild_consider_section (ptr, file, s, wildsec0, callback, data);
+    }
+}
+
+static void
+walk_wild_section_specs2_wild1 (lang_wild_statement_type *ptr,
+                               lang_input_statement_type *file,
+                               callback_t callback,
+                               void *data)
+{
+  asection *s;
+  struct wildcard_list *sec0 = ptr->handler_data[0];
+  struct wildcard_list *wildsec1 = ptr->handler_data[1];
+  bfd_boolean multiple_sections_found;
+  asection *s0 = find_section (file, sec0, &multiple_sections_found);
+
+  if (multiple_sections_found)
+    {
+      walk_wild_section_general (ptr, file, callback, data);
+      return;
+    }
+
+  /* Note that if the section was not found, s0 is NULL and
+     we'll simply never succeed the s == s0 test below.  */
+  for (s = file->the_bfd->sections; s != NULL; s = s->next)
+    {
+      /* Recall that in this code path, a section cannot satisfy more
+        than one spec, so if s == s0 then it cannot match
+        wildspec1.  */
+      if (s == s0)
+       walk_wild_consider_section (ptr, file, s, sec0, callback, data);
+      else
+       {
+         const char *sname = bfd_get_section_name (file->the_bfd, s);
+         bfd_boolean skip = !match_simple_wild (wildsec1->spec.name, sname);
+
+         if (!skip)
+           walk_wild_consider_section (ptr, file, s, wildsec1, callback,
+                                       data);
+       }
+    }
+}
+
+static void
+walk_wild_section_specs3_wild2 (lang_wild_statement_type *ptr,
+                               lang_input_statement_type *file,
+                               callback_t callback,
+                               void *data)
+{
+  asection *s;
+  struct wildcard_list *sec0 = ptr->handler_data[0];
+  struct wildcard_list *wildsec1 = ptr->handler_data[1];
+  struct wildcard_list *wildsec2 = ptr->handler_data[2];
+  bfd_boolean multiple_sections_found;
+  asection *s0 = find_section (file, sec0, &multiple_sections_found);
+
+  if (multiple_sections_found)
+    {
+      walk_wild_section_general (ptr, file, callback, data);
+      return;
+    }
+
+  for (s = file->the_bfd->sections; s != NULL; s = s->next)
+    {
+      if (s == s0)
+       walk_wild_consider_section (ptr, file, s, sec0, callback, data);
+      else
+       {
+         const char *sname = bfd_get_section_name (file->the_bfd, s);
+         bfd_boolean skip = !match_simple_wild (wildsec1->spec.name, sname);
+
+         if (!skip)
+           walk_wild_consider_section (ptr, file, s, wildsec1, callback, data);
+         else
+           {
+             skip = !match_simple_wild (wildsec2->spec.name, sname);
+             if (!skip)
+               walk_wild_consider_section (ptr, file, s, wildsec2, callback,
+                                           data);
+           }
+       }
+    }
+}
+
+static void
+walk_wild_section_specs4_wild2 (lang_wild_statement_type *ptr,
+                               lang_input_statement_type *file,
+                               callback_t callback,
+                               void *data)
+{
+  asection *s;
+  struct wildcard_list *sec0 = ptr->handler_data[0];
+  struct wildcard_list *sec1 = ptr->handler_data[1];
+  struct wildcard_list *wildsec2 = ptr->handler_data[2];
+  struct wildcard_list *wildsec3 = ptr->handler_data[3];
+  bfd_boolean multiple_sections_found;
+  asection *s0 = find_section (file, sec0, &multiple_sections_found), *s1;
+
+  if (multiple_sections_found)
+    {
+      walk_wild_section_general (ptr, file, callback, data);
+      return;
+    }
+
+  s1 = find_section (file, sec1, &multiple_sections_found);
+  if (multiple_sections_found)
+    {
+      walk_wild_section_general (ptr, file, callback, data);
+      return;
+    }
+
+  for (s = file->the_bfd->sections; s != NULL; s = s->next)
+    {
+      if (s == s0)
+       walk_wild_consider_section (ptr, file, s, sec0, callback, data);
+      else
+       if (s == s1)
+         walk_wild_consider_section (ptr, file, s, sec1, callback, data);
+       else
+         {
+           const char *sname = bfd_get_section_name (file->the_bfd, s);
+           bfd_boolean skip = !match_simple_wild (wildsec2->spec.name,
+                                                  sname);
+
+           if (!skip)
+             walk_wild_consider_section (ptr, file, s, wildsec2, callback,
+                                         data);
+           else
+             {
+               skip = !match_simple_wild (wildsec3->spec.name, sname);
+               if (!skip)
+                 walk_wild_consider_section (ptr, file, s, wildsec3,
+                                             callback, data);
+             }
+         }
+    }
+}
+
+static void
+walk_wild_section (lang_wild_statement_type *ptr,
+                  lang_input_statement_type *file,
+                  callback_t callback,
+                  void *data)
+{
+  if (file->just_syms_flag)
+    return;
+
+  (*ptr->walk_wild_section_handler) (ptr, file, callback, data);
+}
+
+/* Returns TRUE when name1 is a wildcard spec that might match
+   something name2 can match.  We're conservative: we return FALSE
+   only if the prefixes of name1 and name2 are different up to the
+   first wildcard character.  */
+
+static bfd_boolean
+wild_spec_can_overlap (const char *name1, const char *name2)
+{
+  size_t prefix1_len = strcspn (name1, "?*[");
+  size_t prefix2_len = strcspn (name2, "?*[");
+  size_t min_prefix_len;
+
+  /* Note that if there is no wildcard character, then we treat the
+     terminating 0 as part of the prefix.  Thus ".text" won't match
+     ".text." or ".text.*", for example.  */
+  if (name1[prefix1_len] == '\0')
+    prefix1_len++;
+  if (name2[prefix2_len] == '\0')
+    prefix2_len++;
+
+  min_prefix_len = prefix1_len < prefix2_len ? prefix1_len : prefix2_len;
+
+  return memcmp (name1, name2, min_prefix_len) == 0;
+}
+
+/* Select specialized code to handle various kinds of wildcard
+   statements.  */
+
+static void
+analyze_walk_wild_section_handler (lang_wild_statement_type *ptr)
+{
+  int sec_count = 0;
+  int wild_name_count = 0;
+  struct wildcard_list *sec;
+  int signature;
+  int data_counter;
+
+  ptr->walk_wild_section_handler = walk_wild_section_general;
+
+  /* Count how many wildcard_specs there are, and how many of those
+     actually use wildcards in the name.  Also, bail out if any of the
+     wildcard names are NULL. (Can this actually happen?
+     walk_wild_section used to test for it.)  And bail out if any
+     of the wildcards are more complex than a simple string
+     ending in a single '*'.  */
+  for (sec = ptr->section_list; sec != NULL; sec = sec->next)
+    {
+      ++sec_count;
+      if (sec->spec.name == NULL)
+       return;
+      if (wildcardp (sec->spec.name))
+       {
+         ++wild_name_count;
+         if (!is_simple_wild (sec->spec.name))
+           return;
+       }
+    }
+
+  /* The zero-spec case would be easy to optimize but it doesn't
+     happen in practice.  Likewise, more than 4 specs doesn't
+     happen in practice.  */
+  if (sec_count == 0 || sec_count > 4)
+    return;
+
+  /* Check that no two specs can match the same section.  */
+  for (sec = ptr->section_list; sec != NULL; sec = sec->next)
+    {
+      struct wildcard_list *sec2;
+      for (sec2 = sec->next; sec2 != NULL; sec2 = sec2->next)
+       {
+         if (wild_spec_can_overlap (sec->spec.name, sec2->spec.name))
+           return;
+       }
+    }
+
+  signature = (sec_count << 8) + wild_name_count;
+  switch (signature)
+    {
+    case 0x0100:
+      ptr->walk_wild_section_handler = walk_wild_section_specs1_wild0;
+      break;
+    case 0x0101:
+      ptr->walk_wild_section_handler = walk_wild_section_specs1_wild1;
+      break;
+    case 0x0201:
+      ptr->walk_wild_section_handler = walk_wild_section_specs2_wild1;
+      break;
+    case 0x0302:
+      ptr->walk_wild_section_handler = walk_wild_section_specs3_wild2;
+      break;
+    case 0x0402:
+      ptr->walk_wild_section_handler = walk_wild_section_specs4_wild2;
+      break;
+    default:
+      return;
+    }
+
+  /* Now fill the data array with pointers to the specs, first the
+     specs with non-wildcard names, then the specs with wildcard
+     names.  It's OK to process the specs in different order from the
+     given order, because we've already determined that no section
+     will match more than one spec.  */
+  data_counter = 0;
+  for (sec = ptr->section_list; sec != NULL; sec = sec->next)
+    if (!wildcardp (sec->spec.name))
+      ptr->handler_data[data_counter++] = sec;
+  for (sec = ptr->section_list; sec != NULL; sec = sec->next)
+    if (wildcardp (sec->spec.name))
+      ptr->handler_data[data_counter++] = sec;
+}
+
 /* Handle a wild statement for a single file F.  */
 
 static void
@@ -306,13 +701,11 @@ lang_for_each_statement_worker (void (*func) (lang_statement_union_type *),
          break;
        case lang_output_section_statement_enum:
          lang_for_each_statement_worker
-           (func,
-            s->output_section_statement.children.head);
+           (func, s->output_section_statement.children.head);
          break;
        case lang_wild_statement_enum:
-         lang_for_each_statement_worker
-           (func,
-            s->wild_statement.children.head);
+         lang_for_each_statement_worker (func,
+                                         s->wild_statement.children.head);
          break;
        case lang_group_statement_enum:
          lang_for_each_statement_worker (func,
@@ -456,6 +849,7 @@ new_afile (const char *name,
   p->next = NULL;
   p->symbol_count = 0;
   p->dynamic = config.dynamic_link;
+  p->add_needed = add_needed;
   p->as_needed = as_needed;
   p->whole_archive = whole_archive;
   p->loaded = FALSE;
@@ -474,6 +868,45 @@ lang_add_input_file (const char *name,
   return new_afile (name, file_type, target, TRUE);
 }
 
+struct output_statement_hash_entry
+{
+  struct bfd_hash_entry root;
+  lang_output_section_statement_type *entry;
+};
+
+/* The hash table.  */
+
+static struct bfd_hash_table output_statement_table;
+
+/* Support routines for the hash table used by lang_output_section_find_1,
+   initialize the table, fill in an entry and remove the table.  */
+
+static struct bfd_hash_entry *
+output_statement_newfunc (struct bfd_hash_entry *entry ATTRIBUTE_UNUSED, 
+                         struct bfd_hash_table *table,
+                         const char *string ATTRIBUTE_UNUSED)
+{
+  struct output_statement_hash_entry *ret
+    = bfd_hash_allocate (table,
+                        sizeof (struct output_statement_hash_entry));
+  ret->entry = NULL;
+  return (struct bfd_hash_entry *) ret;
+}
+
+static void
+output_statement_table_init (void)
+{
+  if (! bfd_hash_table_init_n (&output_statement_table,
+                              output_statement_newfunc, 61))
+    einfo (_("%P%F: Failed to create hash table\n"));
+}
+
+static void
+output_statement_table_free (void)
+{
+  bfd_hash_table_free (&output_statement_table);
+}
+
 /* Build enough state so that the parser can build its tree.  */
 
 void
@@ -483,6 +916,8 @@ lang_init (void)
 
   stat_ptr = &statement_list;
 
+  output_statement_table_init ();
+
   lang_list_init (stat_ptr);
 
   lang_list_init (&input_file_chain);
@@ -501,12 +936,15 @@ lang_init (void)
      simpler to re-use working machinery than using a linked list in terms
      of code-complexity here in ld, besides the initialization which just
      looks like other code here.  */
-  if (bfd_hash_table_init_n (&lang_definedness_table,
-                            lang_definedness_newfunc, 3) != TRUE)
+  if (!bfd_hash_table_init_n (&lang_definedness_table,
+                             lang_definedness_newfunc, 3))
     einfo (_("%P%F: out of memory during initialization"));
+}
 
-  /* Callers of exp_fold_tree need to increment this.  */
-  lang_statement_iteration = 0;
+void
+lang_finish (void)
+{
+  output_statement_table_free ();
 }
 
 /*----------------------------------------------------------------------
@@ -526,7 +964,8 @@ lang_init (void)
   and so we issue a warning.  */
 
 static lang_memory_region_type *lang_memory_region_list;
-static lang_memory_region_type **lang_memory_region_list_tail = &lang_memory_region_list;
+static lang_memory_region_type **lang_memory_region_list_tail
+  = &lang_memory_region_list;
 
 lang_memory_region_type *
 lang_memory_region_lookup (const char *const name, bfd_boolean create)
@@ -542,24 +981,11 @@ lang_memory_region_lookup (const char *const name, bfd_boolean create)
     if (strcmp (p->name, name) == 0)
       {
        if (create)
-         einfo (_("%P:%S: warning: redeclaration of memory region '%s'\n"), name);
+         einfo (_("%P:%S: warning: redeclaration of memory region '%s'\n"),
+                name);
        return p;
       }
 
-#if 0
-  /* This code used to always use the first region in the list as the
-     default region.  I changed it to instead use a region
-     encompassing all of memory as the default region.  This permits
-     NOLOAD sections to work reasonably without requiring a region.
-     People should specify what region they mean, if they really want
-     a region.  */
-  if (strcmp (name, DEFAULT_MEMORY_REGION) == 0)
-    {
-      if (lang_memory_region_list != NULL)
-       return lang_memory_region_list;
-    }
-#endif
-
   if (!create && strcmp (name, DEFAULT_MEMORY_REGION))
     einfo (_("%P:%S: warning: memory region %s not declared\n"), name);
 
@@ -605,17 +1031,31 @@ 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;
+  struct output_statement_hash_entry *entry;
+  unsigned long hash;
+
+  entry = ((struct output_statement_hash_entry *)
+          bfd_hash_lookup (&output_statement_table, name, FALSE,
+                           FALSE));
+  if (entry == NULL || (lookup = entry->entry) == NULL)
+    return NULL;
 
-  for (u = lang_output_section_statement.head; u != NULL; u = lookup->next)
+  hash = entry->root.hash;
+  do
     {
-      lookup = &u->output_section_statement;
-      if (strcmp (name, lookup->name) == 0
-         && lookup->constraint != -1
-         && (constraint == 0 || constraint == lookup->constraint))
+      if (lookup->constraint != -1
+         && (constraint == 0
+             || (constraint == lookup->constraint
+                 && constraint != SPECIAL)))
        return lookup;
+      entry = (struct output_statement_hash_entry *) entry->root.next;
+      lookup = entry ? entry->entry : NULL;
     }
+  while (entry != NULL
+        && entry->root.hash == hash
+        && strcmp (name, lookup->name) == 0);
+
   return NULL;
 }
 
@@ -629,44 +1069,482 @@ static lang_output_section_statement_type *
 lang_output_section_statement_lookup_1 (const char *const name, int constraint)
 {
   lang_output_section_statement_type *lookup;
+  lang_output_section_statement_type **nextp;
+
+  lookup = lang_output_section_find_1 (name, constraint);
+  if (lookup == NULL)
+    {
+      struct output_statement_hash_entry *entry;
+
+      lookup = new_stat (lang_output_section_statement, stat_ptr);
+      lookup->region = NULL;
+      lookup->lma_region = NULL;
+      lookup->fill = 0;
+      lookup->block_value = 1;
+      lookup->name = name;
+
+      lookup->next = NULL;
+      lookup->bfd_section = NULL;
+      lookup->processed = FALSE;
+      lookup->constraint = constraint;
+      lookup->ignored = FALSE;
+      lookup->sectype = normal_section;
+      lookup->addr_tree = NULL;
+      lang_list_init (&lookup->children);
+
+      lookup->memspec = NULL;
+      lookup->flags = 0;
+      lookup->subsection_alignment = -1;
+      lookup->section_alignment = -1;
+      lookup->load_base = NULL;
+      lookup->update_dot_tree = NULL;
+      lookup->phdrs = NULL;
+
+      entry = ((struct output_statement_hash_entry *)
+              bfd_hash_lookup (&output_statement_table, name, TRUE,
+                               FALSE));
+      if (entry == NULL)
+       einfo (_("%P%F: bfd_hash_lookup failed creating section `%s'\n"),
+              name);
+
+      entry->entry = lookup;
+
+      /* GCC's strict aliasing rules prevent us from just casting the
+        address, so we store the pointer in a variable and cast that
+        instead.  */
+      nextp = &lookup->next;
+      lang_statement_append (&lang_output_section_statement,
+                            (lang_statement_union_type *) lookup,
+                            (lang_statement_union_type **) nextp);
+    }
+  return lookup;
+}
+
+lang_output_section_statement_type *
+lang_output_section_statement_lookup (const char *const name)
+{
+  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;
+         if (!bfd_match_sections_by_type (output_bfd,
+                                          look->bfd_section,
+                                          sec->owner, sec))
+           continue;
+       }
+      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;
+             if (!bfd_match_sections_by_type (output_bfd,
+                                              look->bfd_section,
+                                              sec->owner, sec))
+               continue;
+           }
+         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;
+             if (!bfd_match_sections_by_type (output_bfd,
+                                              look->bfd_section,
+                                              sec->owner, sec))
+               continue;
+           }
+         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;
+             if (!bfd_match_sections_by_type (output_bfd,
+                                              look->bfd_section,
+                                              sec->owner, sec))
+               continue;
+           }
+         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;
+             if (!bfd_match_sections_by_type (output_bfd,
+                                              look->bfd_section,
+                                              sec->owner, sec))
+               continue;
+           }
+         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;
+         if (!bfd_match_sections_by_type (output_bfd,
+                                          look->bfd_section,
+                                          sec->owner, sec))
+           continue;
+       }
+      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, *as;
+
+      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;
+
+      as = *place->section;
+      if (as != snew && as->prev != snew)
+       {
+         /* Unlink the section.  */
+         bfd_section_list_remove (output_bfd, snew);
+
+         /* Now tack it back on in the right place.  */
+         bfd_section_list_insert_before (output_bfd, as, 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;
 
-  lookup = lang_output_section_find_1 (name, constraint);
-  if (lookup == NULL)
-    {
-      lookup = new_stat (lang_output_section_statement, stat_ptr);
-      lookup->region = NULL;
-      lookup->lma_region = NULL;
-      lookup->fill = 0;
-      lookup->block_value = 1;
-      lookup->name = name;
+         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;
+               }
 
-      lookup->next = NULL;
-      lookup->bfd_section = NULL;
-      lookup->processed = 0;
-      lookup->constraint = constraint;
-      lookup->sectype = normal_section;
-      lookup->addr_tree = NULL;
-      lang_list_init (&lookup->children);
+             *add.tail = *where;
+             *where = add.head;
 
-      lookup->memspec = NULL;
-      lookup->flags = 0;
-      lookup->subsection_alignment = -1;
-      lookup->section_alignment = -1;
-      lookup->load_base = NULL;
-      lookup->update_dot_tree = NULL;
-      lookup->phdrs = NULL;
+             place->os_tail = &after->next;
+           }
+         else
+           {
+             /* Put it after the last orphan statement we added.  */
+             *add.tail = *place->stmt;
+             *place->stmt = add.head;
+           }
 
-      lang_statement_append (&lang_output_section_statement,
-                            (lang_statement_union_type *) lookup,
-                            &lookup->next);
+         /* 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 lookup;
-}
-
-lang_output_section_statement_type *
-lang_output_section_statement_lookup (const char *const name)
-{
-  return lang_output_section_statement_lookup_1 (name, 0);
+  return os;
 }
 
 static void
@@ -784,9 +1662,10 @@ sort_def_symbol (hash_entry, info)
        }
       else if  (!ud->map_symbol_def_tail)
        ud->map_symbol_def_tail = &ud->map_symbol_def_head;
-      def = (struct map_symbol_def *) obstack_alloc (&map_obstack, sizeof *def);
+
+      def = obstack_alloc (&map_obstack, sizeof *def);
       def->entry = hash_entry;
-      *ud->map_symbol_def_tail = def;
+      *(ud->map_symbol_def_tail) = def;
       ud->map_symbol_def_tail = &def->next;
     }
   return TRUE;
@@ -797,16 +1676,12 @@ sort_def_symbol (hash_entry, info)
 static void
 init_os (lang_output_section_statement_type *s)
 {
-  lean_section_userdata_type *new;
-
   if (s->bfd_section != NULL)
     return;
 
   if (strcmp (s->name, DISCARD_SECTION_NAME) == 0)
     einfo (_("%P%F: Illegal use of `%s' section\n"), DISCARD_SECTION_NAME);
 
-  new = stat_alloc (SECTION_USERDATA_SIZE);
-
   s->bfd_section = bfd_get_section_by_name (output_bfd, s->name);
   if (s->bfd_section == NULL)
     s->bfd_section = bfd_make_section (output_bfd, s->name);
@@ -820,7 +1695,14 @@ init_os (lang_output_section_statement_type *s)
   /* We initialize an output sections output offset to minus its own
      vma to allow us to output a section through itself.  */
   s->bfd_section->output_offset = 0;
-  get_userdata (s->bfd_section) = new;
+  if (!command_line.reduce_memory_overheads)
+    {
+      fat_section_userdata_type *new
+       = stat_alloc (sizeof (fat_section_userdata_type));
+      memset (new, 0, sizeof (fat_section_userdata_type));
+      get_userdata (s->bfd_section) = new;
+    }
+
 
   /* If there is a base address, make sure that any sections it might
      mention are initialized.  */
@@ -840,6 +1722,7 @@ exp_init_os (etree_type *exp)
   switch (exp->type.node_class)
     {
     case etree_assign:
+    case etree_provide:
       exp_init_os (exp->assign.src);
       break;
 
@@ -857,7 +1740,7 @@ exp_init_os (etree_type *exp)
     case etree_assert:
       exp_init_os (exp->assert_s.child);
       break;
-      
+
     case etree_unary:
       exp_init_os (exp->unary.child);
       break;
@@ -883,175 +1766,21 @@ exp_init_os (etree_type *exp)
     }
 }
 \f
-/* Sections marked with the SEC_LINK_ONCE flag should only be linked
-   once into the output.  This routine checks each section, and
-   arrange to discard it if a section of the same name has already
-   been linked.  If the section has COMDAT information, then it uses
-   that to decide whether the section should be included.  This code
-   assumes that all relevant sections have the SEC_LINK_ONCE flag set;
-   that is, it does not depend solely upon the section name.
-   section_already_linked is called via bfd_map_over_sections.  */
-
-/* This is the shape of the elements inside the already_linked hash
-   table. It maps a name onto a list of already_linked elements with
-   the same name.  It's possible to get more than one element in a
-   list if the COMDAT sections have different names.  */
-
-struct already_linked_hash_entry
-{
-  struct bfd_hash_entry root;
-  struct already_linked *entry;
-};
-
-struct already_linked
-{
-  struct already_linked *next;
-  asection *sec;
-};
-
-/* The hash table.  */
-
-static struct bfd_hash_table already_linked_table;
-
 static void
 section_already_linked (bfd *abfd, asection *sec, void *data)
 {
   lang_input_statement_type *entry = data;
-  flagword flags;
-  const char *name;
-  struct already_linked *l;
-  struct already_linked_hash_entry *already_linked_list;
 
   /* If we are only reading symbols from this object, then we want to
      discard all sections.  */
   if (entry->just_syms_flag)
     {
-      bfd_link_just_syms (sec, &link_info);
+      bfd_link_just_syms (abfd, sec, &link_info);
       return;
     }
 
-  flags = bfd_get_section_flags (abfd, sec);
-
-  if ((flags & SEC_LINK_ONCE) == 0)
-    return;
-
-  /* FIXME: When doing a relocatable link, we may have trouble
-     copying relocations in other sections that refer to local symbols
-     in the section being discarded.  Those relocations will have to
-     be converted somehow; as of this writing I'm not sure that any of
-     the backends handle that correctly.
-
-     It is tempting to instead not discard link once sections when
-     doing a relocatable link (technically, they should be discarded
-     whenever we are building constructors).  However, that fails,
-     because the linker winds up combining all the link once sections
-     into a single large link once section, which defeats the purpose
-     of having link once sections in the first place.
-
-     Also, not merging link once sections in a relocatable link
-     causes trouble for MIPS ELF, which relies on link once semantics
-     to handle the .reginfo section correctly.  */
-
-  name = bfd_get_section_name (abfd, sec);
-
-  already_linked_list =
-    ((struct already_linked_hash_entry *)
-     bfd_hash_lookup (&already_linked_table, name, TRUE, FALSE));
-
-  for (l = already_linked_list->entry; l != NULL; l = l->next)
-    {
-      if (sec->comdat == NULL
-         || l->sec->comdat == NULL
-         || strcmp (sec->comdat->name, l->sec->comdat->name) == 0)
-       {
-         /* The section has already been linked.  See if we should
-             issue a warning.  */
-         switch (flags & SEC_LINK_DUPLICATES)
-           {
-           default:
-             abort ();
-
-           case SEC_LINK_DUPLICATES_DISCARD:
-             break;
-
-           case SEC_LINK_DUPLICATES_ONE_ONLY:
-             if (sec->comdat == NULL)
-               einfo (_("%P: %B: warning: ignoring duplicate section `%s'\n"),
-                      abfd, name);
-             else
-               einfo (_("%P: %B: warning: ignoring duplicate `%s' section symbol `%s'\n"),
-                      abfd, name, sec->comdat->name);
-             break;
-
-           case SEC_LINK_DUPLICATES_SAME_CONTENTS:
-             /* FIXME: We should really dig out the contents of both
-                 sections and memcmp them.  The COFF/PE spec says that
-                 the Microsoft linker does not implement this
-                 correctly, so I'm not going to bother doing it
-                 either.  */
-             /* Fall through.  */
-           case SEC_LINK_DUPLICATES_SAME_SIZE:
-             if (bfd_section_size (abfd, sec)
-                 != bfd_section_size (l->sec->owner, l->sec))
-               einfo (_("%P: %B: warning: duplicate section `%s' has different size\n"),
-                      abfd, name);
-             break;
-           }
-
-         /* Set the output_section field so that lang_add_section
-            does not create a lang_input_section structure for this
-            section.  Since there might be a symbol in the section
-            being discarded, we must retain a pointer to the section
-            which we are really going to use.  */
-         sec->output_section = bfd_abs_section_ptr;
-         sec->kept_section = l->sec;
-
-         if (flags & SEC_GROUP)
-           bfd_discard_group (abfd, sec);
-
-         return;
-       }
-    }
-
-  /* This is the first section with this name.  Record it.  Allocate
-     the memory from the same obstack as the hash table is kept in.  */
-
-  l = bfd_hash_allocate (&already_linked_table, sizeof *l);
-
-  l->sec = sec;
-  l->next = already_linked_list->entry;
-  already_linked_list->entry = l;
-}
-
-/* Support routines for the hash table used by section_already_linked,
-   initialize the table, fill in an entry and remove the table.  */
-
-static struct bfd_hash_entry *
-already_linked_newfunc (struct bfd_hash_entry *entry ATTRIBUTE_UNUSED,
-                       struct bfd_hash_table *table,
-                       const char *string ATTRIBUTE_UNUSED)
-{
-  struct already_linked_hash_entry *ret =
-    bfd_hash_allocate (table, sizeof (struct already_linked_hash_entry));
-
-  ret->entry = NULL;
-
-  return &ret->root;
-}
-
-static void
-already_linked_table_init (void)
-{
-  if (! bfd_hash_table_init_n (&already_linked_table,
-                              already_linked_newfunc,
-                              42))
-    einfo (_("%P%F: Failed to create hash table\n"));
-}
-
-static void
-already_linked_table_free (void)
-{
-  bfd_hash_table_free (&already_linked_table);
+  if (!(abfd->flags & DYNAMIC))
+    bfd_section_already_linked (abfd, sec);
 }
 \f
 /* The wild routines.
@@ -1060,25 +1789,6 @@ already_linked_table_free (void)
    explicit actions, like foo.o(.text), bar.o(.text) and
    foo.o(.text, .data).  */
 
-/* Return TRUE if the PATTERN argument is a wildcard pattern.
-   Although backslashes are treated specially if a pattern contains
-   wildcards, we do not consider the mere presence of a backslash to
-   be enough to cause the pattern to be treated as a wildcard.
-   That lets us handle DOS filenames more naturally.  */
-
-static bfd_boolean
-wildcardp (const char *pattern)
-{
-  const char *s;
-
-  for (s = pattern; *s != '\0'; ++s)
-    if (*s == '?'
-       || *s == '*'
-       || *s == '[')
-      return TRUE;
-  return FALSE;
-}
-
 /* Add SECTION to the output section OUTPUT.  Do this by creating a
    lang_input_section statement which is placed at PTR.  FILE is the
    input file which holds SECTION.  */
@@ -1089,19 +1799,11 @@ lang_add_section (lang_statement_list_type *ptr,
                  lang_output_section_statement_type *output,
                  lang_input_statement_type *file)
 {
-  flagword flags;
+  flagword flags = section->flags;
   bfd_boolean discard;
 
-  flags = bfd_get_section_flags (section->owner, section);
-
-  discard = FALSE;
-
-  /* Discard sections marked with SEC_EXCLUDE if we are doing a final
-     link.  Discard debugging sections marked with SEC_EXCLUDE on a
-     relocatable link too.  */
-  if ((flags & SEC_EXCLUDE) != 0
-      && ((flags & SEC_DEBUGGING) != 0 || !link_info.relocatable))
-    discard = TRUE;
+  /* Discard sections marked with SEC_EXCLUDE.  */
+  discard = (flags & SEC_EXCLUDE) != 0;
 
   /* Discard input sections which are assigned to a section named
      DISCARD_SECTION_NAME.  */
@@ -1136,6 +1838,19 @@ lang_add_section (lang_statement_list_type *ptr,
       first = ! output->bfd_section->linker_has_input;
       output->bfd_section->linker_has_input = 1;
 
+      if (!link_info.relocatable
+         && !stripped_excluded_sections)
+       {
+         asection *s = output->bfd_section->map_tail.s;
+         output->bfd_section->map_tail.s = section;
+         section->map_head.s = NULL;
+         section->map_tail.s = s;
+         if (s != NULL)
+           s->map_head.s = section;
+         else
+           output->bfd_section->map_head.s = section;
+       }
+
       /* Add a section reference to the list.  */
       new = new_stat (lang_input_section, ptr);
 
@@ -1163,32 +1878,32 @@ lang_add_section (lang_statement_list_type *ptr,
        flags &= ~ (SEC_LINK_ONCE | SEC_LINK_DUPLICATES);
 
       /* If this is not the first input section, and the SEC_READONLY
-         flag is not currently set, then don't set it just because the
-         input section has it set.  */
+        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.  */
+        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)
        {
@@ -1208,7 +1923,7 @@ lang_add_section (lang_statement_list_type *ptr,
 
       /* 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;
@@ -1217,15 +1932,56 @@ lang_add_section (lang_statement_list_type *ptr,
       if (output->section_alignment != -1)
        output->bfd_section->alignment_power = output->section_alignment;
 
-      if (section->flags & SEC_BLOCK)
+      if (bfd_get_arch (section->owner) == bfd_arch_tic54x
+         && (section->flags & SEC_TIC54X_BLOCK) != 0)
        {
-         section->output_section->flags |= SEC_BLOCK;
+         output->bfd_section->flags |= SEC_TIC54X_BLOCK;
          /* FIXME: This value should really be obtained from the bfd...  */
          output->block_value = 128;
        }
     }
 }
 
+/* Compare sections ASEC and BSEC according to SORT.  */
+
+static int
+compare_section (sort_type sort, asection *asec, asection *bsec)
+{
+  int ret;
+
+  switch (sort)
+    {
+    default:
+      abort ();
+
+    case by_alignment_name:
+      ret = (bfd_section_alignment (bsec->owner, bsec)
+            - bfd_section_alignment (asec->owner, asec));
+      if (ret)
+       break;
+      /* Fall through.  */
+
+    case by_name:
+      ret = strcmp (bfd_get_section_name (asec->owner, asec),
+                   bfd_get_section_name (bsec->owner, bsec));
+      break;
+
+    case by_name_alignment:
+      ret = strcmp (bfd_get_section_name (asec->owner, asec),
+                   bfd_get_section_name (bsec->owner, bsec));
+      if (ret)
+       break;
+      /* Fall through.  */
+
+    case by_alignment:
+      ret = (bfd_section_alignment (bsec->owner, bsec)
+            - bfd_section_alignment (asec->owner, asec));
+      break;
+    }
+
+  return ret;
+}
+
 /* Handle wildcard sorting.  This returns the lang_input_section which
    should follow the one we are going to create for SECTION and FILE,
    based on the sorting requirements of WILD.  It returns NULL if the
@@ -1240,7 +1996,8 @@ wild_sort (lang_wild_statement_type *wild,
   const char *section_name;
   lang_statement_union_type *l;
 
-  if (!wild->filenames_sorted && (sec == NULL || !sec->spec.sorted))
+  if (!wild->filenames_sorted
+      && (sec == NULL || sec->spec.sorted == none))
     return NULL;
 
   section_name = bfd_get_section_name (file->the_bfd, section);
@@ -1253,7 +2010,7 @@ wild_sort (lang_wild_statement_type *wild,
       ls = &l->input_section;
 
       /* Sorting by filename takes precedence over sorting by section
-         name.  */
+        name.  */
 
       if (wild->filenames_sorted)
        {
@@ -1262,9 +2019,9 @@ wild_sort (lang_wild_statement_type *wild,
          int i;
 
          /* The PE support for the .idata section as generated by
-             dlltool assumes that files will be sorted by the name of
-             the archive and then the name of the file within the
-             archive.  */
+            dlltool assumes that files will be sorted by the name of
+            the archive and then the name of the file within the
+            archive.  */
 
          if (file->the_bfd != NULL
              && bfd_my_archive (file->the_bfd) != NULL)
@@ -1312,16 +2069,11 @@ wild_sort (lang_wild_statement_type *wild,
        }
 
       /* Here either the files are not sorted by name, or we are
-         looking at the sections for this file.  */
+        looking at the sections for this file.  */
 
-      if (sec != NULL && sec->spec.sorted)
-       {
-         if (strcmp (section_name,
-                     bfd_get_section_name (ls->ifile->the_bfd,
-                                           ls->section))
-             < 0)
-           break;
-       }
+      if (sec != NULL && sec->spec.sorted != none)
+       if (compare_section (sec->spec.sorted, section, ls->section) < 0)
+         break;
     }
 
   return l;
@@ -1343,11 +2095,6 @@ output_section_callback (lang_wild_statement_type *ptr,
   if (unique_section_p (section))
     return;
 
-  /* If the wild pattern was marked KEEP, the member sections
-     should be as well.  */
-  if (ptr->keep_sections)
-    section->flags |= SEC_KEEP;
-
   before = wild_sort (ptr, sec, file, section);
 
   /* Here BEFORE points to the lang_input_section which
@@ -1394,20 +2141,14 @@ check_section_callback (lang_wild_statement_type *ptr ATTRIBUTE_UNUSED,
                        struct wildcard_list *sec ATTRIBUTE_UNUSED,
                        asection *section,
                        lang_input_statement_type *file ATTRIBUTE_UNUSED,
-                       void *output)
+                       void *data)
 {
   /* Exclude sections that match UNIQUE_SECTION_LIST.  */
   if (unique_section_p (section))
     return;
 
-  if (section->output_section == NULL)
-    {
-      flagword flags = bfd_get_section_flags (section->owner, section);
-
-      if ((flags & SEC_READONLY) == 0)
-       ((lang_output_section_statement_type *) output)->all_input_readonly
-         = FALSE;
-    }
+  if (section->output_section == NULL && (section->flags & SEC_READONLY) == 0)
+    ((lang_output_section_statement_type *) data)->all_input_readonly = FALSE;
 }
 
 /* This is passed a file name which must have been seen already and
@@ -1437,8 +2178,8 @@ lookup_name (const char *name)
     }
 
   if (search == NULL)
-    search = new_afile (name, lang_input_file_is_search_file_enum, default_target,
-                       FALSE);
+    search = new_afile (name, lang_input_file_is_search_file_enum,
+                       default_target, FALSE);
 
   /* If we have already added this file, or this file is not real
      (FIXME: can that ever actually happen?) or the name is NULL
@@ -1454,6 +2195,67 @@ lookup_name (const char *name)
   return search;
 }
 
+/* Save LIST as a list of libraries whose symbols should not be exported.  */
+
+struct excluded_lib
+{
+  char *name;
+  struct excluded_lib *next;
+};
+static struct excluded_lib *excluded_libs;
+
+void
+add_excluded_libs (const char *list)
+{
+  const char *p = list, *end;
+
+  while (*p != '\0')
+    {
+      struct excluded_lib *entry;
+      end = strpbrk (p, ",:");
+      if (end == NULL)
+       end = p + strlen (p);
+      entry = xmalloc (sizeof (*entry));
+      entry->next = excluded_libs;
+      entry->name = xmalloc (end - p + 1);
+      memcpy (entry->name, p, end - p);
+      entry->name[end - p] = '\0';
+      excluded_libs = entry;
+      if (*end == '\0')
+        break;
+      p = end + 1;
+    }
+}
+
+static void
+check_excluded_libs (bfd *abfd)
+{
+  struct excluded_lib *lib = excluded_libs;
+
+  while (lib)
+    {
+      int len = strlen (lib->name);
+      const char *filename = lbasename (abfd->filename);
+
+      if (strcmp (lib->name, "ALL") == 0)
+       {
+         abfd->no_export = TRUE;
+         return;
+       }
+
+      if (strncmp (lib->name, filename, len) == 0
+         && (filename[len] == '\0'
+             || (filename[len] == '.' && filename[len + 1] == 'a'
+                 && filename[len + 2] == '\0')))
+       {
+         abfd->no_export = TRUE;
+         return;
+       }
+
+      lib = lib->next;
+    }
+}
+
 /* Get the symbols for an input file.  */
 
 static bfd_boolean
@@ -1510,6 +2312,9 @@ load_symbols (lang_input_statement_type *entry,
 
       ldfile_assumed_script = TRUE;
       parser_input = input_script;
+      /* We want to use the same -Bdynamic/-Bstatic as the one for
+        ENTRY.  */
+      config.dynamic_link = entry->dynamic;
       yyparse ();
       ldfile_assumed_script = FALSE;
 
@@ -1538,6 +2343,8 @@ load_symbols (lang_input_statement_type *entry,
       break;
 
     case bfd_archive:
+      check_excluded_libs (entry->the_bfd);
+
       if (entry->whole_archive)
        {
          bfd *member = NULL;
@@ -1832,7 +2639,8 @@ open_output (const char *name)
                  /* Oh dear - we could not find any targets that
                     satisfy our requirements.  */
                  if (winner == NULL)
-                   einfo (_("%P: warning: could not find any targets that match endianness requirement\n"));
+                   einfo (_("%P: warning: could not find any targets"
+                            " that match endianness requirement\n"));
                  else
                    output_target = winner->name;
                }
@@ -1852,10 +2660,6 @@ open_output (const char *name)
 
   delete_output_file_on_failure = TRUE;
 
-#if 0
-  output->flags |= D_PAGED;
-#endif
-
   if (! bfd_set_format (output, bfd_object))
     einfo (_("%P%F:%s: can not make object file: %E\n"), name);
   if (! bfd_set_arch_mach (output,
@@ -1975,8 +2779,8 @@ open_input_bfds (lang_statement_union_type *s, bfd_boolean force)
              s->input_statement.target = current_target;
 
              /* If we are being called from within a group, and this
-                 is an archive which has already been searched, then
-                 force it to be researched unless the whole archive
+                is an archive which has already been searched, then
+                force it to be researched unless the whole archive
                 has been loaded already.  */
              if (force
                  && !s->input_statement.whole_archive
@@ -2003,31 +2807,6 @@ open_input_bfds (lang_statement_union_type *s, bfd_boolean force)
     }
 }
 
-/* If there are [COMMONS] statements, put a wild one into the bss
-   section.  */
-
-static void
-lang_reasonable_defaults (void)
-{
-#if 0
-  lang_output_section_statement_lookup (".text");
-  lang_output_section_statement_lookup (".data");
-
-  default_common_section = lang_output_section_statement_lookup (".bss");
-
-  if (!placed_commons)
-    {
-      lang_wild_statement_type *new =
-      new_stat (lang_wild_statement,
-               &default_common_section->children);
-
-      new->section_name = "COMMON";
-      new->filename = NULL;
-      lang_list_init (&new->children);
-    }
-#endif
-}
-
 /* Add a symbol to a hash of symbols used in DEFINED (NAME) expressions.  */
 
 void
@@ -2166,8 +2945,9 @@ lang_place_undefineds (void)
 /* Check for all readonly or some readwrite sections.  */
 
 static void
-check_input_sections (lang_statement_union_type *s,
-                     lang_output_section_statement_type *output_section_statement)
+check_input_sections
+  (lang_statement_union_type *s,
+   lang_output_section_statement_type *output_section_statement)
 {
   for (; s != (lang_statement_union_type *) NULL; s = s->header.next)
     {
@@ -2197,29 +2977,95 @@ check_input_sections (lang_statement_union_type *s,
     }
 }
 
+/* Update wildcard statements if needed.  */
+
+static void
+update_wild_statements (lang_statement_union_type *s)
+{
+  struct wildcard_list *sec;
+
+  switch (sort_section)
+    {
+    default:
+      FAIL ();
+
+    case none:
+      break;
+
+    case by_name:
+    case by_alignment:
+      for (; s != NULL; s = s->header.next)
+       {
+         switch (s->header.type)
+           {
+           default:
+             break;
+
+           case lang_wild_statement_enum:
+             sec = s->wild_statement.section_list;
+             if (sec != NULL)
+               {
+                 switch (sec->spec.sorted)
+                   {
+                   case none:
+                     sec->spec.sorted = sort_section;
+                     break;
+                   case by_name:
+                     if (sort_section == by_alignment)
+                       sec->spec.sorted = by_name_alignment;
+                     break;
+                   case by_alignment:
+                     if (sort_section == by_name)
+                       sec->spec.sorted = by_alignment_name;
+                     break;
+                   default:
+                     break;
+                   }
+               }
+             break;
+
+           case lang_constructors_statement_enum:
+             update_wild_statements (constructor_list.head);
+             break;
+
+           case lang_output_section_statement_enum:
+             update_wild_statements
+               (s->output_section_statement.children.head);
+             break;
+
+           case lang_group_statement_enum:
+             update_wild_statements (s->group_statement.children.head);
+             break;
+           }
+       }
+      break;
+    }
+}
+
 /* Open input files and attach to output sections.  */
 
 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)
            {
-             if (s->output_section_statement.constraint == -1)
+             if (s->output_section_statement.constraint != ONLY_IF_RW
+                 && s->output_section_statement.constraint != ONLY_IF_RO)
                break;
              s->output_section_statement.all_input_readonly = TRUE;
              check_input_sections (s->output_section_statement.children.head,
@@ -2246,27 +3092,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 */
+         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.  */
@@ -2276,16 +3127,27 @@ map_input_to_output_sections
          FAIL ();
          break;
        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;
-         }
+         /* Mark the specified section with the supplied address.  
+
+            If this section was actually a segment marker, then the
+            directive is ignored if the linker script explicitly
+            processed the segment marker.  Originally, the linker
+            treated segment directives (like -Ttext on the
+            command-line) as section directives.  We honor the
+            section directive semantics for backwards compatibilty;
+            linker scripts that do not specifically check for
+            SEGMENT_START automatically get the old semantics.  */
+         if (!s->address_statement.segment 
+             || !s->address_statement.segment->used)
+           {
+             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;
        }
     }
@@ -2295,37 +3157,74 @@ map_input_to_output_sections
    added.  For example, ldemul_before_allocation can remove dynamic
    sections if they turn out to be not needed.  Clean them up here.  */
 
-static void
+void
 strip_excluded_output_sections (void)
 {
-  lang_statement_union_type *u;
+  lang_output_section_statement_type *os;
+
+  /* Run lang_size_sections (if not already done).  */
+  if (expld.phase != lang_mark_phase_enum)
+    {
+      expld.phase = lang_mark_phase_enum;
+      expld.dataseg.phase = exp_dataseg_none;
+      one_lang_size_sections_pass (NULL, FALSE);
+      lang_reset_memory_regions ();
+    }
 
-  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;
+      asection *output_section;
+      bfd_boolean exclude;
 
-      os = &u->output_section_statement;
       if (os->constraint == -1)
        continue;
-      s = os->bfd_section;
-      if (s != NULL && (s->flags & SEC_EXCLUDE) != 0)
-       {
-         asection **p;
 
-         os->bfd_section = NULL;
+      output_section = os->bfd_section;
+      if (output_section == NULL)
+       continue;
+
+      exclude = (output_section->rawsize == 0
+                && (output_section->flags & SEC_KEEP) == 0
+                && !bfd_section_removed_from_list (output_bfd,
+                                                   output_section));
+
+      /* Some sections have not yet been sized, notably .gnu.version,
+        .dynsym, .dynstr and .hash.  These all have SEC_LINKER_CREATED
+        input sections, so don't drop output sections that have such
+        input sections unless they are also marked SEC_EXCLUDE.  */
+      if (exclude && output_section->map_head.s != NULL)
+       {
+         asection *s;
 
-         for (p = &output_bfd->sections; *p; p = &(*p)->next)
-           if (*p == s)
+         for (s = output_section->map_head.s; s != NULL; s = s->map_head.s)
+           if ((s->flags & SEC_LINKER_CREATED) != 0
+               && (s->flags & SEC_EXCLUDE) == 0)
              {
-               bfd_section_list_remove (output_bfd, p);
-               output_bfd->section_count--;
+               exclude = FALSE;
                break;
              }
        }
+
+      /* TODO: Don't just junk map_head.s, turn them into link_orders.  */
+      output_section->map_head.link_order = NULL;
+      output_section->map_tail.link_order = NULL;
+
+      if (exclude)
+       {
+         /* We don't set bfd_section to NULL since bfd_section of the
+            removed output section statement may still be used.  */
+         os->ignored = TRUE;
+         output_section->flags |= SEC_EXCLUDE;
+         bfd_section_list_remove (output_bfd, output_section);
+         output_bfd->section_count--;
+       }
     }
+
+  /* Stop future calls to lang_add_section from messing with map_head
+     and map_tail link_order fields.  */
+  stripped_excluded_sections = TRUE;
 }
 
 static void
@@ -2355,14 +3254,14 @@ print_output_section_statement
              ++len;
            }
 
-         minfo ("0x%V %W", section->vma, section->_raw_size);
+         minfo ("0x%V %W", section->vma, section->size);
 
          if (output_section_statement->load_base != NULL)
            {
              bfd_vma addr;
 
              addr = exp_get_abs_int (output_section_statement->load_base, 0,
-                                     "load base", lang_final_phase_enum);
+                                     "load base");
              minfo (_(" load address 0x%V"), addr);
            }
        }
@@ -2374,29 +3273,117 @@ print_output_section_statement
                        output_section_statement);
 }
 
+/* Scan for the use of the destination in the right hand side
+   of an expression.  In such cases we will not compute the
+   correct expression, since the value of DST that is used on
+   the right hand side will be its final value, not its value
+   just before this expression is evaluated.  */
+   
+static bfd_boolean
+scan_for_self_assignment (const char * dst, etree_type * rhs)
+{
+  if (rhs == NULL || dst == NULL)
+    return FALSE;
+
+  switch (rhs->type.node_class)
+    {
+    case etree_binary:
+      return scan_for_self_assignment (dst, rhs->binary.lhs)
+       ||   scan_for_self_assignment (dst, rhs->binary.rhs);
+
+    case etree_trinary:
+      return scan_for_self_assignment (dst, rhs->trinary.lhs)
+       ||   scan_for_self_assignment (dst, rhs->trinary.rhs);
+
+    case etree_assign:
+    case etree_provided:
+    case etree_provide:
+      if (strcmp (dst, rhs->assign.dst) == 0)
+       return TRUE;
+      return scan_for_self_assignment (dst, rhs->assign.src);
+
+    case etree_unary:
+      return scan_for_self_assignment (dst, rhs->unary.child);
+
+    case etree_value:
+      if (rhs->value.str)
+       return strcmp (dst, rhs->value.str) == 0;
+      return FALSE;
+
+    case etree_name:
+      if (rhs->name.name)
+       return strcmp (dst, rhs->name.name) == 0;
+      return FALSE;
+
+    default:
+      break;
+    }
+
+  return FALSE;
+}
+
+
 static void
 print_assignment (lang_assignment_statement_type *assignment,
                  lang_output_section_statement_type *output_section)
 {
-  int i;
-  etree_value_type result;
+  unsigned int i;
+  bfd_boolean is_dot;
+  bfd_boolean computation_is_valid = TRUE;
+  etree_type *tree;
 
   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 (result.valid_p)
+  if (assignment->exp->type.node_class == etree_assert)
+    {
+      is_dot = FALSE;
+      tree = assignment->exp->assert_s.child;
+      computation_is_valid = TRUE;
+    }
+  else
+    {
+      const char *dst = assignment->exp->assign.dst;
+
+      is_dot = (dst[0] == '.' && dst[1] == 0);
+      tree = assignment->exp->assign.src;
+      computation_is_valid = is_dot || (scan_for_self_assignment (dst, tree) == FALSE);
+    }
+
+  exp_fold_tree (tree, output_section->bfd_section, &print_dot);
+  if (expld.result.valid_p)
     {
-      const char *dst;
       bfd_vma value;
 
-      value = result.value + result.section->bfd_section->vma;
-      dst = assignment->exp->assign.dst;
+      if (computation_is_valid)
+       {
+         value = expld.result.value;
+
+         if (expld.result.section)
+           value += expld.result.section->vma;
+
+         minfo ("0x%V", value);
+         if (is_dot)
+           print_dot = value;
+       }
+      else
+       {
+         struct bfd_link_hash_entry *h;
+
+         h = bfd_link_hash_lookup (link_info.hash, assignment->exp->assign.dst,
+                                   FALSE, FALSE, TRUE);
+         if (h)
+           {
+             value = h->u.def.value;
+
+             if (expld.result.section)
+             value += expld.result.section->vma;
 
-      minfo ("0x%V", value);
-      if (dst[0] == '.' && dst[1] == 0)
-       print_dot = value;
+             minfo ("[0x%V]", value);
+           }
+         else
+           minfo ("[unresolved]");
+       }
     }
   else
     {
@@ -2407,9 +3394,7 @@ print_assignment (lang_assignment_statement_type *assignment,
     }
 
   minfo ("                ");
-
   exp_print_tree (assignment->exp);
-
   print_nl ();
 }
 
@@ -2456,6 +3441,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);
@@ -2467,59 +3455,64 @@ static void
 print_input_section (lang_input_section_type *in)
 {
   asection *i = in->section;
-  bfd_size_type size = i->_cooked_size != 0 ? i->_cooked_size : i->_raw_size;
+  bfd_size_type size = i->size;
 
   init_opb ();
   if (size != 0)
     {
-      print_space ();
+      int len;
+      bfd_vma addr;
 
+      print_space ();
       minfo ("%s", i->name);
 
-      if (i->output_section != NULL)
+      len = 1 + strlen (i->name);
+      if (len >= SECTION_NAME_MAP_LENGTH - 1)
        {
-         int len;
+         print_nl ();
+         len = 0;
+       }
+      while (len < SECTION_NAME_MAP_LENGTH)
+       {
+         print_space ();
+         ++len;
+       }
 
-         len = 1 + strlen (i->name);
-         if (len >= SECTION_NAME_MAP_LENGTH - 1)
-           {
-             print_nl ();
-             len = 0;
-           }
-         while (len < SECTION_NAME_MAP_LENGTH)
-           {
-             print_space ();
-             ++len;
-           }
+      if (i->output_section != NULL && (i->flags & SEC_EXCLUDE) == 0)
+       addr = i->output_section->vma + i->output_offset;
+      else
+       {
+         addr = print_dot;
+         size = 0;
+       }
 
-         minfo ("0x%V %W %B\n",
-                i->output_section->vma + i->output_offset, TO_ADDR (size),
-                i->owner);
+      minfo ("0x%V %W %B\n", addr, TO_ADDR (size), i->owner);
 
-         if (i->_cooked_size != 0 && i->_cooked_size != i->_raw_size)
-           {
-             len = SECTION_NAME_MAP_LENGTH + 3;
+      if (size != i->rawsize && i->rawsize != 0)
+       {
+         len = SECTION_NAME_MAP_LENGTH + 3;
 #ifdef BFD64
-             len += 16;
+         len += 16;
 #else
-             len += 8;
+         len += 8;
 #endif
-             while (len > 0)
-               {
-                 print_space ();
-                 --len;
-               }
-
-             minfo (_("%W (size before relaxing)\n"), i->_raw_size);
+         while (len > 0)
+           {
+             print_space ();
+             --len;
            }
 
+         minfo (_("%W (size before relaxing)\n"), i->rawsize);
+       }
+
+      if (i->output_section != NULL && (i->flags & SEC_EXCLUDE) == 0)
+       {
          if (command_line.reduce_memory_overheads)
            bfd_link_hash_traverse (link_info.hash, print_one_symbol, i);
          else
            print_all_symbols (i);
 
-         print_dot = (i->output_section->vma + i->output_offset
-                      + TO_ADDR (size));
+         print_dot = addr + TO_ADDR (size);
        }
     }
 }
@@ -2853,16 +3846,22 @@ insert_pad (lang_statement_union_type **ptr,
            bfd_vma dot)
 {
   static fill_type zero_fill = { 1, { 0 } };
-  lang_statement_union_type *pad;
+  lang_statement_union_type *pad = NULL;
 
-  pad = ((lang_statement_union_type *)
-        ((char *) ptr - offsetof (lang_statement_union_type, header.next)));
-  if (ptr != &statement_list.head
+  if (ptr != &statement_list.head)
+    pad = ((lang_statement_union_type *)
+          ((char *) ptr - offsetof (lang_statement_union_type, header.next)));
+  if (pad != NULL
+      && pad->header.type == lang_padding_statement_enum
+      && pad->padding_statement.output_section == output_section)
+    {
+      /* Use the existing pad statement.  */
+    }
+  else if ((pad = *ptr) != NULL
       && pad->header.type == lang_padding_statement_enum
       && pad->padding_statement.output_section == output_section)
     {
-      /* Use the existing pad statement.  The above test on output
-        section is probably redundant, but it doesn't hurt to check.  */
+      /* Use the existing pad statement.  */
     }
   else
     {
@@ -2878,21 +3877,22 @@ insert_pad (lang_statement_union_type **ptr,
     }
   pad->padding_statement.output_offset = dot - output_section->vma;
   pad->padding_statement.size = alignment_needed;
-  output_section->_raw_size += alignment_needed;
+  output_section->size += alignment_needed;
 }
 
 /* Work out how much this section will move the dot point.  */
 
 static bfd_vma
-size_input_section (lang_statement_union_type **this_ptr,
-                   lang_output_section_statement_type *output_section_statement,
-                   fill_type *fill,
-                   bfd_vma dot)
+size_input_section
+  (lang_statement_union_type **this_ptr,
+   lang_output_section_statement_type *output_section_statement,
+   fill_type *fill,
+   bfd_vma dot)
 {
   lang_input_section_type *is = &((*this_ptr)->input_section);
   asection *i = is->section;
 
-  if (!is->ifile->just_syms_flag)
+  if (!is->ifile->just_syms_flag && (i->flags & SEC_EXCLUDE) == 0)
     {
       unsigned int alignment_needed;
       asection *o;
@@ -2922,11 +3922,8 @@ size_input_section (lang_statement_union_type **this_ptr,
       i->output_offset = dot - o->vma;
 
       /* Mark how big the output section must be to contain this now.  */
-      if (i->_cooked_size != 0)
-       dot += TO_ADDR (i->_cooked_size);
-      else
-       dot += TO_ADDR (i->_raw_size);
-      o->_raw_size = TO_SIZE (dot - o->vma);
+      dot += TO_ADDR (i->size);
+      o->size = TO_SIZE (dot - o->vma);
     }
   else
     {
@@ -2936,67 +3933,91 @@ size_input_section (lang_statement_union_type **this_ptr,
   return dot;
 }
 
-#define IGNORE_SECTION(bfd, s) \
-  (((bfd_get_section_flags (bfd, s) & SEC_THREAD_LOCAL)                        \
-    ? ((bfd_get_section_flags (bfd, s) & (SEC_LOAD | SEC_NEVER_LOAD))  \
-       != SEC_LOAD)                                                    \
-    :  ((bfd_get_section_flags (bfd, s) & (SEC_ALLOC | SEC_NEVER_LOAD)) \
-       != SEC_ALLOC))                                                  \
-   || bfd_section_size (bfd, s) == 0)
+static int
+sort_sections_by_lma (const void *arg1, const void *arg2)
+{
+  const asection *sec1 = *(const asection **) arg1;
+  const asection *sec2 = *(const asection **) arg2;
+
+  if (bfd_section_lma (sec1->owner, sec1)
+      < bfd_section_lma (sec2->owner, sec2))
+    return -1;
+  else if (bfd_section_lma (sec1->owner, sec1)
+          > bfd_section_lma (sec2->owner, sec2))
+    return 1;
+
+  return 0;
+}
+
+#define IGNORE_SECTION(s) \
+  ((s->flags & SEC_NEVER_LOAD) != 0                            \
+   || (s->flags & SEC_ALLOC) == 0                              \
+   || ((s->flags & SEC_THREAD_LOCAL) != 0                      \
+       && (s->flags & SEC_LOAD) == 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)
 {
-  asection *s;
+  asection *s, *os;
+  asection **sections, **spp;
+  unsigned int count;
+  bfd_vma s_start;
+  bfd_vma s_end;
+  bfd_vma os_start;
+  bfd_vma os_end;
+  bfd_size_type amt;
+
+  if (bfd_count_sections (output_bfd) <= 1)
+    return;
+
+  amt = bfd_count_sections (output_bfd) * sizeof (asection *);
+  sections = xmalloc (amt);
 
   /* Scan all sections in the output list.  */
+  count = 0;
   for (s = output_bfd->sections; s != NULL; s = s->next)
     {
-      asection *os;
-
-      /* Ignore sections which are not loaded or which have no contents.  */
-      if (IGNORE_SECTION (output_bfd, s))
+      /* Only consider loadable sections with real contents.  */
+      if (IGNORE_SECTION (s) || s->size == 0)
        continue;
 
-      /* Once we reach section 's' stop our seach.  This prevents two
-        warning messages from being produced, one for 'section A overlaps
-        section B' and one for 'section B overlaps section A'.  */
-      for (os = output_bfd->sections; os != s; os = os->next)
-       {
-         bfd_vma s_start;
-         bfd_vma s_end;
-         bfd_vma os_start;
-         bfd_vma os_end;
-
-         /* Only consider loadable sections with real contents.  */
-         if (IGNORE_SECTION (output_bfd, os))
-           continue;
-
-         /* We must check the sections' LMA addresses not their
-            VMA addresses because overlay sections can have
-            overlapping VMAs but they must have distinct LMAs.  */
-         s_start = bfd_section_lma (output_bfd, s);
-         os_start = bfd_section_lma (output_bfd, os);
-         s_end = s_start + TO_ADDR (bfd_section_size (output_bfd, s)) - 1;
-         os_end = os_start + TO_ADDR (bfd_section_size (output_bfd, os)) - 1;
-
-         /* Look for an overlap.  */
-         if ((s_end < os_start) || (s_start > os_end))
-           continue;
+      sections[count] = s;
+      count++;
+    }
+  
+  if (count <= 1)
+    return;
 
-         einfo (
-_("%X%P: section %s [%V -> %V] overlaps section %s [%V -> %V]\n"),
-                s->name, s_start, s_end, os->name, os_start, os_end);
+  qsort (sections, (size_t) count, sizeof (asection *),
+        sort_sections_by_lma);
 
-         /* Once we have found one overlap for this section,
-            stop looking for others.  */
-         break;
-       }
+  spp = sections;
+  s = *spp++;
+  s_start = bfd_section_lma (output_bfd, s);
+  s_end = s_start + TO_ADDR (s->size) - 1;
+  for (count--; count; count--)
+    {
+      /* We must check the sections' LMA addresses not their VMA
+        addresses because overlay sections can have overlapping VMAs
+        but they must have distinct LMAs.  */
+      os = s;
+      os_start = s_start; 
+      os_end = s_end;
+      s = *spp++;
+      s_start = bfd_section_lma (output_bfd, s);
+      s_end = s_start + TO_ADDR (s->size) - 1;
+
+      /* Look for an overlap.  */
+      if (s_end >= os_start && s_start <= os_end)
+       einfo (_("%X%P: section %s [%V -> %V] overlaps section %s [%V -> %V]\n"),
+              s->name, s_start, s_end, os->name, os_start, os_end);
     }
+
+  free (sections);
 }
 
 /* Make sure the new address is within the region.  We explicitly permit the
@@ -3017,7 +4038,8 @@ os_region_check (lang_output_section_statement_type *os,
     {
       if (tree != NULL)
        {
-         einfo (_("%X%P: address 0x%v of %B section %s is not within region %s\n"),
+         einfo (_("%X%P: address 0x%v of %B section %s"
+                  " is not within region %s\n"),
                 region->current,
                 os->bfd_section->owner,
                 os->bfd_section->name,
@@ -3054,36 +4076,54 @@ lang_size_sections_1
        {
        case lang_output_section_statement_enum:
          {
-           bfd_vma after;
+           bfd_vma newdot, after;
            lang_output_section_statement_type *os;
 
            os = &s->output_section_statement;
+           if (os->addr_tree != NULL)
+             {
+               os->processed = FALSE;
+               exp_fold_tree (os->addr_tree, bfd_abs_section_ptr, &dot);
+
+               if (!expld.result.valid_p
+                   && expld.phase != lang_mark_phase_enum)
+                 einfo (_("%F%S: non constant or forward reference"
+                          " address expression for section %s\n"),
+                        os->name);
+
+               dot = expld.result.value + expld.result.section->vma;
+             }
+
            if (os->bfd_section == NULL)
-             /* This section was never actually created.  */
+             /* This section was removed or never actually created.  */
              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
               to do this, but nothing simple comes to mind.  */
-           if ((os->bfd_section->flags & SEC_COFF_SHARED_LIBRARY) != 0)
+           if ((bfd_get_flavour (output_bfd) == bfd_target_ecoff_flavour
+                || bfd_get_flavour (output_bfd) == bfd_target_coff_flavour)
+               && (os->bfd_section->flags & SEC_COFF_SHARED_LIBRARY) != 0)
              {
                asection *input;
 
                if (os->children.head == NULL
                    || os->children.head->header.next != NULL
-                   || os->children.head->header.type != lang_input_section_enum)
-                 einfo (_("%P%X: Internal error on COFF shared library section %s\n"),
-                        os->name);
+                   || (os->children.head->header.type
+                       != lang_input_section_enum))
+                 einfo (_("%P%X: Internal error on COFF shared library"
+                          " section %s\n"), os->name);
 
                input = os->children.head->input_section.section;
                bfd_set_section_vma (os->bfd_section->owner,
                                     os->bfd_section,
                                     bfd_section_vma (input->owner, input));
-               os->bfd_section->_raw_size = input->_raw_size;
+               os->bfd_section->size = input->size;
                break;
              }
 
+           newdot = dot;
            if (bfd_is_abs_section (os->bfd_section))
              {
                /* No matter what happens, an abs section starts at zero.  */
@@ -3096,10 +4136,10 @@ lang_size_sections_1
                    /* No address specified for this section, get one
                       from the region specification.  */
                    if (os->region == NULL
-                       || (((bfd_get_section_flags (output_bfd, os->bfd_section)
-                             & (SEC_ALLOC | SEC_LOAD)) != 0)
+                       || ((os->bfd_section->flags & (SEC_ALLOC | SEC_LOAD))
                            && os->region->name[0] == '*'
-                           && strcmp (os->region->name, DEFAULT_MEMORY_REGION) == 0))
+                           && strcmp (os->region->name,
+                                      DEFAULT_MEMORY_REGION) == 0))
                      {
                        os->region = lang_memory_default (os->bfd_section);
                      }
@@ -3107,106 +4147,96 @@ lang_size_sections_1
                    /* If a loadable section is using the default memory
                       region, and some non default memory regions were
                       defined, issue an error message.  */
-                   if (!IGNORE_SECTION (output_bfd, os->bfd_section)
+                   if (!IGNORE_SECTION (os->bfd_section)
                        && ! link_info.relocatable
                        && check_regions
-                       && strcmp (os->region->name, DEFAULT_MEMORY_REGION) == 0
+                       && strcmp (os->region->name,
+                                  DEFAULT_MEMORY_REGION) == 0
                        && lang_memory_region_list != NULL
                        && (strcmp (lang_memory_region_list->name,
                                    DEFAULT_MEMORY_REGION) != 0
-                           || lang_memory_region_list->next != NULL))
+                           || lang_memory_region_list->next != NULL)
+                       && expld.phase != lang_mark_phase_enum)
                      {
                        /* By default this is an error rather than just a
                           warning because if we allocate the section to the
                           default memory region we can end up creating an
                           excessively large binary, or even seg faulting when
                           attempting to perform a negative seek.  See
-                            http://sources.redhat.com/ml/binutils/2003-04/msg00423.html
+                          sources.redhat.com/ml/binutils/2003-04/msg00423.html
                           for an example of this.  This behaviour can be
                           overridden by the using the --no-check-sections
                           switch.  */
                        if (command_line.check_section_addresses)
-                         einfo (_("%P%F: error: no memory region specified for loadable section `%s'\n"),
+                         einfo (_("%P%F: error: no memory region specified"
+                                  " for loadable section `%s'\n"),
                                 bfd_get_section_name (output_bfd,
                                                       os->bfd_section));
                        else
-                         einfo (_("%P: warning: no memory region specified for loadable section `%s'\n"),
+                         einfo (_("%P: warning: no memory region specified"
+                                  " for loadable section `%s'\n"),
                                 bfd_get_section_name (output_bfd,
                                                       os->bfd_section));
                      }
 
-                   dot = os->region->current;
+                   newdot = os->region->current;
 
                    if (os->section_alignment == -1)
                      {
-                       bfd_vma olddot;
-
-                       olddot = dot;
-                       dot = align_power (dot,
-                                          os->bfd_section->alignment_power);
-
-                       if (dot != olddot && config.warn_section_align)
-                         einfo (_("%P: warning: changing start of section %s by %u bytes\n"),
-                                os->name, (unsigned int) (dot - olddot));
+                       bfd_vma savedot = newdot;
+                       newdot = align_power (newdot,
+                                             os->bfd_section->alignment_power);
+
+                       if (newdot != savedot
+                           && config.warn_section_align
+                           && expld.phase != lang_mark_phase_enum)
+                         einfo (_("%P: warning: changing start of section"
+                                  " %s by %lu bytes\n"),
+                                os->name, (unsigned long) (newdot - savedot));
                      }
                  }
-               else
-                 {
-                   etree_value_type r;
-
-                   os->processed = -1;
-                   r = exp_fold_tree (os->addr_tree,
-                                      abs_output_section,
-                                      lang_allocating_phase_enum,
-                                      dot, &dot);
-                   os->processed = 0;
-                   
-                   if (!r.valid_p)
-                     einfo (_("%F%S: non constant or forward reference address expression for section %s\n"),
-                            os->name);
-
-                   dot = r.value + r.section->bfd_section->vma;
-                 }
 
                /* The section starts here.
                   First, align to what the section needs.  */
 
                if (os->section_alignment != -1)
-                 dot = align_power (dot, os->section_alignment);
+                 newdot = align_power (newdot, os->section_alignment);
 
-               bfd_set_section_vma (0, os->bfd_section, dot);
+               bfd_set_section_vma (0, os->bfd_section, newdot);
 
                os->bfd_section->output_offset = 0;
              }
 
            lang_size_sections_1 (os->children.head, os, &os->children.head,
-                                 os->fill, dot, relax, check_regions);
+                                 os->fill, newdot, relax, check_regions);
+
+           os->processed = TRUE;
+
+           if (bfd_is_abs_section (os->bfd_section) || os->ignored)
+             {
+               ASSERT (os->bfd_section->size == 0);
+               break;
+             }
+
+           dot = os->bfd_section->vma;
 
            /* Put the section within the requested block size, or
               align at the block boundary.  */
-           after = ((os->bfd_section->vma
-                     + TO_ADDR (os->bfd_section->_raw_size)
+           after = ((dot
+                     + TO_ADDR (os->bfd_section->size)
                      + os->block_value - 1)
                     & - (bfd_vma) os->block_value);
 
-           if (bfd_is_abs_section (os->bfd_section))
-             ASSERT (after == os->bfd_section->vma);
-           else
-             os->bfd_section->_raw_size
-               = TO_SIZE (after - os->bfd_section->vma);
+           os->bfd_section->size = TO_SIZE (after - os->bfd_section->vma);
 
-           dot = os->bfd_section->vma;
            /* .tbss sections effectively have zero size.  */
            if ((os->bfd_section->flags & SEC_HAS_CONTENTS) != 0
                || (os->bfd_section->flags & SEC_THREAD_LOCAL) == 0
                || link_info.relocatable)
-             dot += TO_ADDR (os->bfd_section->_raw_size);
-
-           os->processed = 1;
+             dot += TO_ADDR (os->bfd_section->size);
 
            if (os->update_dot_tree != 0)
-             exp_fold_tree (os->update_dot_tree, abs_output_section,
-                            lang_allocating_phase_enum, dot, &dot);
+             exp_fold_tree (os->update_dot_tree, bfd_abs_section_ptr, &dot);
 
            /* Update dot in the region ?
               We only do this if the section is going to be allocated,
@@ -3217,10 +4247,8 @@ lang_size_sections_1
               addresses of sections after it. We have to update
               dot.  */
            if (os->region != NULL
-               && ((bfd_get_section_flags (output_bfd, os->bfd_section)
-                    & SEC_NEVER_LOAD) == 0
-                   || (bfd_get_section_flags (output_bfd, os->bfd_section)
-                       & (SEC_ALLOC | SEC_LOAD))))
+               && ((os->bfd_section->flags & SEC_NEVER_LOAD) == 0
+                   || (os->bfd_section->flags & (SEC_ALLOC | SEC_LOAD))))
              {
                os->region->current = dot;
 
@@ -3239,7 +4267,7 @@ lang_size_sections_1
                    /* Set load_base, which will be handled later.  */
                    os->load_base = exp_intop (os->lma_region->current);
                    os->lma_region->current +=
-                     TO_ADDR (os->bfd_section->_raw_size);
+                     TO_ADDR (os->bfd_section->size);
                    if (check_regions)
                      os_region_check (os, os->lma_region, NULL,
                                       os->bfd_section->lma);
@@ -3266,8 +4294,7 @@ lang_size_sections_1
 
            /* We might refer to provided symbols in the expression, and
               need to mark them as needed.  */
-           exp_fold_tree (s->data_statement.exp, abs_output_section,
-                          lang_allocating_phase_enum, dot, &dot);
+           exp_fold_tree (s->data_statement.exp, bfd_abs_section_ptr, &dot);
 
            switch (s->data_statement.type)
              {
@@ -3290,15 +4317,7 @@ lang_size_sections_1
            if (size < TO_SIZE ((unsigned) 1))
              size = TO_SIZE ((unsigned) 1);
            dot += TO_ADDR (size);
-           output_section_statement->bfd_section->_raw_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;
-             }
+           output_section_statement->bfd_section->size += size;
          }
          break;
 
@@ -3312,37 +4331,32 @@ lang_size_sections_1
              output_section_statement->bfd_section;
            size = bfd_get_reloc_size (s->reloc_statement.howto);
            dot += TO_ADDR (size);
-           output_section_statement->bfd_section->_raw_size += size;
+           output_section_statement->bfd_section->size += size;
          }
          break;
 
        case lang_wild_statement_enum:
-
          dot = lang_size_sections_1 (s->wild_statement.children.head,
                                      output_section_statement,
                                      &s->wild_statement.children.head,
                                      fill, dot, relax, check_regions);
-
          break;
 
        case lang_object_symbols_statement_enum:
          link_info.create_object_symbols_section =
            output_section_statement->bfd_section;
          break;
+
        case lang_output_statement_enum:
        case lang_target_statement_enum:
          break;
+
        case lang_input_section_enum:
          {
            asection *i;
 
            i = (*prev)->input_section.section;
-           if (! relax)
-             {
-               if (i->_cooked_size == 0)
-                 i->_cooked_size = i->_raw_size;
-             }
-           else
+           if (relax)
              {
                bfd_boolean again;
 
@@ -3355,31 +4369,33 @@ lang_size_sections_1
                                      output_section_statement->fill, dot);
          }
          break;
+
        case lang_input_statement_enum:
          break;
+
        case lang_fill_statement_enum:
          s->fill_statement.output_section =
            output_section_statement->bfd_section;
 
          fill = s->fill_statement.fill;
          break;
+
        case lang_assignment_statement_enum:
          {
            bfd_vma newdot = dot;
 
            exp_fold_tree (s->assignment_statement.exp,
-                          output_section_statement,
-                          lang_allocating_phase_enum,
-                          dot,
+                          output_section_statement->bfd_section,
                           &newdot);
 
-           if (newdot != dot)
+           if (newdot != dot && !output_section_statement->ignored)
              {
                if (output_section_statement == abs_output_section)
                  {
                    /* If we don't have an output section, then just adjust
                       the default memory address.  */
-                   lang_memory_region_lookup (DEFAULT_MEMORY_REGION, FALSE)->current = newdot;
+                   lang_memory_region_lookup (DEFAULT_MEMORY_REGION,
+                                              FALSE)->current = newdot;
                  }
                else
                  {
@@ -3391,14 +4407,15 @@ lang_size_sections_1
 
                    /* Don't neuter the pad below when relaxing.  */
                    s = s->header.next;
-                 }
-
-               /* If dot is advanced, this implies that the section should
-                  have space allocated to it, unless the user has explicitly
-                  stated that the section should never be loaded.  */
-               if (!(output_section_statement->flags & (SEC_NEVER_LOAD | SEC_ALLOC)))
-                 output_section_statement->bfd_section->flags |= SEC_ALLOC;
 
+                   /* If dot is advanced, this implies that the section
+                      should have space allocated to it, unless the
+                      user has explicitly stated that the section
+                      should never be loaded.  */
+                   if (!(output_section_statement->flags
+                         & (SEC_NEVER_LOAD | SEC_ALLOC)))
+                     output_section_statement->bfd_section->flags |= SEC_ALLOC;
+                 }
                dot = newdot;
              }
          }
@@ -3441,66 +4458,89 @@ lang_size_sections_1
   return dot;
 }
 
-bfd_vma
-lang_size_sections
-  (lang_statement_union_type *s,
-   lang_output_section_statement_type *output_section_statement,
-   lang_statement_union_type **prev,
-   fill_type *fill,
-   bfd_vma dot,
-   bfd_boolean *relax,
-   bfd_boolean check_regions)
+void
+one_lang_size_sections_pass (bfd_boolean *relax, bfd_boolean check_regions)
 {
-  bfd_vma result;
-  asection *o;
-
-  /* Callers of exp_fold_tree need to increment this.  */
   lang_statement_iteration++;
+  lang_size_sections_1 (statement_list.head, abs_output_section,
+                       &statement_list.head, 0, 0, relax, check_regions);
+}
+
+void
+lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions)
+{
+  expld.phase = lang_allocating_phase_enum;
+  expld.dataseg.phase = exp_dataseg_none;
 
-  exp_data_seg.phase = exp_dataseg_none;
-  result = lang_size_sections_1 (s, output_section_statement, prev, fill,
-                                dot, relax, check_regions);
-  if (exp_data_seg.phase == exp_dataseg_end_seen
-      && link_info.relro && exp_data_seg.relro_end)
+  one_lang_size_sections_pass (relax, check_regions);
+  if (expld.dataseg.phase == exp_dataseg_end_seen
+      && link_info.relro && expld.dataseg.relro_end)
     {
       /* If DATA_SEGMENT_ALIGN DATA_SEGMENT_RELRO_END pair was seen, try
-        to put exp_data_seg.relro on a (common) page boundary.  */
-
-      exp_data_seg.phase = exp_dataseg_relro_adjust;
-      result = lang_size_sections_1 (s, output_section_statement, prev, fill,
-                                    dot, relax, check_regions);
-      link_info.relro_start = exp_data_seg.base;
-      link_info.relro_end = exp_data_seg.relro_end;
+        to put expld.dataseg.relro on a (common) page boundary.  */
+      bfd_vma old_min_base, relro_end, maxpage;
+
+      expld.dataseg.phase = exp_dataseg_relro_adjust;
+      old_min_base = expld.dataseg.min_base;
+      maxpage = expld.dataseg.maxpagesize;
+      expld.dataseg.base += (-expld.dataseg.relro_end
+                            & (expld.dataseg.pagesize - 1));
+      /* Compute the expected PT_GNU_RELRO segment end.  */
+      relro_end = (expld.dataseg.relro_end + expld.dataseg.pagesize - 1)
+                 & ~(expld.dataseg.pagesize - 1);
+      if (old_min_base + maxpage < expld.dataseg.base)
+       {
+         expld.dataseg.base -= maxpage;
+         relro_end -= maxpage;
+       }
+      one_lang_size_sections_pass (relax, check_regions);
+      if (expld.dataseg.relro_end > relro_end)
+       {
+         /* The alignment of sections between DATA_SEGMENT_ALIGN
+            and DATA_SEGMENT_RELRO_END caused huge padding to be
+            inserted at DATA_SEGMENT_RELRO_END.  Try some other base.  */
+         asection *sec;
+         unsigned int max_alignment_power = 0;
+
+         /* Find maximum alignment power of sections between
+            DATA_SEGMENT_ALIGN and DATA_SEGMENT_RELRO_END.  */
+         for (sec = output_bfd->sections; sec; sec = sec->next)
+           if (sec->vma >= expld.dataseg.base
+               && sec->vma < expld.dataseg.relro_end
+               && sec->alignment_power > max_alignment_power)
+             max_alignment_power = sec->alignment_power;
+
+         if (((bfd_vma) 1 << max_alignment_power) < expld.dataseg.pagesize)
+           {
+             if (expld.dataseg.base - (1 << max_alignment_power)
+                 < old_min_base)
+               expld.dataseg.base += expld.dataseg.pagesize;
+             expld.dataseg.base -= (1 << max_alignment_power);
+             one_lang_size_sections_pass (relax, check_regions);
+           }
+       }
+      link_info.relro_start = expld.dataseg.base;
+      link_info.relro_end = expld.dataseg.relro_end;
     }
-  else if (exp_data_seg.phase == exp_dataseg_end_seen)
+  else if (expld.dataseg.phase == exp_dataseg_end_seen)
     {
       /* If DATA_SEGMENT_ALIGN DATA_SEGMENT_END pair was seen, check whether
         a page could be saved in the data segment.  */
       bfd_vma first, last;
 
-      first = -exp_data_seg.base & (exp_data_seg.pagesize - 1);
-      last = exp_data_seg.end & (exp_data_seg.pagesize - 1);
+      first = -expld.dataseg.base & (expld.dataseg.pagesize - 1);
+      last = expld.dataseg.end & (expld.dataseg.pagesize - 1);
       if (first && last
-         && ((exp_data_seg.base & ~(exp_data_seg.pagesize - 1))
-             != (exp_data_seg.end & ~(exp_data_seg.pagesize - 1)))
-         && first + last <= exp_data_seg.pagesize)
+         && ((expld.dataseg.base & ~(expld.dataseg.pagesize - 1))
+             != (expld.dataseg.end & ~(expld.dataseg.pagesize - 1)))
+         && first + last <= expld.dataseg.pagesize)
        {
-         exp_data_seg.phase = exp_dataseg_adjust;
-         lang_statement_iteration++;
-         result = lang_size_sections_1 (s, output_section_statement, prev,
-                                        fill, dot, relax, check_regions);
+         expld.dataseg.phase = exp_dataseg_adjust;
+         one_lang_size_sections_pass (relax, check_regions);
        }
     }
 
-  /* Some backend relaxers want to refer to the output section size.  Give
-     them a section size that does not change on the next call while they
-     relax.  We can't set this at top because lang_reset_memory_regions
-     which is called before we get here, sets _raw_size to 0 on relaxing
-     rounds.  */
-  for (o = output_bfd->sections; o != NULL; o = o->next)
-    o->_cooked_size = o->_raw_size;
-
-  return result;
+  expld.phase = lang_final_phase_enum;
 }
 
 /* Worker function for lang_do_assignments.  Recursiveness goes here.  */
@@ -3528,7 +4568,7 @@ lang_do_assignments_1
            lang_output_section_statement_type *os;
 
            os = &(s->output_section_statement);
-           if (os->bfd_section != NULL)
+           if (os->bfd_section != NULL && !os->ignored)
              {
                dot = os->bfd_section->vma;
                lang_do_assignments_1 (os->children.head, os, os->fill, dot);
@@ -3536,48 +4576,40 @@ lang_do_assignments_1
                if ((os->bfd_section->flags & SEC_HAS_CONTENTS) != 0
                    || (os->bfd_section->flags & SEC_THREAD_LOCAL) == 0
                    || link_info.relocatable)
-                 dot += TO_ADDR (os->bfd_section->_raw_size);
+                 dot += TO_ADDR (os->bfd_section->size);
              }
            if (os->load_base)
              {
                /* If nothing has been placed into the output section then
                   it won't have a bfd_section.  */
-               if (os->bfd_section)
+               if (os->bfd_section && !os->ignored)
                  {
                    os->bfd_section->lma
-                     = exp_get_abs_int (os->load_base, 0, "load base",
-                                        lang_final_phase_enum);
+                     = exp_get_abs_int (os->load_base, 0, "load base");
                  }
              }
          }
          break;
+
        case lang_wild_statement_enum:
 
          dot = lang_do_assignments_1 (s->wild_statement.children.head,
                                       output_section_statement,
                                       fill, dot);
-
          break;
 
        case lang_object_symbols_statement_enum:
        case lang_output_statement_enum:
        case lang_target_statement_enum:
-#if 0
-       case lang_common_statement_enum:
-#endif
          break;
+
        case lang_data_statement_enum:
-         {
-           etree_value_type value;
-
-           value = exp_fold_tree (s->data_statement.exp,
-                                  abs_output_section,
-                                  lang_final_phase_enum, dot, &dot);
-           if (!value.valid_p)
-             einfo (_("%F%P: invalid data statement\n"));
-           s->data_statement.value
-             = value.value + value.section->bfd_section->vma;
-         }
+         exp_fold_tree (s->data_statement.exp, bfd_abs_section_ptr, &dot);
+         if (expld.result.valid_p)
+           s->data_statement.value = (expld.result.value
+                                      + expld.result.section->vma);
+         else
+           einfo (_("%F%P: invalid data statement\n"));
          {
            unsigned int size;
            switch (s->data_statement.type)
@@ -3605,16 +4637,12 @@ lang_do_assignments_1
          break;
 
        case lang_reloc_statement_enum:
-         {
-           etree_value_type value;
-
-           value = exp_fold_tree (s->reloc_statement.addend_exp,
-                                  abs_output_section,
-                                  lang_final_phase_enum, dot, &dot);
-           s->reloc_statement.addend_value = value.value;
-           if (!value.valid_p)
-             einfo (_("%F%P: invalid reloc statement\n"));
-         }
+         exp_fold_tree (s->reloc_statement.addend_exp,
+                        bfd_abs_section_ptr, &dot);
+         if (expld.result.valid_p)
+           s->reloc_statement.addend_value = expld.result.value;
+         else
+           einfo (_("%F%P: invalid reloc statement\n"));
          dot += TO_ADDR (bfd_get_reloc_size (s->reloc_statement.howto));
          break;
 
@@ -3622,28 +4650,24 @@ lang_do_assignments_1
          {
            asection *in = s->input_section.section;
 
-           if (in->_cooked_size != 0)
-             dot += TO_ADDR (in->_cooked_size);
-           else
-             dot += TO_ADDR (in->_raw_size);
+           if ((in->flags & SEC_EXCLUDE) == 0)
+             dot += TO_ADDR (in->size);
          }
          break;
 
        case lang_input_statement_enum:
          break;
+
        case lang_fill_statement_enum:
          fill = s->fill_statement.fill;
          break;
-       case lang_assignment_statement_enum:
-         {
-           exp_fold_tree (s->assignment_statement.exp,
-                          output_section_statement,
-                          lang_final_phase_enum,
-                          dot,
-                          &dot);
-         }
 
+       case lang_assignment_statement_enum:
+         exp_fold_tree (s->assignment_statement.exp,
+                        output_section_statement->bfd_section,
+                        &dot);
          break;
+
        case lang_padding_statement_enum:
          dot += TO_ADDR (s->padding_statement.size);
          break;
@@ -3652,29 +4676,24 @@ lang_do_assignments_1
          dot = lang_do_assignments_1 (s->group_statement.children.head,
                                       output_section_statement,
                                       fill, dot);
-
          break;
 
        default:
          FAIL ();
          break;
+
        case lang_address_statement_enum:
          break;
        }
-
     }
   return dot;
 }
 
 void
-lang_do_assignments (lang_statement_union_type *s,
-                    lang_output_section_statement_type *output_section_statement,
-                    fill_type *fill,
-                    bfd_vma dot)
+lang_do_assignments (void)
 {
-  /* Callers of exp_fold_tree need to increment this.  */
   lang_statement_iteration++;
-  lang_do_assignments_1 (s, output_section_statement, fill, dot);
+  lang_do_assignments_1 (statement_list.head, abs_output_section, NULL, 0);
 }
 
 /* Fix any .startof. or .sizeof. symbols.  When the assemblers see the
@@ -3715,10 +4734,7 @@ lang_set_startof (void)
       if (h != NULL && h->type == bfd_link_hash_undefined)
        {
          h->type = bfd_link_hash_defined;
-         if (s->_cooked_size != 0)
-           h->u.def.value = TO_ADDR (s->_cooked_size);
-         else
-           h->u.def.value = TO_ADDR (s->_raw_size);
+         h->u.def.value = TO_ADDR (s->size);
          h->u.def.section = bfd_abs_section_ptr;
        }
 
@@ -3727,7 +4743,7 @@ lang_set_startof (void)
 }
 
 static void
-lang_finish (void)
+lang_end (void)
 {
   struct bfd_link_hash_entry *h;
   bfd_boolean warn;
@@ -3739,9 +4755,9 @@ lang_finish (void)
 
   if (entry_symbol.name == NULL)
     {
-      /* No entry has been specified.  Look for start, but don't warn
-        if we don't find it.  */
-      entry_symbol.name = "start";
+      /* No entry has been specified.  Look for the default entry, but
+        don't warn if we don't find it.  */
+      entry_symbol.name = entry_symbol_default;
       warn = FALSE;
     }
 
@@ -3767,7 +4783,7 @@ lang_finish (void)
       const char *send;
 
       /* We couldn't find the entry symbol.  Try parsing it as a
-         number.  */
+        number.  */
       val = bfd_scan_vma (entry_symbol.name, &send, 0);
       if (*send == '\0')
        {
@@ -3784,7 +4800,8 @@ lang_finish (void)
          if (ts != NULL)
            {
              if (warn)
-               einfo (_("%P: warning: cannot find entry symbol %s; defaulting to %V\n"),
+               einfo (_("%P: warning: cannot find entry symbol %s;"
+                        " defaulting to %V\n"),
                       entry_symbol.name,
                       bfd_get_section_vma (output_bfd, ts));
              if (! bfd_set_start_address (output_bfd,
@@ -3795,13 +4812,15 @@ lang_finish (void)
          else
            {
              if (warn)
-               einfo (_("%P: warning: cannot find entry symbol %s; not setting start address\n"),
+               einfo (_("%P: warning: cannot find entry symbol %s;"
+                        " not setting start address\n"),
                       entry_symbol.name);
            }
        }
     }
 
-  bfd_hash_table_free (&lang_definedness_table);
+  /* Don't bfd_hash_table_free (&lang_definedness_table);
+     map file output may result in a call of lang_track_definedness.  */
 }
 
 /* This is a small function used when we want to ignore errors from
@@ -3827,8 +4846,9 @@ lang_check (void)
   for (file = file_chain.head; file != NULL; file = file->input_statement.next)
     {
       input_bfd = file->input_statement.the_bfd;
-      compatible = bfd_arch_get_compatible (input_bfd, output_bfd,
-                                           command_line.accept_unknown_input_arch);
+      compatible
+       = bfd_arch_get_compatible (input_bfd, output_bfd,
+                                  command_line.accept_unknown_input_arch);
 
       /* In general it is not possible to perform a relocatable
         link between differing object formats when the input
@@ -3841,7 +4861,8 @@ lang_check (void)
              || bfd_get_flavour (input_bfd) != bfd_get_flavour (output_bfd))
          && (bfd_get_file_flags (input_bfd) & HAS_RELOC) != 0)
        {
-         einfo (_("%P%F: Relocatable linking with relocations from format %s (%B) to format %s (%B) is not supported\n"),
+         einfo (_("%P%F: Relocatable linking with relocations from"
+                  " format %s (%B) to format %s (%B) is not supported\n"),
                 bfd_get_target (input_bfd), input_bfd,
                 bfd_get_target (output_bfd), output_bfd);
          /* einfo with %F exits.  */
@@ -3850,7 +4871,8 @@ lang_check (void)
       if (compatible == NULL)
        {
          if (command_line.warn_mismatch)
-           einfo (_("%P: warning: %s architecture of input file `%B' is incompatible with %s output\n"),
+           einfo (_("%P: warning: %s architecture of input file `%B'"
+                    " is incompatible with %s output\n"),
                   bfd_printable_name (input_bfd), input_bfd,
                   bfd_printable_name (output_bfd));
        }
@@ -3862,17 +4884,17 @@ lang_check (void)
          bfd_error_handler_type pfn = NULL;
 
          /* If we aren't supposed to warn about mismatched input
-             files, temporarily set the BFD error handler to a
-             function which will do nothing.  We still want to call
-             bfd_merge_private_bfd_data, since it may set up
-             information which is needed in the output file.  */
+            files, temporarily set the BFD error handler to a
+            function which will do nothing.  We still want to call
+            bfd_merge_private_bfd_data, since it may set up
+            information which is needed in the output file.  */
          if (! command_line.warn_mismatch)
            pfn = bfd_set_error_handler (ignore_bfd_errors);
          if (! bfd_merge_private_bfd_data (input_bfd, output_bfd))
            {
              if (command_line.warn_mismatch)
-               einfo (_("%P%X: failed to merge target specific data of file %B\n"),
-                      input_bfd);
+               einfo (_("%P%X: failed to merge target specific data"
+                        " of file %B\n"), input_bfd);
            }
          if (! command_line.warn_mismatch)
            bfd_set_error_handler (pfn);
@@ -3926,8 +4948,8 @@ lang_one_common (struct bfd_link_hash_entry *h, void *info)
   section = h->u.c.p->section;
 
   /* Increase the size of the section to align the common sym.  */
-  section->_cooked_size += ((bfd_vma) 1 << (power_of_two + opb_shift)) - 1;
-  section->_cooked_size &= (- (bfd_vma) 1 << (power_of_two + opb_shift));
+  section->size += ((bfd_vma) 1 << (power_of_two + opb_shift)) - 1;
+  section->size &= (- (bfd_vma) 1 << (power_of_two + opb_shift));
 
   /* Adjust the alignment if necessary.  */
   if (power_of_two > section->alignment_power)
@@ -3936,10 +4958,10 @@ lang_one_common (struct bfd_link_hash_entry *h, void *info)
   /* Change the symbol from common to defined.  */
   h->type = bfd_link_hash_defined;
   h->u.def.section = section;
-  h->u.def.value = section->_cooked_size;
+  h->u.def.value = section->size;
 
   /* Increase the size of the section.  */
-  section->_cooked_size += size;
+  section->size += size;
 
   /* Make sure the section is allocated in memory, and make sure that
      it is no longer a common section.  */
@@ -4012,12 +5034,12 @@ lang_place_orphans (void)
          if (s->output_section == NULL)
            {
              /* This section of the file is not attached, root
-                around for a sensible place for it to go.  */
+                around for a sensible place for it to go.  */
 
              if (file->just_syms_flag)
-               {
-                 abort ();
-               }
+               bfd_link_just_syms (file->the_bfd, s, &link_info);
+             else if ((s->flags & SEC_EXCLUDE) != 0)
+               s->output_section = bfd_abs_section_ptr;
              else if (strcmp (s->name, "COMMON") == 0)
                {
                  /* This is a lonely common section which must have
@@ -4028,12 +5050,6 @@ lang_place_orphans (void)
                    {
                      if (default_common_section == NULL)
                        {
-#if 0
-                         /* This message happens when using the
-                             svr3.ifile linker script, so I have
-                             disabled it.  */
-                         info_msg (_("%P: no [COMMON] command, defaulting to .bss\n"));
-#endif
                          default_common_section =
                            lang_output_section_statement_lookup (".bss");
 
@@ -4202,11 +5218,6 @@ lang_enter_output_section_statement (const char *output_section_statement_name,
     lang_output_section_statement_lookup_1 (output_section_statement_name,
                                            constraint);
 
-  /* Add this statement to tree.  */
-#if 0
-  add_statement (lang_output_section_statement_enum,
-                output_section_statement);
-#endif
   /* Make next things chain into subchain of this.  */
 
   if (os->addr_tree == NULL)
@@ -4222,9 +5233,9 @@ lang_enter_output_section_statement (const char *output_section_statement_name,
   stat_ptr = &os->children;
 
   os->subsection_alignment =
-    topower (exp_get_value_int (subalign, -1, "subsection alignment", 0));
+    topower (exp_get_value_int (subalign, -1, "subsection alignment"));
   os->section_alignment =
-    topower (exp_get_value_int (align, -1, "section alignment", 0));
+    topower (exp_get_value_int (align, -1, "section alignment"));
 
   os->load_base = ebase;
   return os;
@@ -4246,6 +5257,7 @@ lang_reset_memory_regions (void)
 {
   lang_memory_region_type *p = lang_memory_region_list;
   asection *o;
+  lang_output_section_statement_type *os;
 
   for (p = lang_memory_region_list; p != NULL; p = p->next)
     {
@@ -4253,12 +5265,20 @@ lang_reset_memory_regions (void)
       p->current = p->origin;
     }
 
+  for (os = &lang_output_section_statement.head->output_section_statement;
+       os != NULL;
+       os = os->next)
+    os->processed = FALSE;
+
   for (o = output_bfd->sections; o != NULL; o = o->next)
-    o->_raw_size = 0;
+    {
+      /* Save the last size for possible use by bfd_relax_section.  */
+      o->rawsize = o->size;
+      o->size = 0;
+    }
 }
 
-/* If the wild pattern was marked KEEP, the member sections
-   should be as well.  */
+/* Worker for lang_gc_sections_1.  */
 
 static void
 gc_section_callback (lang_wild_statement_type *ptr,
@@ -4267,18 +5287,12 @@ gc_section_callback (lang_wild_statement_type *ptr,
                     lang_input_statement_type *file ATTRIBUTE_UNUSED,
                     void *data ATTRIBUTE_UNUSED)
 {
+  /* If the wild pattern was marked KEEP, the member sections
+     should be as well.  */
   if (ptr->keep_sections)
     section->flags |= SEC_KEEP;
 }
 
-/* Handle a wild statement, marking it against GC.  */
-
-static void
-lang_gc_wild (lang_wild_statement_type *s)
-{
-  walk_wild (s, gc_section_callback, NULL);
-}
-
 /* Iterate over sections marking them against GC.  */
 
 static void
@@ -4289,7 +5303,7 @@ lang_gc_sections_1 (lang_statement_union_type *s)
       switch (s->header.type)
        {
        case lang_wild_statement_enum:
-         lang_gc_wild (&s->wild_statement);
+         walk_wild (&s->wild_statement, gc_section_callback, NULL);
          break;
        case lang_constructors_statement_enum:
          lang_gc_sections_1 (constructor_list.head);
@@ -4333,13 +5347,27 @@ lang_gc_sections (void)
        }
     }
 
-  bfd_gc_sections (output_bfd, &link_info);
+  /* SEC_EXCLUDE is ignored when doing a relocatable link, except in
+     the special case of debug info.  (See bfd/stabs.c)
+     Twiddle the flag here, to simplify later linker code.  */
+  if (link_info.relocatable)
+    {
+      LANG_FOR_EACH_INPUT_STATEMENT (f)
+       {
+         asection *sec;
+         for (sec = f->the_bfd->sections; sec != NULL; sec = sec->next)
+           if ((sec->flags & SEC_DEBUGGING) == 0)
+             sec->flags &= ~SEC_EXCLUDE;
+       }
+    }
+
+  if (link_info.gc_sections)
+    bfd_gc_sections (output_bfd, &link_info);
 }
 
 void
 lang_process (void)
 {
-  lang_reasonable_defaults ();
   current_target = default_target;
 
   /* Open the output file.  */
@@ -4351,7 +5379,8 @@ lang_process (void)
   /* Add to the hash table all undefineds on the command line.  */
   lang_place_undefineds ();
 
-  already_linked_table_init ();
+  if (!bfd_section_already_linked_table_init ())
+    einfo (_("%P%F: Failed to create hash table\n"));
 
   /* Create a bfd for each input file.  */
   current_target = default_target;
@@ -4363,7 +5392,7 @@ lang_process (void)
 
   ldemul_after_open ();
 
-  already_linked_table_free ();
+  bfd_section_already_linked_table_free ();
 
   /* Make sure that we're not mixing architectures.  We call this
      after all the input files have been opened, but before we do any
@@ -4381,18 +5410,14 @@ lang_process (void)
   ldctor_build_sets ();
 
   /* Remove unreferenced sections if asked to.  */
-  if (command_line.gc_sections)
-    lang_gc_sections ();
-
-  /* If there were any SEC_MERGE sections, finish their merging, so that
-     section sizes can be computed.  This has to be done after GC of sections,
-     so that GCed sections are not merged, but before assigning output
-     sections, since removing whole input sections is hard then.  */
-  bfd_merge_sections (output_bfd, &link_info);
+  lang_gc_sections ();
 
   /* Size up the common data.  */
   lang_common ();
 
+  /* Update wild statements.  */
+  update_wild_statements (statement_list.head);
+
   /* Run through the contours of the script and attach input sections
      to the correct output sections.  */
   map_input_to_output_sections (statement_list.head, NULL, NULL);
@@ -4402,8 +5427,16 @@ lang_process (void)
 
   if (! link_info.relocatable)
     {
+      asection *found;
+
+      /* Merge SEC_MERGE sections.  This has to be done after GC of
+        sections, so that GCed sections are not merged, but before
+        assigning dynamic symbols, since removing whole input sections
+        is hard then.  */
+      bfd_merge_sections (output_bfd, &link_info);
+
       /* Look for a text section and set the readonly attribute in it.  */
-      asection *found = bfd_get_section_by_name (output_bfd, ".text");
+      found = bfd_get_section_by_name (output_bfd, ".text");
 
       if (found != NULL)
        {
@@ -4418,17 +5451,12 @@ lang_process (void)
      and other back-ends size dynamic sections.  */
   ldemul_before_allocation ();
 
-  if (!link_info.relocatable)
-    strip_excluded_output_sections ();
-
   /* We must record the program headers before we try to fix the
      section positions, since they will affect SIZEOF_HEADERS.  */
   lang_record_phdrs ();
 
   /* Size up the sections.  */
-  lang_size_sections (statement_list.head, abs_output_section,
-                     &statement_list.head, 0, 0, NULL,
-                     command_line.relax ? FALSE : TRUE);
+  lang_size_sections (NULL, !command_line.relax);
 
   /* Now run around and relax if we can.  */
   if (command_line.relax)
@@ -4446,17 +5474,15 @@ lang_process (void)
 
          /* Do all the assignments with our current guesses as to
             section sizes.  */
-         lang_do_assignments (statement_list.head, abs_output_section,
-                              NULL, 0);
+         lang_do_assignments ();
 
          /* We must do this after lang_do_assignments, because it uses
-            _raw_size.  */
+            size.  */
          lang_reset_memory_regions ();
 
          /* Perform another relax pass - this time we know where the
             globals are, so can make a better guess.  */
-         lang_size_sections (statement_list.head, abs_output_section,
-                             &statement_list.head, 0, 0, &relax_again, FALSE);
+         lang_size_sections (&relax_again, FALSE);
 
          /* If the normal relax is done and the relax finalize pass
             is not performed yet, we perform another relax pass.  */
@@ -4469,10 +5495,9 @@ lang_process (void)
       while (relax_again);
 
       /* Final extra sizing to report errors.  */
-      lang_do_assignments (statement_list.head, abs_output_section, NULL, 0);
+      lang_do_assignments ();
       lang_reset_memory_regions ();
-      lang_size_sections (statement_list.head, abs_output_section,
-                         &statement_list.head, 0, 0, NULL, TRUE);
+      lang_size_sections (NULL, TRUE);
     }
 
   /* See if anything special should be done now we know how big
@@ -4485,7 +5510,7 @@ lang_process (void)
   /* Do all the assignments, now that we know the final resting places
      of all the symbols.  */
 
-  lang_do_assignments (statement_list.head, abs_output_section, NULL, 0);
+  lang_do_assignments ();
 
   /* Make sure that the section addresses make sense.  */
   if (! link_info.relocatable
@@ -4493,9 +5518,8 @@ lang_process (void)
     lang_check_section_addresses ();
 
   /* Final stuffs.  */
-
   ldemul_finish ();
-  lang_finish ();
+  lang_end ();
 }
 
 /* EXPORTED TO YACC */
@@ -4534,21 +5558,24 @@ lang_add_wild (struct wildcard_spec *filespec,
   if (filespec != NULL)
     {
       new->filename = filespec->name;
-      new->filenames_sorted = filespec->sorted;
+      new->filenames_sorted = filespec->sorted == by_name;
     }
   new->section_list = section_list;
   new->keep_sections = keep_sections;
   lang_list_init (&new->children);
+  analyze_walk_wild_section_handler (new);
 }
 
 void
-lang_section_start (const char *name, etree_type *address)
+lang_section_start (const char *name, etree_type *address,
+                   const segment_type *segment)
 {
   lang_address_statement_type *ad;
 
   ad = new_stat (lang_address_statement, stat_ptr);
   ad->section_name = name;
   ad->address = address;
+  ad->segment = segment;
 }
 
 /* Set the start symbol to NAME.  CMDLINE is nonzero if this is called
@@ -4568,6 +5595,16 @@ lang_add_entry (const char *name, bfd_boolean cmdline)
     }
 }
 
+/* Set the default start symbol to NAME.  .em files should use this,
+   not lang_add_entry, to override the use of "start" if neither the
+   linker script nor the command line specifies an entry point.  NAME
+   must be permanently allocated.  */
+void
+lang_default_entry (const char *name)
+{
+  entry_symbol_default = name;
+}
+
 void
 lang_add_target (const char *name)
 {
@@ -4662,7 +5699,7 @@ lang_startup (const char *name)
 {
   if (startup_file != NULL)
     {
-      einfo (_("%P%Fmultiple STARTUP files\n"));
+      einfo (_("%P%Fmultiple STARTUP files\n"));
     }
   first_file->filename = name;
   first_file->local_sym_name = name;
@@ -4699,8 +5736,9 @@ lang_get_regions (lang_memory_region_type **region,
 {
   *lma_region = lang_memory_region_lookup (lma_memspec, FALSE);
 
-  /* If no runtime region or VMA has been specified, but the load region has
-     been specified, then use the load region for the runtime region as well.  */
+  /* If no runtime region or VMA has been specified, but the load region
+     has been specified, then use the load region for the runtime region
+     as well.  */
   if (lma_memspec != NULL
       && ! have_vma
       && strcmp (memspec, DEFAULT_MEMORY_REGION) == 0)
@@ -4784,7 +5822,7 @@ lang_abs_symbol_at_end_of (const char *secname, const char *name)
        h->u.def.value = 0;
       else
        h->u.def.value = (bfd_get_section_vma (output_bfd, sec)
-                         + TO_ADDR (bfd_section_size (output_bfd, sec)));
+                         + TO_ADDR (sec->size));
 
       h->u.def.section = bfd_abs_section_ptr;
     }
@@ -4860,8 +5898,7 @@ lang_new_phdr (const char *name,
   n = stat_alloc (sizeof (struct lang_phdr));
   n->next = NULL;
   n->name = name;
-  n->type = exp_get_value_int (type, 0, "program header type",
-                              lang_final_phase_enum);
+  n->type = exp_get_value_int (type, 0, "program header type");
   n->filehdr = filehdr;
   n->phdrs = phdrs;
   n->at = at;
@@ -4882,7 +5919,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 *));
@@ -4894,14 +5931,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;
 
@@ -4939,14 +5974,12 @@ lang_record_phdrs (void)
       if (l->flags == NULL)
        flags = 0;
       else
-       flags = exp_get_vma (l->flags, 0, "phdr flags",
-                            lang_final_phase_enum);
+       flags = exp_get_vma (l->flags, 0, "phdr flags");
 
       if (l->at == NULL)
        at = 0;
       else
-       at = exp_get_vma (l->at, 0, "phdr load address",
-                         lang_final_phase_enum);
+       at = exp_get_vma (l->at, 0, "phdr load address");
 
       if (! bfd_record_phdr (output_bfd, l->type,
                             l->flags != NULL, flags, l->at != NULL,
@@ -4957,22 +5990,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);
     }
 }
 
@@ -5225,8 +6258,8 @@ lang_vers_match (struct bfd_elf_version_expr_head *head,
                while (expr && strcmp (expr->symbol, sym) == 0)
                  if (expr->mask == BFD_ELF_VERSION_C_TYPE)
                    goto out_ret;
-               else
-                 expr = expr->next;
+                 else
+                   expr = expr->next;
              }
            /* Fallthrough */
          case BFD_ELF_VERSION_C_TYPE:
@@ -5237,8 +6270,8 @@ lang_vers_match (struct bfd_elf_version_expr_head *head,
                while (expr && strcmp (expr->symbol, cxx_sym) == 0)
                  if (expr->mask == BFD_ELF_VERSION_CXX_TYPE)
                    goto out_ret;
-               else
-                 expr = expr->next;
+                 else
+                   expr = expr->next;
              }
            /* Fallthrough */
          case BFD_ELF_VERSION_CXX_TYPE:
@@ -5249,8 +6282,8 @@ lang_vers_match (struct bfd_elf_version_expr_head *head,
                while (expr && strcmp (expr->symbol, java_sym) == 0)
                  if (expr->mask == BFD_ELF_VERSION_JAVA_TYPE)
                    goto out_ret;
-               else
-                 expr = expr->next;
+                 else
+                   expr = expr->next;
              }
            /* Fallthrough */
          default:
@@ -5263,10 +6296,13 @@ lang_vers_match (struct bfd_elf_version_expr_head *head,
     expr = head->remaining;
   else
     expr = prev->next;
-  while (expr)
+  for (; expr; expr = expr->next)
     {
       const char *s;
 
+      if (!expr->pattern)
+       continue;
+
       if (expr->pattern[0] == '*' && expr->pattern[1] == '\0')
        break;
 
@@ -5278,7 +6314,6 @@ lang_vers_match (struct bfd_elf_version_expr_head *head,
        s = sym;
       if (fnmatch (expr->pattern, s, 0) == 0)
        break;
-      expr = expr->next;
     }
 
 out_ret:
@@ -5333,21 +6368,24 @@ realsymbol (const char *pattern)
     }
 }
 
-/* This is called for each variable name or match expression.  */
+/* This is called for each variable name or match expression.  NEW is
+   the name of the symbol to match, or, if LITERAL_P is FALSE, a glob
+   pattern to be matched against symbol names.  */
 
 struct bfd_elf_version_expr *
 lang_new_vers_pattern (struct bfd_elf_version_expr *orig,
                       const char *new,
-                      const char *lang)
+                      const char *lang,
+                      bfd_boolean literal_p)
 {
   struct bfd_elf_version_expr *ret;
 
   ret = xmalloc (sizeof *ret);
   ret->next = orig;
-  ret->pattern = new;
+  ret->pattern = literal_p ? NULL : new;
   ret->symver = 0;
   ret->script = 0;
-  ret->symbol = realsymbol (new);
+  ret->symbol = literal_p ? new : realsymbol (new);
 
   if (lang == NULL || strcasecmp (lang, "C") == 0)
     ret->mask = BFD_ELF_VERSION_C_TYPE;
@@ -5499,7 +6537,8 @@ lang_register_vers_node (const char *name,
   if ((name[0] == '\0' && lang_elf_version_info != NULL)
       || (lang_elf_version_info && lang_elf_version_info->name[0] == '\0'))
     {
-      einfo (_("%X%P: anonymous version tag cannot be combined with other version tags\n"));
+      einfo (_("%X%P: anonymous version tag cannot be combined"
+              " with other version tags\n"));
       free (version);
       return;
     }
@@ -5527,16 +6566,17 @@ lang_register_vers_node (const char *name,
              while (e2 && strcmp (e1->symbol, e2->symbol) == 0)
                {
                  if (e1->mask == e2->mask)
-                   einfo (_("%X%P: duplicate expression `%s' in version information\n"),
-                          e1->symbol);
+                   einfo (_("%X%P: duplicate expression `%s'"
+                            " in version information\n"), e1->symbol);
                  e2 = e2->next;
                }
            }
          else if (!e1->symbol)
            for (e2 = t->locals.remaining; e2 != NULL; e2 = e2->next)
-             if (strcmp (e1->pattern, e2->pattern) == 0 && e1->mask == e2->mask)
-               einfo (_("%X%P: duplicate expression `%s' in version information\n"),
-                      e1->pattern);
+             if (strcmp (e1->pattern, e2->pattern) == 0
+                 && e1->mask == e2->mask)
+               einfo (_("%X%P: duplicate expression `%s'"
+                        " in version information\n"), e1->pattern);
        }
     }
 
@@ -5552,16 +6592,18 @@ lang_register_vers_node (const char *name,
              while (e2 && strcmp (e1->symbol, e2->symbol) == 0)
                {
                  if (e1->mask == e2->mask)
-                   einfo (_("%X%P: duplicate expression `%s' in version information\n"),
+                   einfo (_("%X%P: duplicate expression `%s'"
+                            " in version information\n"),
                           e1->symbol);
                  e2 = e2->next;
                }
            }
          else if (!e1->symbol)
            for (e2 = t->globals.remaining; e2 != NULL; e2 = e2->next)
-             if (strcmp (e1->pattern, e2->pattern) == 0 && e1->mask == e2->mask)
-               einfo (_("%X%P: duplicate expression `%s' in version information\n"),
-                      e1->pattern);
+             if (strcmp (e1->pattern, e2->pattern) == 0
+                 && e1->mask == e2->mask)
+               einfo (_("%X%P: duplicate expression `%s'"
+                        " in version information\n"), e1->pattern);
        }
     }
 
@@ -5619,7 +6661,7 @@ lang_do_version_exports_section (void)
       if (sec == NULL)
        continue;
 
-      len = bfd_section_size (is->the_bfd, sec);
+      len = sec->size;
       contents = xmalloc (len);
       if (!bfd_get_section_contents (is->the_bfd, sec, contents, 0, len))
        einfo (_("%X%P: unable to read .exports section contents\n"), sec);
@@ -5627,18 +6669,17 @@ lang_do_version_exports_section (void)
       p = contents;
       while (p < contents + len)
        {
-         greg = lang_new_vers_pattern (greg, p, NULL);
+         greg = lang_new_vers_pattern (greg, p, NULL, FALSE);
          p = strchr (p, '\0') + 1;
        }
 
       /* Do not free the contents, as we used them creating the regex.  */
 
       /* Do not include this section in the link.  */
-      bfd_set_section_flags (is->the_bfd, sec,
-       bfd_get_section_flags (is->the_bfd, sec) | SEC_EXCLUDE);
+      sec->flags |= SEC_EXCLUDE;
     }
 
-  lreg = lang_new_vers_pattern (NULL, "*", NULL);
+  lreg = lang_new_vers_pattern (NULL, "*", NULL, FALSE);
   lang_register_vers_node (command_line.version_exports_section,
                           lang_new_vers_node (greg, lreg), NULL);
 }
This page took 0.074948 seconds and 4 git commands to generate.