bfd/
[deliverable/binutils-gdb.git] / ld / ldlang.c
index 119fb7aefcfec4bcc5202825a6637efd6ba8501b..44fb136d84b3b51bdd9fc134be7b6ac8c01663de 100644 (file)
@@ -1,27 +1,29 @@
 /* Linker command language support.
    Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-   2001, 2002, 2003, 2004, 2005
+   2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
    Free Software Foundation, Inc.
 
-   This file is part of GLD, the Gnu Linker.
+   This file is part of the GNU Binutils.
 
-   GLD is free software; you can redistribute it and/or modify
+   This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
 
-   GLD is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    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, 51 Franklin Street - Fifth Floor, Boston, MA
-   02110-1301, USA.  */
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#include <limits.h>
 
-#include "bfd.h"
 #include "sysdep.h"
+#include "bfd.h"
 #include "libiberty.h"
 #include "safe-ctype.h"
 #include "obstack.h"
@@ -52,7 +54,6 @@ static struct obstack map_obstack;
 #define obstack_chunk_alloc xmalloc
 #define obstack_chunk_free free
 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;
@@ -62,34 +63,34 @@ static lang_input_statement_type *first_file;
 static const char *current_target;
 static const char *output_target;
 static lang_statement_list_type statement_list;
-static struct lang_phdr *lang_phdr_list;
 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 lang_input_statement_type *lookup_name (const char *);
-static bfd_boolean load_symbols (lang_input_statement_type *,
-                                lang_statement_list_type *);
 static struct bfd_hash_entry *lang_definedness_newfunc
  (struct bfd_hash_entry *, struct bfd_hash_table *, const char *);
 static void insert_undefined (const char *);
-static void print_all_symbols (asection *);
 static bfd_boolean sort_def_symbol (struct bfd_link_hash_entry *, void *);
 static void print_statement (lang_statement_union_type *,
                             lang_output_section_statement_type *);
 static void print_statement_list (lang_statement_union_type *,
                                  lang_output_section_statement_type *);
 static void print_statements (void);
+static void print_input_section (asection *);
 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);
+static void lang_finalize_version_expr_head
+  (struct bfd_elf_version_expr_head *);
 
 /* 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 };
+lang_statement_list_type input_file_chain;
 struct bfd_sym_chain entry_symbol = { NULL, NULL };
 static const char *entry_symbol_default = "start";
 const char *entry_section = ".text";
@@ -98,6 +99,7 @@ bfd_boolean lang_has_input_file = FALSE;
 bfd_boolean had_output_filename = FALSE;
 bfd_boolean lang_float_flag = FALSE;
 bfd_boolean delete_output_file_on_failure = FALSE;
+struct lang_phdr *lang_phdr_list;
 struct lang_nocrossrefs *nocrossref_list;
 static struct unique_sections *unique_section_list;
 static bfd_boolean ldlang_sysrooted_script = FALSE;
@@ -132,6 +134,65 @@ stat_alloc (size_t size)
   return obstack_alloc (&stat_obstack, size);
 }
 
+static int
+name_match (const char *pattern, const char *name)
+{
+  if (wildcardp (pattern))
+    return fnmatch (pattern, name, 0);
+  return strcmp (pattern, name);
+}
+
+/* If PATTERN is of the form archive:file, return a pointer to the
+   separator.  If not, return NULL.  */
+
+static char *
+archive_path (const char *pattern)
+{
+  char *p = NULL;
+
+  if (link_info.path_separator == 0)
+    return p;
+
+  p = strchr (pattern, link_info.path_separator);
+#ifdef HAVE_DOS_BASED_FILE_SYSTEM
+  if (p == NULL || link_info.path_separator != ':')
+    return p;
+
+  /* Assume a match on the second char is part of drive specifier,
+     as in "c:\silly.dos".  */
+  if (p == pattern + 1 && ISALPHA (*pattern))
+    p = strchr (p + 1, link_info.path_separator);
+#endif
+  return p;
+}
+
+/* Given that FILE_SPEC results in a non-NULL SEP result from archive_path,
+   return whether F matches FILE_SPEC.  */
+
+static bfd_boolean
+input_statement_is_archive_path (const char *file_spec, char *sep,
+                                lang_input_statement_type *f)
+{
+  bfd_boolean match = FALSE;
+
+  if ((*(sep + 1) == 0
+       || name_match (sep + 1, f->filename) == 0)
+      && ((sep != file_spec)
+         == (f->the_bfd != NULL && f->the_bfd->my_archive != NULL)))
+    {
+      match = TRUE;
+
+      if (sep != file_spec)
+       {
+         const char *aname = f->the_bfd->my_archive->filename;
+         *sep = 0;
+         match = name_match (file_spec, aname) == 0;
+         *sep = link_info.path_separator;
+       }
+    }
+  return match;
+}
+
 bfd_boolean
 unique_section_p (const asection *sec)
 {
@@ -145,12 +206,8 @@ unique_section_p (const asection *sec)
 
   secnam = sec->name;
   for (unam = unique_section_list; unam; unam = unam->next)
-    if (wildcardp (unam->name)
-       ? fnmatch (unam->name, secnam, 0) == 0
-       : strcmp (unam->name, secnam) == 0)
-      {
-       return TRUE;
-      }
+    if (name_match (unam->name, secnam) == 0)
+      return TRUE;
 
   return FALSE;
 }
@@ -170,42 +227,35 @@ walk_wild_consider_section (lang_wild_statement_type *ptr,
                            callback_t callback,
                            void *data)
 {
-  bfd_boolean skip = FALSE;
   struct name_list *list_tmp;
 
-  /* Don't process sections from files which were
-     excluded.  */
+  /* 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;
+      char *p = archive_path (list_tmp->name);
 
-      /* 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 (p != 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 (input_statement_is_archive_path (list_tmp->name, p, file))
+           return;
        }
 
-      if (skip)
-       break;
+      else if (name_match (list_tmp->name, file->filename) == 0)
+       return;
+
+      /* FIXME: Perhaps remove the following at some stage?  Matching
+        unadorned archives like this was never documented and has
+        been superceded by the archive:path syntax.  */
+      else if (file->the_bfd != NULL
+              && file->the_bfd->my_archive != NULL
+              && name_match (list_tmp->name,
+                             file->the_bfd->my_archive->filename) == 0)
+       return;
     }
 
-  if (!skip)
-    (*callback) (ptr, sec, s, file, data);
+  (*callback) (ptr, sec, s, file, data);
 }
 
 /* Lowest common denominator routine that can handle everything correctly,
@@ -234,10 +284,7 @@ walk_wild_section_general (lang_wild_statement_type *ptr,
            {
              const char *sname = bfd_get_section_name (file->the_bfd, s);
 
-             if (wildcardp (sec->spec.name))
-               skip = fnmatch (sec->spec.name, sname, 0) != 0;
-             else
-               skip = strcmp (sec->spec.name, sname) != 0;
+             skip = name_match (sec->spec.name, sname) != 0;
            }
 
          if (!skip)
@@ -279,7 +326,7 @@ find_section (lang_input_statement_type *file,
 {
   section_iterator_callback_data cb_data = { NULL, FALSE };
 
-  bfd_get_section_by_name_if (file->the_bfd, sec->spec.name, 
+  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;
@@ -316,6 +363,124 @@ match_simple_wild (const char *pattern, const char *name)
   return TRUE;
 }
 
+/* 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;
+}
+
+/* Build a Binary Search Tree to sort sections, unlike insertion sort
+   used in wild_sort(). BST is considerably faster if the number of
+   of sections are large.  */
+
+static lang_section_bst_type **
+wild_sort_fast (lang_wild_statement_type *wild,
+               struct wildcard_list *sec,
+               lang_input_statement_type *file ATTRIBUTE_UNUSED,
+               asection *section)
+{
+  lang_section_bst_type **tree;
+
+  tree = &wild->tree;
+  if (!wild->filenames_sorted
+      && (sec == NULL || sec->spec.sorted == none))
+    {
+      /* Append at the right end of tree.  */
+      while (*tree)
+       tree = &((*tree)->right);
+      return tree;
+    }
+
+  while (*tree)
+    {
+      /* Find the correct node to append this section.  */
+      if (compare_section (sec->spec.sorted, section, (*tree)->section) < 0)
+       tree = &((*tree)->left);
+      else
+       tree = &((*tree)->right);
+    }
+
+  return tree;
+}
+
+/* Use wild_sort_fast to build a BST to sort sections.  */
+
+static void
+output_section_callback_fast (lang_wild_statement_type *ptr,
+                             struct wildcard_list *sec,
+                             asection *section,
+                             lang_input_statement_type *file,
+                             void *output ATTRIBUTE_UNUSED)
+{
+  lang_section_bst_type *node;
+  lang_section_bst_type **tree;
+
+  if (unique_section_p (section))
+    return;
+
+  node = xmalloc (sizeof (lang_section_bst_type));
+  node->left = 0;
+  node->right = 0;
+  node->section = section;
+
+  tree = wild_sort_fast (ptr, sec, file, section);
+  if (tree != NULL)
+    *tree = node;
+}
+
+/* Convert a sorted sections' BST back to list form.  */
+
+static void
+output_section_callback_tree_to_list (lang_wild_statement_type *ptr,
+                                     lang_section_bst_type *tree,
+                                     void *output)
+{
+  if (tree->left)
+    output_section_callback_tree_to_list (ptr, tree->left, output);
+
+  lang_add_section (&ptr->children, tree->section,
+                   (lang_output_section_statement_type *) output);
+
+  if (tree->right)
+    output_section_callback_tree_to_list (ptr, tree->right, output);
+
+  free (tree);
+}
+
 /* Specialized, optimized routines for handling different kinds of
    wildcards */
 
@@ -543,6 +708,11 @@ analyze_walk_wild_section_handler (lang_wild_statement_type *ptr)
   int data_counter;
 
   ptr->walk_wild_section_handler = walk_wild_section_general;
+  ptr->handler_data[0] = NULL;
+  ptr->handler_data[1] = NULL;
+  ptr->handler_data[2] = NULL;
+  ptr->handler_data[3] = NULL;
+  ptr->tree = NULL;
 
   /* 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
@@ -655,6 +825,7 @@ static void
 walk_wild (lang_wild_statement_type *s, callback_t callback, void *data)
 {
   const char *file_spec = s->filename;
+  char *p;
 
   if (file_spec == NULL)
     {
@@ -664,11 +835,19 @@ walk_wild (lang_wild_statement_type *s, callback_t callback, void *data)
          walk_wild_file (s, f, callback, data);
        }
     }
+  else if ((p = archive_path (file_spec)) != NULL)
+    {
+      LANG_FOR_EACH_INPUT_STATEMENT (f)
+       {
+         if (input_statement_is_archive_path (file_spec, p, f))
+           walk_wild_file (s, f, callback, data);
+       }
+    }
   else if (wildcardp (file_spec))
     {
       LANG_FOR_EACH_INPUT_STATEMENT (f)
        {
-         if (fnmatch (file_spec, f->filename, FNM_FILE_NAME) == 0)
+         if (fnmatch (file_spec, f->filename, 0) == 0)
            walk_wild_file (s, f, callback, data);
        }
     }
@@ -722,6 +901,7 @@ lang_for_each_statement_worker (void (*func) (lang_statement_union_type *),
        case lang_padding_statement_enum:
        case lang_address_statement_enum:
        case lang_fill_statement_enum:
+       case lang_insert_statement_enum:
          break;
        default:
          FAIL ();
@@ -790,6 +970,14 @@ new_afile (const char *name,
   lang_has_input_file = TRUE;
   p->target = target;
   p->sysrooted = FALSE;
+
+  if (file_type == lang_input_file_is_l_enum
+      && name[0] == ':' && name[1] != '\0')
+    {
+      file_type = lang_input_file_is_search_file_enum;
+      name = name + 1;
+    }
+
   switch (file_type)
     {
     case lang_input_file_is_symbols_only_enum:
@@ -812,7 +1000,7 @@ new_afile (const char *name,
       p->is_archive = TRUE;
       p->filename = name;
       p->real = TRUE;
-      p->local_sym_name = concat ("-l", name, NULL);
+      p->local_sym_name = concat ("-l", name, (const char *) NULL);
       p->just_syms_flag = FALSE;
       p->search_dirs_flag = TRUE;
       break;
@@ -845,10 +1033,8 @@ new_afile (const char *name,
       FAIL ();
     }
   p->the_bfd = NULL;
-  p->asymbols = NULL;
   p->next_real_file = NULL;
   p->next = NULL;
-  p->symbol_count = 0;
   p->dynamic = config.dynamic_link;
   p->add_needed = add_needed;
   p->as_needed = as_needed;
@@ -865,30 +1051,29 @@ lang_add_input_file (const char *name,
                     lang_input_file_enum_type file_type,
                     const char *target)
 {
-  lang_has_input_file = TRUE;
   return new_afile (name, file_type, target, TRUE);
 }
 
-struct output_statement_hash_entry
+struct out_section_hash_entry
 {
   struct bfd_hash_entry root;
-  lang_output_section_statement_type os;
+  lang_statement_union_type s;
 };
 
 /* The hash table.  */
 
-static struct bfd_hash_table output_statement_table;
+static struct bfd_hash_table output_section_statement_table;
 
 /* Support routines for the hash table used by lang_output_section_find,
    initialize the table, fill in an entry and remove the table.  */
 
 static struct bfd_hash_entry *
-output_statement_newfunc (struct bfd_hash_entry *entry, 
-                         struct bfd_hash_table *table,
-                         const char *string)
+output_section_statement_newfunc (struct bfd_hash_entry *entry,
+                                 struct bfd_hash_table *table,
+                                 const char *string)
 {
   lang_output_section_statement_type **nextp;
-  struct output_statement_hash_entry *ret;
+  struct out_section_hash_entry *ret;
 
   if (entry == NULL)
     {
@@ -901,41 +1086,48 @@ output_statement_newfunc (struct bfd_hash_entry *entry,
   if (entry == NULL)
     return entry;
 
-  ret = (struct output_statement_hash_entry *) entry;
-  memset (&ret->os, 0, sizeof (ret->os));
-  ret->os.header.type = lang_output_section_statement_enum;
-  ret->os.subsection_alignment = -1;
-  ret->os.section_alignment = -1;
-  ret->os.block_value = 1;
-  lang_list_init (&ret->os.children);
-  lang_statement_append (stat_ptr,
-                        (lang_statement_union_type *) &ret->os,
-                        &ret->os.header.next);
-
-  ret->os.prev = &((*lang_output_section_statement.tail)
-                  ->output_section_statement);
+  ret = (struct out_section_hash_entry *) entry;
+  memset (&ret->s, 0, sizeof (ret->s));
+  ret->s.header.type = lang_output_section_statement_enum;
+  ret->s.output_section_statement.subsection_alignment = -1;
+  ret->s.output_section_statement.section_alignment = -1;
+  ret->s.output_section_statement.block_value = 1;
+  lang_list_init (&ret->s.output_section_statement.children);
+  lang_statement_append (stat_ptr, &ret->s, &ret->s.header.next);
+
+  /* For every output section statement added to the list, except the
+     first one, lang_output_section_statement.tail points to the "next"
+     field of the last element of the list.  */
+  if (lang_output_section_statement.head != NULL)
+    ret->s.output_section_statement.prev
+      = ((lang_output_section_statement_type *)
+        ((char *) lang_output_section_statement.tail
+         - offsetof (lang_output_section_statement_type, next)));
+
   /* 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 = &ret->os.next;
+  nextp = &ret->s.output_section_statement.next;
   lang_statement_append (&lang_output_section_statement,
-                        (lang_statement_union_type *) &ret->os,
+                        &ret->s,
                         (lang_statement_union_type **) nextp);
   return &ret->root;
 }
 
 static void
-output_statement_table_init (void)
+output_section_statement_table_init (void)
 {
-  if (! bfd_hash_table_init_n (&output_statement_table,
-                              output_statement_newfunc, 61))
+  if (!bfd_hash_table_init_n (&output_section_statement_table,
+                             output_section_statement_newfunc,
+                             sizeof (struct out_section_hash_entry),
+                             61))
     einfo (_("%P%F: can not create hash table: %E\n"));
 }
 
 static void
-output_statement_table_free (void)
+output_section_statement_table_free (void)
 {
-  bfd_hash_table_free (&output_statement_table);
+  bfd_hash_table_free (&output_section_statement_table);
 }
 
 /* Build enough state so that the parser can build its tree.  */
@@ -947,7 +1139,7 @@ lang_init (void)
 
   stat_ptr = &statement_list;
 
-  output_statement_table_init ();
+  output_section_statement_table_init ();
 
   lang_list_init (stat_ptr);
 
@@ -957,7 +1149,7 @@ lang_init (void)
   first_file = lang_add_input_file (NULL, lang_input_file_is_marker_enum,
                                    NULL);
   abs_output_section =
-    lang_output_section_statement_lookup (BFD_ABS_SECTION_NAME);
+    lang_output_section_statement_lookup (BFD_ABS_SECTION_NAME, 0, TRUE);
 
   abs_output_section->bfd_section = bfd_abs_section_ptr;
 
@@ -968,14 +1160,16 @@ lang_init (void)
      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))
+                             lang_definedness_newfunc,
+                             sizeof (struct lang_definedness_hash_entry),
+                             3))
     einfo (_("%P%F: can not create hash table: %E\n"));
 }
 
 void
 lang_finish (void)
 {
-  output_statement_table_free ();
+  output_section_statement_table_free ();
 }
 
 /*----------------------------------------------------------------------
@@ -1024,16 +1218,17 @@ lang_memory_region_lookup (const char *const name, bfd_boolean create)
 
   new->name = xstrdup (name);
   new->next = NULL;
-
-  *lang_memory_region_list_tail = new;
-  lang_memory_region_list_tail = &new->next;
   new->origin = 0;
-  new->flags = 0;
-  new->not_flags = 0;
   new->length = ~(bfd_size_type) 0;
   new->current = 0;
+  new->last_os = NULL;
+  new->flags = 0;
+  new->not_flags = 0;
   new->had_full_message = FALSE;
 
+  *lang_memory_region_list_tail = new;
+  lang_memory_region_list_tail = &new->next;
+
   return new;
 }
 
@@ -1060,66 +1255,51 @@ lang_memory_default (asection *section)
 }
 
 lang_output_section_statement_type *
-lang_output_section_find (const char *const name)
+lang_output_section_statement_lookup (const char *const name,
+                                     int constraint,
+                                     bfd_boolean create)
 {
-  struct output_statement_hash_entry *entry;
-  unsigned long hash;
+  struct out_section_hash_entry *entry;
 
-  entry = ((struct output_statement_hash_entry *)
-          bfd_hash_lookup (&output_statement_table, name, FALSE, FALSE));
+  entry = ((struct out_section_hash_entry *)
+          bfd_hash_lookup (&output_section_statement_table, name,
+                           create, FALSE));
   if (entry == NULL)
-    return NULL;
-
-  hash = entry->root.hash;
-  do
     {
-      if (entry->os.constraint != -1)
-       return &entry->os;
-      entry = (struct output_statement_hash_entry *) entry->root.next;
-    }
-  while (entry != NULL
-        && entry->root.hash == hash
-        && strcmp (name, entry->os.name) == 0);
-
-  return NULL;
-}
-
-static lang_output_section_statement_type *
-lang_output_section_statement_lookup_1 (const char *const name, int constraint)
-{
-  struct output_statement_hash_entry *entry;
-  struct output_statement_hash_entry *last_ent;
-  unsigned long hash;
-
-  entry = ((struct output_statement_hash_entry *)
-          bfd_hash_lookup (&output_statement_table, name, TRUE, FALSE));
-  if (entry == NULL)
-    {
-      einfo (_("%P%F: failed creating section `%s': %E\n"), name);
+      if (create)
+       einfo (_("%P%F: failed creating section `%s': %E\n"), name);
       return NULL;
     }
 
-  if (entry->os.name != NULL)
+  if (entry->s.output_section_statement.name != NULL)
     {
       /* We have a section of this name, but it might not have the correct
         constraint.  */
-      hash = entry->root.hash;
+      struct out_section_hash_entry *last_ent;
+      unsigned long hash = entry->root.hash;
+
       do
        {
-         if (entry->os.constraint != -1
+         if (entry->s.output_section_statement.constraint >= 0
              && (constraint == 0
-                 || (constraint == entry->os.constraint
+                 || (constraint == entry->s.output_section_statement.constraint
                      && constraint != SPECIAL)))
-           return &entry->os;
+           return &entry->s.output_section_statement;
          last_ent = entry;
-         entry = (struct output_statement_hash_entry *) entry->root.next;
+         entry = (struct out_section_hash_entry *) entry->root.next;
        }
       while (entry != NULL
             && entry->root.hash == hash
-            && strcmp (name, entry->os.name) == 0);
+            && strcmp (name, entry->s.output_section_statement.name) == 0);
+
+      if (!create)
+       return NULL;
 
-      entry = ((struct output_statement_hash_entry *)
-              output_statement_newfunc (NULL, &output_statement_table, name));
+      entry
+       = ((struct out_section_hash_entry *)
+          output_section_statement_newfunc (NULL,
+                                            &output_section_statement_table,
+                                            name));
       if (entry == NULL)
        {
          einfo (_("%P%F: failed creating section `%s': %E\n"), name);
@@ -1129,15 +1309,9 @@ lang_output_section_statement_lookup_1 (const char *const name, int constraint)
       last_ent->root.next = &entry->root;
     }
 
-  entry->os.name = name;
-  entry->os.constraint = constraint;
-  return &entry->os;
-}
-
-lang_output_section_statement_type *
-lang_output_section_statement_lookup (const char *const name)
-{
-  return lang_output_section_statement_lookup_1 (name, 0);
+  entry->s.output_section_statement.name = name;
+  entry->s.output_section_statement.constraint = constraint;
+  return &entry->s.output_section_statement;
 }
 
 /* A variant of lang_output_section_find used by place_orphan.
@@ -1147,7 +1321,8 @@ lang_output_section_statement_lookup (const char *const name)
 
 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 **exact,
+                                  lang_match_sec_type_func match_type)
 {
   lang_output_section_statement_type *first, *look, *found;
   flagword flags;
@@ -1165,9 +1340,9 @@ lang_output_section_find_by_flags (const asection *sec,
       if (look->bfd_section != NULL)
        {
          flags = look->bfd_section->flags;
-         if (!bfd_match_sections_by_type (output_bfd,
-                                          look->bfd_section,
-                                          sec->owner, sec))
+         if (match_type && !match_type (link_info.output_bfd,
+                                        look->bfd_section,
+                                        sec->owner, sec))
            continue;
        }
       flags ^= sec->flags;
@@ -1177,7 +1352,8 @@ lang_output_section_find_by_flags (const asection *sec,
     }
   if (found != NULL)
     {
-      *exact = found;
+      if (exact != NULL)
+       *exact = found;
       return found;
     }
 
@@ -1190,9 +1366,9 @@ lang_output_section_find_by_flags (const asection *sec,
          if (look->bfd_section != NULL)
            {
              flags = look->bfd_section->flags;
-             if (!bfd_match_sections_by_type (output_bfd,
-                                              look->bfd_section,
-                                              sec->owner, sec))
+             if (match_type && !match_type (link_info.output_bfd,
+                                            look->bfd_section,
+                                            sec->owner, sec))
                continue;
            }
          flags ^= sec->flags;
@@ -1200,10 +1376,8 @@ lang_output_section_find_by_flags (const asection *sec,
                         | SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
            found = look;
        }
-      return found;
     }
-
-  if (sec->flags & (SEC_READONLY | SEC_THREAD_LOCAL))
+  else if (sec->flags & (SEC_READONLY | SEC_THREAD_LOCAL))
     {
       /* .rodata can go after .text, .sdata2 after .rodata.  */
       for (look = first; look; look = look->next)
@@ -1212,9 +1386,9 @@ lang_output_section_find_by_flags (const asection *sec,
          if (look->bfd_section != NULL)
            {
              flags = look->bfd_section->flags;
-             if (!bfd_match_sections_by_type (output_bfd,
-                                              look->bfd_section,
-                                              sec->owner, sec))
+             if (match_type && !match_type (link_info.output_bfd,
+                                            look->bfd_section,
+                                            sec->owner, sec))
                continue;
            }
          flags ^= sec->flags;
@@ -1223,10 +1397,8 @@ lang_output_section_find_by_flags (const asection *sec,
              && !(look->flags & (SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
            found = look;
        }
-      return found;
     }
-
-  if (sec->flags & SEC_SMALL_DATA)
+  else if (sec->flags & SEC_SMALL_DATA)
     {
       /* .sdata goes after .data, .sbss after .sdata.  */
       for (look = first; look; look = look->next)
@@ -1235,9 +1407,9 @@ lang_output_section_find_by_flags (const asection *sec,
          if (look->bfd_section != NULL)
            {
              flags = look->bfd_section->flags;
-             if (!bfd_match_sections_by_type (output_bfd,
-                                              look->bfd_section,
-                                              sec->owner, sec))
+             if (match_type && !match_type (link_info.output_bfd,
+                                            look->bfd_section,
+                                            sec->owner, sec))
                continue;
            }
          flags ^= sec->flags;
@@ -1247,10 +1419,8 @@ lang_output_section_find_by_flags (const asection *sec,
                  && !(sec->flags & SEC_HAS_CONTENTS)))
            found = look;
        }
-      return found;
     }
-
-  if (sec->flags & SEC_HAS_CONTENTS)
+  else if (sec->flags & SEC_HAS_CONTENTS)
     {
       /* .data goes after .rodata.  */
       for (look = first; look; look = look->next)
@@ -1259,9 +1429,9 @@ lang_output_section_find_by_flags (const asection *sec,
          if (look->bfd_section != NULL)
            {
              flags = look->bfd_section->flags;
-             if (!bfd_match_sections_by_type (output_bfd,
-                                              look->bfd_section,
-                                              sec->owner, sec))
+             if (match_type && !match_type (link_info.output_bfd,
+                                            look->bfd_section,
+                                            sec->owner, sec))
                continue;
            }
          flags ^= sec->flags;
@@ -1269,27 +1439,31 @@ lang_output_section_find_by_flags (const asection *sec,
                         | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
            found = look;
        }
-      return found;
     }
-
-  /* .bss goes last.  */
-  for (look = first; look; look = look->next)
+  else
     {
-      flags = look->flags;
-      if (look->bfd_section != NULL)
+      /* .bss goes last.  */
+      for (look = first; look; look = look->next)
        {
-         flags = look->bfd_section->flags;
-         if (!bfd_match_sections_by_type (output_bfd,
-                                          look->bfd_section,
-                                          sec->owner, sec))
-           continue;
+         flags = look->flags;
+         if (look->bfd_section != NULL)
+           {
+             flags = look->bfd_section->flags;
+             if (match_type && !match_type (link_info.output_bfd,
+                                            look->bfd_section,
+                                            sec->owner, sec))
+               continue;
+           }
+         flags ^= sec->flags;
+         if (!(flags & SEC_ALLOC))
+           found = look;
        }
-      flags ^= sec->flags;
-      if (!(flags & SEC_ALLOC))
-       found = look;
     }
 
-  return found;
+  if (found || !match_type)
+    return found;
+
+  return lang_output_section_find_by_flags (sec, NULL, NULL);
 }
 
 /* Find the last output section before given output statement.
@@ -1302,7 +1476,7 @@ output_prev_sec_find (lang_output_section_statement_type *os)
 
   for (lookup = os->prev; lookup != NULL; lookup = lookup->prev)
     {
-      if (lookup->constraint == -1)
+      if (lookup->constraint < 0)
        continue;
 
       if (lookup->bfd_section != NULL && lookup->bfd_section->owner != NULL)
@@ -1312,6 +1486,73 @@ output_prev_sec_find (lang_output_section_statement_type *os)
   return NULL;
 }
 
+/* Look for a suitable place for a new output section statement.  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.  An exception to this rule is made for
+   the first assignment to dot, otherwise we might put an orphan
+   before . = . + SIZEOF_HEADERS or similar assignments that set the
+   initial address.  */
+
+static lang_statement_union_type **
+insert_os_after (lang_output_section_statement_type *after)
+{
+  lang_statement_union_type **where;
+  lang_statement_union_type **assign = NULL;
+  bfd_boolean ignore_first;
+
+  ignore_first
+    = after == &lang_output_section_statement.head->output_section_statement;
+
+  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
+                 && !ignore_first)
+               assign = where;
+           }
+         ignore_first = FALSE;
+         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;
+         break;
+       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_insert_statement_enum:
+         continue;
+       }
+      break;
+    }
+
+  return where;
+}
+
 lang_output_section_statement_type *
 lang_insert_orphan (asection *s,
                    const char *secname,
@@ -1323,7 +1564,6 @@ lang_insert_orphan (asection *s,
   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;
 
@@ -1354,33 +1594,24 @@ lang_insert_orphan (asection *s,
          etree_type *e_align;
 
          symname = (char *) xmalloc (ps - secname + sizeof "__start_" + 1);
-         symname[0] = bfd_get_symbol_leading_char (output_bfd);
+         symname[0] = bfd_get_symbol_leading_char (link_info.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, ".")));
+         lang_add_assignment (exp_provide (symname,
+                                           exp_nameop (NAME, "."),
+                                           FALSE));
        }
     }
 
   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);
+                                           NULL, 0);
 
   if (add_child == NULL)
     add_child = &os->children;
@@ -1398,10 +1629,11 @@ lang_insert_orphan (asection *s,
        stat_ptr = &add;
 
       symname = (char *) xmalloc (ps - secname + sizeof "__stop_" + 1);
-      symname[0] = bfd_get_symbol_leading_char (output_bfd);
+      symname[0] = bfd_get_symbol_leading_char (link_info.output_bfd);
       sprintf (symname + (symname[0] != 0), "__stop_%s", secname);
-      lang_add_assignment (exp_assop ('=', symname,
-                                     exp_nameop (NAME, ".")));
+      lang_add_assignment (exp_provide (symname,
+                                       exp_nameop (NAME, "."),
+                                       FALSE));
     }
 
   /* Restore the global list pointer.  */
@@ -1434,16 +1666,27 @@ lang_insert_orphan (asection *s,
        }
 
       if (place->section == NULL)
-       place->section = &output_bfd->sections;
+       place->section = &link_info.output_bfd->sections;
 
       as = *place->section;
-      if (as != snew && as->prev != snew)
+
+      if (!as)
        {
+         /* Put the section at the end of the list.  */
+
          /* Unlink the section.  */
-         bfd_section_list_remove (output_bfd, snew);
+         bfd_section_list_remove (link_info.output_bfd, snew);
 
          /* Now tack it back on in the right place.  */
-         bfd_section_list_insert_before (output_bfd, as, snew);
+         bfd_section_list_append (link_info.output_bfd, snew);
+       }
+      else if (as != snew && as->prev != snew)
+       {
+         /* Unlink the section.  */
+         bfd_section_list_remove (link_info.output_bfd, snew);
+
+         /* Now tack it back on in the right place.  */
+         bfd_section_list_insert_before (link_info.output_bfd, as, snew);
        }
 
       /* Save the end of this list.  Further ophans of this type will
@@ -1465,64 +1708,7 @@ lang_insert_orphan (asection *s,
 
          if (place->stmt == NULL)
            {
-             lang_statement_union_type **where;
-             lang_statement_union_type **assign = NULL;
-             bfd_boolean ignore_first;
-
-             /* 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.  An exception to this rule is made for
-                the first assignment to dot, otherwise we might put an
-                orphan before . = . + SIZEOF_HEADERS or similar
-                assignments that set the initial address.  */
-
-             ignore_first = after == (&lang_output_section_statement.head
-                                      ->output_section_statement);
-             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
-                             && !ignore_first)
-                           assign = where;
-                       }
-                     ignore_first = FALSE;
-                     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;
-               }
+             lang_statement_union_type **where = insert_os_after (after);
 
              *add.tail = *where;
              *where = add.head;
@@ -1547,7 +1733,12 @@ lang_insert_orphan (asection *s,
          /* Do the same for the list of output section statements.  */
          newly_added_os = *os_tail;
          *os_tail = NULL;
+         newly_added_os->prev = (lang_output_section_statement_type *)
+           ((char *) place->os_tail
+            - offsetof (lang_output_section_statement_type, next));
          newly_added_os->next = *place->os_tail;
+         if (newly_added_os->next != NULL)
+           newly_added_os->next->prev = newly_added_os;
          *place->os_tail = newly_added_os;
          place->os_tail = &newly_added_os->next;
 
@@ -1587,8 +1778,32 @@ void
 lang_map (void)
 {
   lang_memory_region_type *m;
+  bfd_boolean dis_header_printed = FALSE;
   bfd *p;
 
+  LANG_FOR_EACH_INPUT_STATEMENT (file)
+    {
+      asection *s;
+
+      if ((file->the_bfd->flags & (BFD_LINKER_CREATED | DYNAMIC)) != 0
+         || file->just_syms_flag)
+       continue;
+
+      for (s = file->the_bfd->sections; s != NULL; s = s->next)
+       if ((s->output_section == NULL
+            || s->output_section->owner != link_info.output_bfd)
+           && (s->flags & (SEC_LINKER_CREATED | SEC_KEEP)) == 0)
+         {
+           if (! dis_header_printed)
+             {
+               fprintf (config.map_file, _("\nDiscarded input sections\n\n"));
+               dis_header_printed = TRUE;
+             }
+
+           print_input_section (s);
+         }
+    }
+
   minfo (_("\nMemory Configuration\n\n"));
   fprintf (config.map_file, "%-16s %-18s %-18s %s\n",
           _("Name"), _("Origin"), _("Length"), _("Attributes"));
@@ -1633,21 +1848,21 @@ lang_map (void)
 
   fprintf (config.map_file, _("\nLinker script and memory map\n\n"));
 
-  if (! command_line.reduce_memory_overheads)
+  if (! link_info.reduce_memory_overheads)
     {
       obstack_begin (&map_obstack, 1000);
       for (p = link_info.input_bfds; p != (bfd *) NULL; p = p->link_next)
        bfd_map_over_sections (p, init_map_userdata, 0);
       bfd_link_hash_traverse (link_info.hash, sort_def_symbol, 0);
     }
+  lang_statement_iteration ++;
   print_statements ();
 }
 
 static void
-init_map_userdata (abfd, sec, data)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     asection *sec;
-     void *data ATTRIBUTE_UNUSED;
+init_map_userdata (bfd *abfd ATTRIBUTE_UNUSED,
+                  asection *sec,
+                  void *data ATTRIBUTE_UNUSED)
 {
   fat_section_userdata_type *new_data
     = ((fat_section_userdata_type *) (stat_alloc
@@ -1659,9 +1874,8 @@ init_map_userdata (abfd, sec, data)
 }
 
 static bfd_boolean
-sort_def_symbol (hash_entry, info)
-     struct bfd_link_hash_entry *hash_entry;
-     void *info ATTRIBUTE_UNUSED;
+sort_def_symbol (struct bfd_link_hash_entry *hash_entry,
+                void *info ATTRIBUTE_UNUSED)
 {
   if (hash_entry->type == bfd_link_hash_defined
       || hash_entry->type == bfd_link_hash_defweak)
@@ -1691,7 +1905,8 @@ sort_def_symbol (hash_entry, info)
 /* Initialize an output section.  */
 
 static void
-init_os (lang_output_section_statement_type *s, asection *isec)
+init_os (lang_output_section_statement_type *s, asection *isec,
+        flagword flags)
 {
   if (s->bfd_section != NULL)
     return;
@@ -1699,17 +1914,19 @@ init_os (lang_output_section_statement_type *s, asection *isec)
   if (strcmp (s->name, DISCARD_SECTION_NAME) == 0)
     einfo (_("%P%F: Illegal use of `%s' section\n"), DISCARD_SECTION_NAME);
 
-  s->bfd_section = bfd_get_section_by_name (output_bfd, s->name);
+  s->bfd_section = bfd_get_section_by_name (link_info.output_bfd, s->name);
   if (s->bfd_section == NULL)
-    s->bfd_section = bfd_make_section (output_bfd, s->name);
+    s->bfd_section = bfd_make_section_with_flags (link_info.output_bfd,
+                                                 s->name, flags);
   if (s->bfd_section == NULL)
     {
       einfo (_("%P%F: output format %s cannot represent section called %s\n"),
-            output_bfd->xvec->name, s->name);
+            link_info.output_bfd->xvec->name, s->name);
     }
   s->bfd_section->output_section = s->bfd_section;
   s->bfd_section->output_offset = 0;
-  if (!command_line.reduce_memory_overheads)
+
+  if (!link_info.reduce_memory_overheads)
     {
       fat_section_userdata_type *new
        = stat_alloc (sizeof (fat_section_userdata_type));
@@ -1717,7 +1934,6 @@ init_os (lang_output_section_statement_type *s, asection *isec)
       get_userdata (s->bfd_section) = new;
     }
 
-
   /* If there is a base address, make sure that any sections it might
      mention are initialized.  */
   if (s->addr_tree != NULL)
@@ -1732,7 +1948,7 @@ init_os (lang_output_section_statement_type *s, asection *isec)
 
   if (isec)
     bfd_init_private_section_data (isec->owner, isec,
-                                  output_bfd, s->bfd_section,
+                                  link_info.output_bfd, s->bfd_section,
                                   &link_info);
 }
 
@@ -1779,7 +1995,7 @@ exp_init_os (etree_type *exp)
 
            os = lang_output_section_find (exp->name.name);
            if (os != NULL && os->bfd_section == NULL)
-             init_os (os, NULL);
+             init_os (os, NULL, 0);
          }
        }
       break;
@@ -1803,7 +2019,7 @@ section_already_linked (bfd *abfd, asection *sec, void *data)
     }
 
   if (!(abfd->flags & DYNAMIC))
-    bfd_section_already_linked (abfd, sec);
+    bfd_section_already_linked (abfd, sec, &link_info);
 }
 \f
 /* The wild routines.
@@ -1854,8 +2070,33 @@ lang_add_section (lang_statement_list_type *ptr,
       lang_input_section_type *new;
       flagword flags;
 
+      flags = section->flags;
+
+      /* We don't copy the SEC_NEVER_LOAD flag from an input section
+        to an output section, because we want to be able to include a
+        SEC_NEVER_LOAD section in the middle of an otherwise loaded
+        section (I don't know why we want to do this, but we do).
+        build_link_order in ldwrite.c handles this case by turning
+        the embedded SEC_NEVER_LOAD section into a fill.  */
+
+      flags &= ~ SEC_NEVER_LOAD;
+
+      switch (output->sectype)
+       {
+       case normal_section:
+       case overlay_section:
+         break;
+       case noalloc_section:
+         flags &= ~SEC_ALLOC;
+         break;
+       case noload_section:
+         flags &= ~SEC_LOAD;
+         flags |= SEC_NEVER_LOAD;
+         break;
+       }
+
       if (output->bfd_section == NULL)
-       init_os (output, section);
+       init_os (output, section, flags);
 
       first = ! output->bfd_section->linker_has_input;
       output->bfd_section->linker_has_input = 1;
@@ -1879,17 +2120,6 @@ lang_add_section (lang_statement_list_type *ptr,
       new->section = section;
       section->output_section = output->bfd_section;
 
-      flags = section->flags;
-
-      /* We don't copy the SEC_NEVER_LOAD flag from an input section
-        to an output section, because we want to be able to include a
-        SEC_NEVER_LOAD section in the middle of an otherwise loaded
-        section (I don't know why we want to do this, but we do).
-        build_link_order in ldwrite.c handles this case by turning
-        the embedded SEC_NEVER_LOAD section into a fill.  */
-
-      flags &= ~ SEC_NEVER_LOAD;
-
       /* If final link, don't copy the SEC_LINK_ONCE flags, they've
         already been processed.  One reason to do this is that on pe
         format targets, .text$foo sections go into .text and it's odd
@@ -1926,22 +2156,6 @@ lang_add_section (lang_statement_list_type *ptr,
       if ((section->flags & SEC_READONLY) == 0)
        output->bfd_section->flags &= ~SEC_READONLY;
 
-      switch (output->sectype)
-       {
-       case normal_section:
-         break;
-       case dsect_section:
-       case copy_section:
-       case info_section:
-       case overlay_section:
-         output->bfd_section->flags &= ~SEC_ALLOC;
-         break;
-       case noload_section:
-         output->bfd_section->flags &= ~SEC_LOAD;
-         output->bfd_section->flags |= SEC_NEVER_LOAD;
-         break;
-       }
-
       /* Copy over SEC_SMALL_DATA.  */
       if (section->flags & SEC_SMALL_DATA)
        output->bfd_section->flags |= SEC_SMALL_DATA;
@@ -1959,46 +2173,6 @@ lang_add_section (lang_statement_list_type *ptr,
     }
 }
 
-/* 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
@@ -2181,12 +2355,9 @@ lookup_name (const char *name)
       /* Use the local_sym_name as the name of the file that has
         already been loaded as filename might have been transformed
         via the search directory lookup mechanism.  */
-      const char * filename = search->local_sym_name;
+      const char *filename = search->local_sym_name;
 
-      if (filename == NULL && name == NULL)
-       return search;
       if (filename != NULL
-         && name != NULL
          && strcmp (filename, name) == 0)
        break;
     }
@@ -2196,11 +2367,8 @@ lookup_name (const char *name)
                        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
-     (FIXME: can that ever actually happen?) don't add this file.  */
-  if (search->loaded
-      || ! search->real
-      || search->filename == NULL)
+     don't add this file.  */
+  if (search->loaded || !search->real)
     return search;
 
   if (! load_symbols (search, NULL))
@@ -2236,7 +2404,7 @@ add_excluded_libs (const char *list)
       entry->name[end - p] = '\0';
       excluded_libs = entry;
       if (*end == '\0')
-        break;
+       break;
       p = end + 1;
     }
 }
@@ -2272,7 +2440,7 @@ check_excluded_libs (bfd *abfd)
 
 /* Get the symbols for an input file.  */
 
-static bfd_boolean
+bfd_boolean
 load_symbols (lang_input_statement_type *entry,
              lang_statement_list_type *place)
 {
@@ -2290,6 +2458,7 @@ load_symbols (lang_input_statement_type *entry,
       lang_statement_list_type *hold;
       bfd_boolean bad_load = TRUE;
       bfd_boolean save_ldlang_sysrooted_script;
+      bfd_boolean save_as_needed, save_add_needed;
 
       err = bfd_get_error ();
 
@@ -2309,7 +2478,7 @@ load_symbols (lang_input_statement_type *entry,
        }
       else if (err != bfd_error_file_not_recognized
               || place == NULL)
-         einfo (_("%F%B: file not recognized: %E\n"), entry->the_bfd);
+       einfo (_("%F%B: file not recognized: %E\n"), entry->the_bfd);
       else
        bad_load = FALSE;
 
@@ -2323,6 +2492,10 @@ load_symbols (lang_input_statement_type *entry,
       stat_ptr = place;
       save_ldlang_sysrooted_script = ldlang_sysrooted_script;
       ldlang_sysrooted_script = entry->sysrooted;
+      save_as_needed = as_needed;
+      as_needed = entry->as_needed;
+      save_add_needed = add_needed;
+      add_needed = entry->add_needed;
 
       ldfile_assumed_script = TRUE;
       parser_input = input_script;
@@ -2333,6 +2506,8 @@ load_symbols (lang_input_statement_type *entry,
       ldfile_assumed_script = FALSE;
 
       ldlang_sysrooted_script = save_ldlang_sysrooted_script;
+      as_needed = save_as_needed;
+      add_needed = save_add_needed;
       stat_ptr = hold;
 
       return ! bad_load;
@@ -2416,19 +2591,33 @@ wild (lang_wild_statement_type *s,
 {
   struct wildcard_list *sec;
 
-  walk_wild (s, output_section_callback, output);
-
-  for (sec = s->section_list; sec != NULL; sec = sec->next)
+  if (s->handler_data[0]
+      && s->handler_data[0]->spec.sorted == by_name
+      && !s->filenames_sorted)
     {
-      if (default_common_section != NULL)
-       break;
+      lang_section_bst_type *tree;
+
+      walk_wild (s, output_section_callback_fast, output);
+
+      tree = s->tree;
+      if (tree)
+       {
+         output_section_callback_tree_to_list (s, tree, output);
+         s->tree = NULL;
+       }
+    }
+  else
+    walk_wild (s, output_section_callback, output);
+
+  if (default_common_section == NULL)
+    for (sec = s->section_list; sec != NULL; sec = sec->next)
       if (sec->spec.name != NULL && strcmp (sec->spec.name, "COMMON") == 0)
        {
          /* Remember the section that common is going to in case we
             later get something which doesn't know where to put it.  */
          default_common_section = output;
+         break;
        }
-    }
 }
 
 /* Return TRUE iff target is the sought target.  */
@@ -2536,6 +2725,13 @@ closest_target_match (const bfd_target *target, void *data)
   if (target->flavour != original->flavour)
     return 0;
 
+  /* Ignore generic big and little endian elf vectors.  */
+  if (strcmp (target->name, "elf32-big") == 0
+      || strcmp (target->name, "elf64-big") == 0
+      || strcmp (target->name, "elf32-little") == 0
+      || strcmp (target->name, "elf64-little") == 0)
+    return 0;
+
   /* If we have not found a potential winner yet, then record this one.  */
   if (winner == NULL)
     {
@@ -2606,11 +2802,9 @@ lang_get_output_target (void)
 
 /* Open the output file.  */
 
-static bfd *
+static void
 open_output (const char *name)
 {
-  bfd *output;
-
   output_target = lang_get_output_target ();
 
   /* Has the user requested a particular endianness on the command
@@ -2662,9 +2856,9 @@ open_output (const char *name)
        }
     }
 
-  output = bfd_openw (name, output_target);
+  link_info.output_bfd = bfd_openw (name, output_target);
 
-  if (output == NULL)
+  if (link_info.output_bfd == NULL)
     {
       if (bfd_get_error () == bfd_error_invalid_target)
        einfo (_("%P%F: target %s not found\n"), output_target);
@@ -2674,19 +2868,18 @@ open_output (const char *name)
 
   delete_output_file_on_failure = TRUE;
 
-  if (! bfd_set_format (output, bfd_object))
+  if (! bfd_set_format (link_info.output_bfd, bfd_object))
     einfo (_("%P%F:%s: can not make object file: %E\n"), name);
-  if (! bfd_set_arch_mach (output,
+  if (! bfd_set_arch_mach (link_info.output_bfd,
                           ldfile_output_architecture,
                           ldfile_output_machine))
     einfo (_("%P%F:%s: can not set architecture: %E\n"), name);
 
-  link_info.hash = bfd_link_hash_table_create (output);
+  link_info.hash = bfd_link_hash_table_create (link_info.output_bfd);
   if (link_info.hash == NULL)
     einfo (_("%P%F: can not create hash table: %E\n"));
 
-  bfd_set_gp_size (output, g_switch_value);
-  return output;
+  bfd_set_gp_size (link_info.output_bfd, g_switch_value);
 }
 
 static void
@@ -2695,21 +2888,21 @@ ldlang_open_output (lang_statement_union_type *statement)
   switch (statement->header.type)
     {
     case lang_output_statement_enum:
-      ASSERT (output_bfd == NULL);
-      output_bfd = open_output (statement->output_statement.name);
+      ASSERT (link_info.output_bfd == NULL);
+      open_output (statement->output_statement.name);
       ldemul_set_output_arch ();
       if (config.magic_demand_paged && !link_info.relocatable)
-       output_bfd->flags |= D_PAGED;
+       link_info.output_bfd->flags |= D_PAGED;
       else
-       output_bfd->flags &= ~D_PAGED;
+       link_info.output_bfd->flags &= ~D_PAGED;
       if (config.text_read_only)
-       output_bfd->flags |= WP_TEXT;
+       link_info.output_bfd->flags |= WP_TEXT;
       else
-       output_bfd->flags &= ~WP_TEXT;
+       link_info.output_bfd->flags &= ~WP_TEXT;
       if (link_info.traditional_format)
-       output_bfd->flags |= BFD_TRADITIONAL_FORMAT;
+       link_info.output_bfd->flags |= BFD_TRADITIONAL_FORMAT;
       else
-       output_bfd->flags &= ~BFD_TRADITIONAL_FORMAT;
+       link_info.output_bfd->flags &= ~BFD_TRADITIONAL_FORMAT;
       break;
 
     case lang_target_statement_enum:
@@ -2762,7 +2955,8 @@ open_input_bfds (lang_statement_union_type *s, bfd_boolean force)
        case lang_wild_statement_enum:
          /* Maybe we should load the file's symbols.  */
          if (s->wild_statement.filename
-             && ! wildcardp (s->wild_statement.filename))
+             && !wildcardp (s->wild_statement.filename)
+             && !archive_path (s->wild_statement.filename))
            lookup_name (s->wild_statement.filename);
          open_input_bfds (s->wild_statement.children.head, force);
          break;
@@ -2921,7 +3115,7 @@ ldlang_add_undef (const char *const name)
 
   new->name = xstrdup (name);
 
-  if (output_bfd != NULL)
+  if (link_info.output_bfd != NULL)
     insert_undefined (new->name);
 }
 
@@ -2966,28 +3160,28 @@ check_input_sections
   for (; s != (lang_statement_union_type *) NULL; s = s->header.next)
     {
       switch (s->header.type)
-      {
-      case lang_wild_statement_enum:
-       walk_wild (&s->wild_statement, check_section_callback,
-                  output_section_statement);
-       if (! output_section_statement->all_input_readonly)
-         return;
-       break;
-      case lang_constructors_statement_enum:
-       check_input_sections (constructor_list.head,
-                             output_section_statement);
-       if (! output_section_statement->all_input_readonly)
-         return;
-       break;
-      case lang_group_statement_enum:
-       check_input_sections (s->group_statement.children.head,
-                             output_section_statement);
-       if (! output_section_statement->all_input_readonly)
-         return;
-       break;
-      default:
-       break;
-      }
+       {
+       case lang_wild_statement_enum:
+         walk_wild (&s->wild_statement, check_section_callback,
+                    output_section_statement);
+         if (! output_section_statement->all_input_readonly)
+           return;
+         break;
+       case lang_constructors_statement_enum:
+         check_input_sections (constructor_list.head,
+                               output_section_statement);
+         if (! output_section_statement->all_input_readonly)
+           return;
+         break;
+       case lang_group_statement_enum:
+         check_input_sections (s->group_statement.children.head,
+                               output_section_statement);
+         if (! output_section_statement->all_input_readonly)
+           return;
+         break;
+       default:
+         break;
+       }
     }
 }
 
@@ -3017,7 +3211,8 @@ update_wild_statements (lang_statement_union_type *s)
 
            case lang_wild_statement_enum:
              sec = s->wild_statement.section_list;
-             if (sec != NULL)
+             for (sec = s->wild_statement.section_list; sec != NULL;
+                  sec = sec->next)
                {
                  switch (sec->spec.sorted)
                    {
@@ -3063,6 +3258,8 @@ map_input_to_output_sections
   (lang_statement_union_type *s, const char *target,
    lang_output_section_statement_type *os)
 {
+  flagword flags;
+
   for (; s != NULL; s = s->header.next)
     {
       switch (s->header.type)
@@ -3112,37 +3309,36 @@ map_input_to_output_sections
          /* Make sure that any sections mentioned in the expression
             are initialized.  */
          exp_init_os (s->data_statement.exp);
-         if (os != NULL && os->bfd_section == NULL)
-           init_os (os, NULL);
+         flags = SEC_HAS_CONTENTS;
          /* 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;
+           flags |= SEC_ALLOC | SEC_LOAD;
+         if (os->bfd_section == NULL)
+           init_os (os, NULL, flags);
+         else
+           os->bfd_section->flags |= flags;
          break;
-       case lang_fill_statement_enum:
        case lang_input_section_enum:
+         break;
+       case lang_fill_statement_enum:
        case lang_object_symbols_statement_enum:
        case lang_reloc_statement_enum:
        case lang_padding_statement_enum:
        case lang_input_statement_enum:
          if (os != NULL && os->bfd_section == NULL)
-           init_os (os, NULL);
+           init_os (os, NULL, 0);
          break;
        case lang_assignment_statement_enum:
          if (os != NULL && os->bfd_section == NULL)
-           init_os (os, NULL);
+           init_os (os, NULL, 0);
 
          /* Make sure that any sections mentioned in the assignment
             are initialized.  */
          exp_init_os (s->assignment_statement.exp);
          break;
-       case lang_afile_asection_pair_statement_enum:
-         FAIL ();
-         break;
        case lang_address_statement_enum:
-         /* Mark the specified section with the supplied 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
@@ -3151,20 +3347,181 @@ map_input_to_output_sections
             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 
+         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));
-             
+                  (s->address_statement.section_name, 0, TRUE));
+
              if (aos->bfd_section == NULL)
-               init_os (aos, NULL);
+               init_os (aos, NULL, 0);
              aos->addr_tree = s->address_statement.address;
            }
          break;
+       case lang_insert_statement_enum:
+         break;
+       }
+    }
+}
+
+/* An insert statement snips out all the linker statements from the
+   start of the list and places them after the output section
+   statement specified by the insert.  This operation is complicated
+   by the fact that we keep a doubly linked list of output section
+   statements as well as the singly linked list of all statements.  */
+
+static void
+process_insert_statements (void)
+{
+  lang_statement_union_type **s;
+  lang_output_section_statement_type *first_os = NULL;
+  lang_output_section_statement_type *last_os = NULL;
+  lang_output_section_statement_type *os;
+
+  /* "start of list" is actually the statement immediately after
+     the special abs_section output statement, so that it isn't
+     reordered.  */
+  s = &lang_output_section_statement.head;
+  while (*(s = &(*s)->header.next) != NULL)
+    {
+      if ((*s)->header.type == lang_output_section_statement_enum)
+       {
+         /* Keep pointers to the first and last output section
+            statement in the sequence we may be about to move.  */
+         last_os = &(*s)->output_section_statement;
+
+         /* Set constraint negative so that lang_output_section_find
+            won't match this output section statement.  At this
+            stage in linking constraint has values in the range
+            [-1, ONLY_IN_RW].  */
+         last_os->constraint = -2 - last_os->constraint;
+         if (first_os == NULL)
+           first_os = last_os;
+       }
+      else if ((*s)->header.type == lang_insert_statement_enum)
+       {
+         lang_insert_statement_type *i = &(*s)->insert_statement;
+         lang_output_section_statement_type *where;
+         lang_statement_union_type **ptr;
+         lang_statement_union_type *first;
+
+         where = lang_output_section_find (i->where);
+         if (where != NULL && i->is_before)
+           {
+             do
+               where = where->prev;
+             while (where != NULL && where->constraint < 0);
+           }
+         if (where == NULL)
+           {
+             einfo (_("%F%P: %s not found for insert\n"), i->where);
+             return;
+           }
+
+         /* Deal with reordering the output section statement list.  */
+         if (last_os != NULL)
+           {
+             asection *first_sec, *last_sec;
+             struct lang_output_section_statement_struct **next;
+
+             /* Snip out the output sections we are moving.  */
+             first_os->prev->next = last_os->next;
+             if (last_os->next == NULL)
+               {
+                 next = &first_os->prev->next;
+                 lang_output_section_statement.tail
+                   = (lang_statement_union_type **) next;
+               }
+             else
+               last_os->next->prev = first_os->prev;
+             /* Add them in at the new position.  */
+             last_os->next = where->next;
+             if (where->next == NULL)
+               {
+                 next = &last_os->next;
+                 lang_output_section_statement.tail
+                   = (lang_statement_union_type **) next;
+               }
+             else
+               where->next->prev = last_os;
+             first_os->prev = where;
+             where->next = first_os;
+
+             /* Move the bfd sections in the same way.  */
+             first_sec = NULL;
+             last_sec = NULL;
+             for (os = first_os; os != NULL; os = os->next)
+               {
+                 os->constraint = -2 - os->constraint;
+                 if (os->bfd_section != NULL
+                     && os->bfd_section->owner != NULL)
+                   {
+                     last_sec = os->bfd_section;
+                     if (first_sec == NULL)
+                       first_sec = last_sec;
+                   }
+                 if (os == last_os)
+                   break;
+               }
+             if (last_sec != NULL)
+               {
+                 asection *sec = where->bfd_section;
+                 if (sec == NULL)
+                   sec = output_prev_sec_find (where);
+
+                 /* The place we want to insert must come after the
+                    sections we are moving.  So if we find no
+                    section or if the section is the same as our
+                    last section, then no move is needed.  */
+                 if (sec != NULL && sec != last_sec)
+                   {
+                     /* Trim them off.  */
+                     if (first_sec->prev != NULL)
+                       first_sec->prev->next = last_sec->next;
+                     else
+                       link_info.output_bfd->sections = last_sec->next;
+                     if (last_sec->next != NULL)
+                       last_sec->next->prev = first_sec->prev;
+                     else
+                       link_info.output_bfd->section_last = first_sec->prev;
+                     /* Add back.  */
+                     last_sec->next = sec->next;
+                     if (sec->next != NULL)
+                       sec->next->prev = last_sec;
+                     else
+                       link_info.output_bfd->section_last = last_sec;
+                     first_sec->prev = sec;
+                     sec->next = first_sec;
+                   }
+               }
+
+             first_os = NULL;
+             last_os = NULL;
+           }
+
+         ptr = insert_os_after (where);
+         /* Snip everything after the abs_section output statement we
+            know is at the start of the list, up to and including
+            the insert statement we are currently processing.  */
+         first = lang_output_section_statement.head->header.next;
+         lang_output_section_statement.head->header.next = (*s)->header.next;
+         /* Add them back where they belong.  */
+         *s = *ptr;
+         if (*s == NULL)
+           statement_list.tail = s;
+         *ptr = first;
+         s = &lang_output_section_statement.head;
        }
     }
+
+  /* Undo constraint twiddling.  */
+  for (os = first_os; os != NULL; os = os->next)
+    {
+      os->constraint = -2 - os->constraint;
+      if (os == last_os)
+       break;
+    }
 }
 
 /* An output section might have been removed after its statement was
@@ -3192,7 +3549,7 @@ strip_excluded_output_sections (void)
       asection *output_section;
       bfd_boolean exclude;
 
-      if (os->constraint == -1)
+      if (os->constraint < 0)
        continue;
 
       output_section = os->bfd_section;
@@ -3201,7 +3558,7 @@ strip_excluded_output_sections (void)
 
       exclude = (output_section->rawsize == 0
                 && (output_section->flags & SEC_KEEP) == 0
-                && !bfd_section_removed_from_list (output_bfd,
+                && !bfd_section_removed_from_list (link_info.output_bfd,
                                                    output_section));
 
       /* Some sections have not yet been sized, notably .gnu.version,
@@ -3229,10 +3586,12 @@ strip_excluded_output_sections (void)
        {
          /* We don't set bfd_section to NULL since bfd_section of the
             removed output section statement may still be used.  */
-         os->ignored = TRUE;
+         if (!os->section_relative_symbol
+             && !os->update_dot_tree)
+           os->ignored = TRUE;
          output_section->flags |= SEC_EXCLUDE;
-         bfd_section_list_remove (output_bfd, output_section);
-         output_bfd->section_count--;
+         bfd_section_list_remove (link_info.output_bfd, output_section);
+         link_info.output_bfd->section_count--;
        }
     }
 
@@ -3270,14 +3629,12 @@ print_output_section_statement
 
          minfo ("0x%V %W", section->vma, section->size);
 
-         if (output_section_statement->load_base != NULL)
-           {
-             bfd_vma addr;
+         if (section->vma != section->lma)
+           minfo (_(" load address 0x%V"), section->lma);
 
-             addr = exp_get_abs_int (output_section_statement->load_base, 0,
-                                     "load base");
-             minfo (_(" load address 0x%V"), addr);
-           }
+         if (output_section_statement->update_dot_tree != NULL)
+           exp_fold_tree (output_section_statement->update_dot_tree,
+                          bfd_abs_section_ptr, &print_dot);
        }
 
       print_nl ();
@@ -3292,7 +3649,7 @@ print_output_section_statement
    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)
 {
@@ -3391,7 +3748,7 @@ print_assignment (lang_assignment_statement_type *assignment,
              value = h->u.def.value;
 
              if (expld.result.section)
-             value += expld.result.section->vma;
+               value += expld.result.section->vma;
 
              minfo ("[0x%V]", value);
            }
@@ -3415,10 +3772,10 @@ print_assignment (lang_assignment_statement_type *assignment,
 static void
 print_input_statement (lang_input_statement_type *statm)
 {
-  if (statm->filename != NULL)
-    {
-      fprintf (config.map_file, "LOAD %s\n", statm->filename);
-    }
+  if (statm->filename != NULL
+      && (statm->the_bfd == NULL
+         || (statm->the_bfd->flags & BFD_LINKER_CREATED) == 0))
+    fprintf (config.map_file, "LOAD %s\n", statm->filename);
 }
 
 /* Print all symbols defined in a particular section.  This is called
@@ -3449,8 +3806,7 @@ print_one_symbol (struct bfd_link_hash_entry *hash_entry, void *ptr)
 }
 
 static void
-print_all_symbols (sec)
-     asection *sec;
+print_all_symbols (asection *sec)
 {
   struct fat_user_section_struct *ud = get_userdata (sec);
   struct map_symbol_def *def;
@@ -3466,68 +3822,70 @@ print_all_symbols (sec)
 /* Print information about an input section to the map file.  */
 
 static void
-print_input_section (lang_input_section_type *in)
+print_input_section (asection *i)
 {
-  asection *i = in->section;
   bfd_size_type size = i->size;
+  int len;
+  bfd_vma addr;
 
   init_opb ();
-  if (size != 0)
-    {
-      int len;
-      bfd_vma addr;
 
-      print_space ();
-      minfo ("%s", i->name);
+  print_space ();
+  minfo ("%s", i->name);
 
-      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;
-       }
+  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;
-       }
+  if (i->output_section != NULL
+      && i->output_section->owner == link_info.output_bfd)
+    addr = i->output_section->vma + i->output_offset;
+  else
+    {
+      addr = print_dot;
+      size = 0;
+    }
 
-      minfo ("0x%V %W %B\n", addr, TO_ADDR (size), i->owner);
+  minfo ("0x%V %W %B\n", addr, TO_ADDR (size), i->owner);
 
-      if (size != i->rawsize && i->rawsize != 0)
-       {
-         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->rawsize);
+      while (len > 0)
+       {
+         print_space ();
+         --len;
        }
 
-      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);
+      minfo (_("%W (size before relaxing)\n"), i->rawsize);
+    }
 
-         print_dot = addr + TO_ADDR (size);
-       }
+  if (i->output_section != NULL
+      && i->output_section->owner == link_info.output_bfd)
+    {
+      if (link_info.reduce_memory_overheads)
+       bfd_link_hash_traverse (link_info.hash, print_one_symbol, i);
+      else
+       print_all_symbols (i);
+
+      /* Update print_dot, but make sure that we do not move it
+        backwards - this could happen if we have overlays and a
+        later overlay is shorter than an earier one.  */
+      if (addr + TO_ADDR (size) > print_dot)
+       print_dot = addr + TO_ADDR (size);
     }
 }
 
@@ -3788,7 +4146,7 @@ print_statement (lang_statement_union_type *s,
       print_reloc_statement (&s->reloc_statement);
       break;
     case lang_input_section_enum:
-      print_input_section (&s->input_section);
+      print_input_section (s->input_section.section);
       break;
     case lang_padding_statement_enum:
       print_padding_statement (&s->padding_statement);
@@ -3814,8 +4172,10 @@ print_statement (lang_statement_union_type *s,
     case lang_group_statement_enum:
       print_group (&s->group_statement, os);
       break;
-    case lang_afile_asection_pair_statement_enum:
-      FAIL ();
+    case lang_insert_statement_enum:
+      minfo ("INSERT %s %s\n",
+            s->insert_statement.is_before ? "BEFORE" : "AFTER",
+            s->insert_statement.where);
       break;
     }
 }
@@ -3872,8 +4232,8 @@ insert_pad (lang_statement_union_type **ptr,
       /* Use the existing pad statement.  */
     }
   else if ((pad = *ptr) != NULL
-      && pad->header.type == lang_padding_statement_enum
-      && pad->padding_statement.output_section == output_section)
+          && pad->header.type == lang_padding_statement_enum
+          && pad->padding_statement.output_section == output_section)
     {
       /* Use the existing pad statement.  */
     }
@@ -3960,6 +4320,10 @@ sort_sections_by_lma (const void *arg1, const void *arg2)
   else if (bfd_section_lma (sec1->owner, sec1)
           > bfd_section_lma (sec2->owner, sec2))
     return 1;
+  else if (sec1->id < sec2->id)
+    return -1;
+  else if (sec1->id > sec2->id)
+    return 1;
 
   return 0;
 }
@@ -3972,7 +4336,8 @@ sort_sections_by_lma (const void *arg1, const void *arg2)
 
 /* Check to see if any allocated sections overlap with other allocated
    sections.  This can happen if a linker script specifies the output
-   section addresses of the two sections.  */
+   section addresses of the two sections.  Also check whether any memory
+   region has overflowed.  */
 
 static void
 lang_check_section_addresses (void)
@@ -3985,16 +4350,17 @@ lang_check_section_addresses (void)
   bfd_vma os_start;
   bfd_vma os_end;
   bfd_size_type amt;
+  lang_memory_region_type *m;
 
-  if (bfd_count_sections (output_bfd) <= 1)
+  if (bfd_count_sections (link_info.output_bfd) <= 1)
     return;
 
-  amt = bfd_count_sections (output_bfd) * sizeof (asection *);
+  amt = bfd_count_sections (link_info.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)
+  for (s = link_info.output_bfd->sections; s != NULL; s = s->next)
     {
       /* Only consider loadable sections with real contents.  */
       if (IGNORE_SECTION (s) || s->size == 0)
@@ -4003,7 +4369,7 @@ lang_check_section_addresses (void)
       sections[count] = s;
       count++;
     }
-  
+
   if (count <= 1)
     return;
 
@@ -4012,7 +4378,7 @@ lang_check_section_addresses (void)
 
   spp = sections;
   s = *spp++;
-  s_start = bfd_section_lma (output_bfd, s);
+  s_start = bfd_section_lma (link_info.output_bfd, s);
   s_end = s_start + TO_ADDR (s->size) - 1;
   for (count--; count; count--)
     {
@@ -4020,10 +4386,10 @@ lang_check_section_addresses (void)
         addresses because overlay sections can have overlapping VMAs
         but they must have distinct LMAs.  */
       os = s;
-      os_start = s_start; 
+      os_start = s_start;
       os_end = s_end;
       s = *spp++;
-      s_start = bfd_section_lma (output_bfd, s);
+      s_start = bfd_section_lma (link_info.output_bfd, s);
       s_end = s_start + TO_ADDR (s->size) - 1;
 
       /* Look for an overlap.  */
@@ -4033,6 +4399,20 @@ lang_check_section_addresses (void)
     }
 
   free (sections);
+
+  /* If any memory region has overflowed, report by how much.
+     We do not issue this diagnostic for regions that had sections
+     explicitly placed outside their bounds; os_region_check's
+     diagnostics are adequate for that case.
+
+     FIXME: It is conceivable that m->current - (m->origin + m->length)
+     might overflow a 32-bit integer.  There is, alas, no way to print
+     a bfd_vma quantity in decimal.  */
+  for (m = lang_memory_region_list; m; m = m->next)
+    if (m->had_full_message)
+      einfo (_("%X%P: region %s overflowed by %ld bytes\n"),
+            m->name, (long)(m->current - (m->origin + m->length)));
+
 }
 
 /* Make sure the new address is within the region.  We explicitly permit the
@@ -4060,15 +4440,15 @@ os_region_check (lang_output_section_statement_type *os,
                 os->bfd_section->name,
                 region->name);
        }
-      else
+      else if (!region->had_full_message)
        {
-         einfo (_("%X%P: region %s is full (%B section %s)\n"),
-                region->name,
+         region->had_full_message = TRUE;
+
+         einfo (_("%X%P: %B section %s will not fit in region %s\n"),
                 os->bfd_section->owner,
-                os->bfd_section->name);
+                os->bfd_section->name,
+                region->name);
        }
-      /* Reset the region pointer.  */
-      region->current = region->origin;
     }
 }
 
@@ -4093,20 +4473,20 @@ lang_size_sections_1
          {
            bfd_vma newdot, after;
            lang_output_section_statement_type *os;
+           lang_memory_region_type *r;
 
            os = &s->output_section_statement;
            if (os->addr_tree != NULL)
              {
-               os->processed = FALSE;
+               os->processed_vma = FALSE;
                exp_fold_tree (os->addr_tree, bfd_abs_section_ptr, &dot);
 
-               if (!expld.result.valid_p
-                   && expld.phase != lang_mark_phase_enum)
+               if (expld.result.valid_p)
+                 dot = expld.result.value + expld.result.section->vma;
+               else if (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)
@@ -4117,8 +4497,10 @@ lang_size_sections_1
               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 ((bfd_get_flavour (output_bfd) == bfd_target_ecoff_flavour
-                || bfd_get_flavour (output_bfd) == bfd_target_coff_flavour)
+           if (((bfd_get_flavour (link_info.output_bfd)
+                 == bfd_target_ecoff_flavour)
+                || (bfd_get_flavour (link_info.output_bfd)
+                    == bfd_target_coff_flavour))
                && (os->bfd_section->flags & SEC_COFF_SHARED_LIBRARY) != 0)
              {
                asection *input;
@@ -4164,7 +4546,8 @@ 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 (os->bfd_section)
+                   if (!os->ignored
+                       && !IGNORE_SECTION (os->bfd_section)
                        && ! link_info.relocatable
                        && check_regions
                        && strcmp (os->region->name,
@@ -4187,12 +4570,12 @@ lang_size_sections_1
                        if (command_line.check_section_addresses)
                          einfo (_("%P%F: error: no memory region specified"
                                   " for loadable section `%s'\n"),
-                                bfd_get_section_name (output_bfd,
+                                bfd_get_section_name (link_info.output_bfd,
                                                       os->bfd_section));
                        else
                          einfo (_("%P: warning: no memory region specified"
                                   " for loadable section `%s'\n"),
-                                bfd_get_section_name (output_bfd,
+                                bfd_get_section_name (link_info.output_bfd,
                                                       os->bfd_section));
                      }
 
@@ -4225,24 +4608,115 @@ lang_size_sections_1
            lang_size_sections_1 (os->children.head, os, &os->children.head,
                                  os->fill, newdot, relax, check_regions);
 
-           os->processed = TRUE;
+           os->processed_vma = TRUE;
 
            if (bfd_is_abs_section (os->bfd_section) || os->ignored)
+             /* Except for some special linker created sections,
+                no output section should change from zero size
+                after strip_excluded_output_sections.  A non-zero
+                size on an ignored section indicates that some
+                input section was not sized early enough.  */
+             ASSERT (os->bfd_section->size == 0);
+           else
              {
-               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 = ((dot
+                         + TO_ADDR (os->bfd_section->size)
+                         + os->block_value - 1)
+                        & - (bfd_vma) os->block_value);
+
+               os->bfd_section->size = TO_SIZE (after - os->bfd_section->vma);
+             }
+
+           /* Set section lma.  */
+           r = os->region;
+           if (r == NULL)
+             r = lang_memory_region_lookup (DEFAULT_MEMORY_REGION, FALSE);
+
+           if (os->load_base)
+             {
+               bfd_vma lma = exp_get_abs_int (os->load_base, 0, "load base");
+               os->bfd_section->lma = lma;
              }
+           else if (os->lma_region != NULL)
+             {
+               bfd_vma lma = os->lma_region->current;
 
-           dot = os->bfd_section->vma;
+               if (os->section_alignment != -1)
+                 lma = align_power (lma, os->section_alignment);
+               os->bfd_section->lma = lma;
+             }
+           else if (r->last_os != NULL
+                    && (os->bfd_section->flags & SEC_ALLOC) != 0)
+             {
+               bfd_vma lma;
+               asection *last;
+
+               last = r->last_os->output_section_statement.bfd_section;
+
+               /* A backwards move of dot should be accompanied by
+                  an explicit assignment to the section LMA (ie.
+                  os->load_base set) because backwards moves can
+                  create overlapping LMAs.  */
+               if (dot < last->vma
+                   && os->bfd_section->size != 0
+                   && dot + os->bfd_section->size <= last->vma)
+                 {
+                   /* If dot moved backwards then leave lma equal to
+                      vma.  This is the old default lma, which might
+                      just happen to work when the backwards move is
+                      sufficiently large.  Nag if this changes anything,
+                      so people can fix their linker scripts.  */
+
+                   if (last->vma != last->lma)
+                     einfo (_("%P: warning: dot moved backwards before `%s'\n"),
+                            os->name);
+                 }
+               else
+                 {
+                   /* If this is an overlay, set the current lma to that
+                      at the end of the previous section.  */
+                   if (os->sectype == overlay_section)
+                     lma = last->lma + last->size;
+
+                   /* Otherwise, keep the same lma to vma relationship
+                      as the previous section.  */
+                   else
+                     lma = dot + last->lma - last->vma;
+
+                   if (os->section_alignment != -1)
+                     lma = align_power (lma, os->section_alignment);
+                   os->bfd_section->lma = lma;
+                 }
+             }
+           os->processed_lma = TRUE;
 
-           /* Put the section within the requested block size, or
-              align at the block boundary.  */
-           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) || os->ignored)
+             break;
 
-           os->bfd_section->size = TO_SIZE (after - os->bfd_section->vma);
+           /* Keep track of normal sections using the default
+              lma region.  We use this to set the lma for
+              following sections.  Overlays or other linker
+              script assignment to lma might mean that the
+              default lma == vma is incorrect.
+              To avoid warnings about dot moving backwards when using
+              -Ttext, don't start tracking sections until we find one
+              of non-zero size or with lma set differently to vma.  */
+           if (((os->bfd_section->flags & SEC_HAS_CONTENTS) != 0
+                || (os->bfd_section->flags & SEC_THREAD_LOCAL) == 0)
+               && (os->bfd_section->flags & SEC_ALLOC) != 0
+               && (os->bfd_section->size != 0
+                   || (r->last_os == NULL
+                       && os->bfd_section->vma != os->bfd_section->lma)
+                   || (r->last_os != NULL
+                       && dot >= (r->last_os->output_section_statement
+                                  .bfd_section->vma)))
+               && os->lma_region == NULL
+               && !link_info.relocatable)
+             r->last_os = s;
 
            /* .tbss sections effectively have zero size.  */
            if ((os->bfd_section->flags & SEC_HAS_CONTENTS) != 0
@@ -4272,17 +4746,12 @@ lang_size_sections_1
                  os_region_check (os, os->region, os->addr_tree,
                                   os->bfd_section->vma);
 
-               /* If there's no load address specified, use the run
-                  region as the load region.  */
-               if (os->lma_region == NULL && os->load_base == NULL)
-                 os->lma_region = os->region;
-
-               if (os->lma_region != NULL && os->lma_region != os->region)
+               if (os->lma_region != NULL && os->lma_region != os->region
+                   && (os->bfd_section->flags & SEC_LOAD))
                  {
-                   /* 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->size);
+                   os->lma_region->current
+                     = os->bfd_section->lma + TO_ADDR (os->bfd_section->size);
+
                    if (check_regions)
                      os_region_check (os, os->lma_region, NULL,
                                       os->bfd_section->lma);
@@ -4398,12 +4867,42 @@ lang_size_sections_1
        case lang_assignment_statement_enum:
          {
            bfd_vma newdot = dot;
+           etree_type *tree = s->assignment_statement.exp;
 
-           exp_fold_tree (s->assignment_statement.exp,
+           expld.dataseg.relro = exp_dataseg_relro_none;
+
+           exp_fold_tree (tree,
                           output_section_statement->bfd_section,
                           &newdot);
 
-           if (newdot != dot && !output_section_statement->ignored)
+           if (expld.dataseg.relro == exp_dataseg_relro_start)
+             {
+               if (!expld.dataseg.relro_start_stat)
+                 expld.dataseg.relro_start_stat = s;
+               else
+                 {
+                   ASSERT (expld.dataseg.relro_start_stat == s);
+                 }
+             }
+           else if (expld.dataseg.relro == exp_dataseg_relro_end)
+             {
+               if (!expld.dataseg.relro_end_stat)
+                 expld.dataseg.relro_end_stat = s;
+               else
+                 {
+                   ASSERT (expld.dataseg.relro_end_stat == s);
+                 }
+             }
+           expld.dataseg.relro = exp_dataseg_relro_none;
+
+           /* This symbol is relative to this section.  */
+           if ((tree->type.node_class == etree_provided
+                || tree->type.node_class == etree_assign)
+               && (tree->assign.dst [0] != '.'
+                   || tree->assign.dst [1] != '\0'))
+             output_section_statement->section_relative_symbol = 1;
+
+           if (!output_section_statement->ignored)
              {
                if (output_section_statement == abs_output_section)
                  {
@@ -4412,7 +4911,7 @@ lang_size_sections_1
                    lang_memory_region_lookup (DEFAULT_MEMORY_REGION,
                                               FALSE)->current = newdot;
                  }
-               else
+               else if (newdot != dot)
                  {
                    /* Insert a pad after this statement.  We can't
                       put the pad before when relaxing, in case the
@@ -4427,8 +4926,7 @@ lang_size_sections_1
                       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)))
+                   if (!(output_section_statement->flags & SEC_NEVER_LOAD))
                      output_section_statement->bfd_section->flags |= SEC_ALLOC;
                  }
                dot = newdot;
@@ -4460,19 +4958,64 @@ lang_size_sections_1
                                      fill, dot, relax, check_regions);
          break;
 
-       default:
-         FAIL ();
+       case lang_insert_statement_enum:
          break;
 
          /* We can only get here when relaxing is turned on.  */
        case lang_address_statement_enum:
          break;
+
+       default:
+         FAIL ();
+         break;
        }
       prev = &s->header.next;
     }
   return dot;
 }
 
+/* Callback routine that is used in _bfd_elf_map_sections_to_segments.
+   The BFD library has set NEW_SEGMENT to TRUE iff it thinks that
+   CURRENT_SECTION and PREVIOUS_SECTION ought to be placed into different
+   segments.  We are allowed an opportunity to override this decision.  */
+
+bfd_boolean
+ldlang_override_segment_assignment (struct bfd_link_info * info ATTRIBUTE_UNUSED,
+                                   bfd * abfd ATTRIBUTE_UNUSED,
+                                   asection * current_section,
+                                   asection * previous_section,
+                                   bfd_boolean new_segment)
+{
+  lang_output_section_statement_type * cur;
+  lang_output_section_statement_type * prev;
+
+  /* The checks below are only necessary when the BFD library has decided
+     that the two sections ought to be placed into the same segment.  */
+  if (new_segment)
+    return TRUE;
+
+  /* Paranoia checks.  */
+  if (current_section == NULL || previous_section == NULL)
+    return new_segment;
+
+  /* Find the memory regions associated with the two sections.
+     We call lang_output_section_find() here rather than scanning the list
+     of output sections looking for a matching section pointer because if
+     we have a large number of sections then a hash lookup is faster.  */
+  cur  = lang_output_section_find (current_section->name);
+  prev = lang_output_section_find (previous_section->name);
+
+  /* More paranoia.  */
+  if (cur == NULL || prev == NULL)
+    return new_segment;
+
+  /* If the regions are different then force the sections to live in
+     different segments.  See the email thread starting at the following
+     URL for the reasons why this is necessary:
+     http://sourceware.org/ml/binutils/2007-02/msg00216.html  */
+  return cur->region != prev->region;
+}
+
 void
 one_lang_size_sections_pass (bfd_boolean *relax, bfd_boolean check_regions)
 {
@@ -4493,33 +5036,41 @@ lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions)
     {
       /* If DATA_SEGMENT_ALIGN DATA_SEGMENT_RELRO_END pair was seen, try
         to put expld.dataseg.relro on a (common) page boundary.  */
-      bfd_vma old_min_base, relro_end, maxpage;
+      bfd_vma min_base, old_base, relro_end, maxpage;
 
       expld.dataseg.phase = exp_dataseg_relro_adjust;
-      old_min_base = expld.dataseg.min_base;
       maxpage = expld.dataseg.maxpagesize;
+      /* MIN_BASE is the absolute minimum address we are allowed to start the
+        read-write segment (byte before will be mapped read-only).  */
+      min_base = (expld.dataseg.min_base + maxpage - 1) & ~(maxpage - 1);
+      /* OLD_BASE is the address for a feasible minimum address which will
+        still not cause a data overlap inside MAXPAGE causing file offset skip
+        by MAXPAGE.  */
+      old_base = expld.dataseg.base;
       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)
+      relro_end = ((expld.dataseg.relro_end + expld.dataseg.pagesize - 1)
+                  & ~(expld.dataseg.pagesize - 1));
+      if (min_base + maxpage < expld.dataseg.base)
        {
          expld.dataseg.base -= maxpage;
          relro_end -= maxpage;
        }
+      lang_reset_memory_regions ();
       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.  */
+            inserted at DATA_SEGMENT_RELRO_END.  Try to start a bit lower so
+            that the section alignments will fit in.  */
          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)
+         for (sec = link_info.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)
@@ -4527,10 +5078,10 @@ lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions)
 
          if (((bfd_vma) 1 << max_alignment_power) < expld.dataseg.pagesize)
            {
-             if (expld.dataseg.base - (1 << max_alignment_power)
-                 < old_min_base)
+             if (expld.dataseg.base - (1 << max_alignment_power) < old_base)
                expld.dataseg.base += expld.dataseg.pagesize;
              expld.dataseg.base -= (1 << max_alignment_power);
+             lang_reset_memory_regions ();
              one_lang_size_sections_pass (relax, check_regions);
            }
        }
@@ -4551,6 +5102,7 @@ lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions)
          && first + last <= expld.dataseg.pagesize)
        {
          expld.dataseg.phase = exp_dataseg_adjust;
+         lang_reset_memory_regions ();
          one_lang_size_sections_pass (relax, check_regions);
        }
     }
@@ -4561,11 +5113,10 @@ lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions)
 /* Worker function for lang_do_assignments.  Recursiveness goes here.  */
 
 static bfd_vma
-lang_do_assignments_1
-  (lang_statement_union_type *s,
-   lang_output_section_statement_type *output_section_statement,
-   fill_type *fill,
-   bfd_vma dot)
+lang_do_assignments_1 (lang_statement_union_type *s,
+                      lang_output_section_statement_type *current_os,
+                      fill_type *fill,
+                      bfd_vma dot)
 {
   for (; s != NULL; s = s->header.next)
     {
@@ -4573,9 +5124,7 @@ lang_do_assignments_1
        {
        case lang_constructors_statement_enum:
          dot = lang_do_assignments_1 (constructor_list.head,
-                                      output_section_statement,
-                                      fill,
-                                      dot);
+                                      current_os, fill, dot);
          break;
 
        case lang_output_section_statement_enum:
@@ -4586,22 +5135,17 @@ lang_do_assignments_1
            if (os->bfd_section != NULL && !os->ignored)
              {
                dot = os->bfd_section->vma;
+
                lang_do_assignments_1 (os->children.head, os, os->fill, dot);
+
                /* .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->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 && !os->ignored)
-                 {
-                   os->bfd_section->lma
-                     = exp_get_abs_int (os->load_base, 0, "load base");
-                 }
+
+               if (os->update_dot_tree != NULL)
+                 exp_fold_tree (os->update_dot_tree, bfd_abs_section_ptr, &dot);
              }
          }
          break;
@@ -4609,8 +5153,7 @@ lang_do_assignments_1
        case lang_wild_statement_enum:
 
          dot = lang_do_assignments_1 (s->wild_statement.children.head,
-                                      output_section_statement,
-                                      fill, dot);
+                                      current_os, fill, dot);
          break;
 
        case lang_object_symbols_statement_enum:
@@ -4679,7 +5222,7 @@ lang_do_assignments_1
 
        case lang_assignment_statement_enum:
          exp_fold_tree (s->assignment_statement.exp,
-                        output_section_statement->bfd_section,
+                        current_os->bfd_section,
                         &dot);
          break;
 
@@ -4689,16 +5232,18 @@ lang_do_assignments_1
 
        case lang_group_statement_enum:
          dot = lang_do_assignments_1 (s->group_statement.children.head,
-                                      output_section_statement,
-                                      fill, dot);
+                                      current_os, fill, dot);
          break;
 
-       default:
-         FAIL ();
+       case lang_insert_statement_enum:
          break;
 
        case lang_address_statement_enum:
          break;
+
+       default:
+         FAIL ();
+         break;
        }
     }
   return dot;
@@ -4726,13 +5271,13 @@ lang_set_startof (void)
   if (link_info.relocatable)
     return;
 
-  for (s = output_bfd->sections; s != NULL; s = s->next)
+  for (s = link_info.output_bfd->sections; s != NULL; s = s->next)
     {
       const char *secname;
       char *buf;
       struct bfd_link_hash_entry *h;
 
-      secname = bfd_get_section_name (output_bfd, s);
+      secname = bfd_get_section_name (link_info.output_bfd, s);
       buf = xmalloc (10 + strlen (secname));
 
       sprintf (buf, ".startof.%s", secname);
@@ -4740,7 +5285,7 @@ lang_set_startof (void)
       if (h != NULL && h->type == bfd_link_hash_undefined)
        {
          h->type = bfd_link_hash_defined;
-         h->u.def.value = bfd_get_section_vma (output_bfd, s);
+         h->u.def.value = bfd_get_section_vma (link_info.output_bfd, s);
          h->u.def.section = bfd_abs_section_ptr;
        }
 
@@ -4763,11 +5308,20 @@ lang_end (void)
   struct bfd_link_hash_entry *h;
   bfd_boolean warn;
 
-  if (link_info.relocatable || link_info.shared)
-    warn = FALSE;
+  if ((link_info.relocatable && !link_info.gc_sections)
+      || link_info.shared)
+    warn = entry_from_cmdline;
   else
     warn = TRUE;
 
+  /* Force the user to specify a root when generating a relocatable with
+     --gc-sections.  */
+  if (link_info.gc_sections && link_info.relocatable
+      && (entry_symbol.name == NULL
+         && ldlang_undef_chain_list_head == NULL))
+    einfo (_("%P%F: gc-sections requires either an entry or "
+            "an undefined symbol\n"));
+
   if (entry_symbol.name == NULL)
     {
       /* No entry has been specified.  Look for the default entry, but
@@ -4786,10 +5340,10 @@ lang_end (void)
       bfd_vma val;
 
       val = (h->u.def.value
-            + bfd_get_section_vma (output_bfd,
+            + bfd_get_section_vma (link_info.output_bfd,
                                    h->u.def.section->output_section)
             + h->u.def.section->output_offset);
-      if (! bfd_set_start_address (output_bfd, val))
+      if (! bfd_set_start_address (link_info.output_bfd, val))
        einfo (_("%P%F:%s: can't set start address\n"), entry_symbol.name);
     }
   else
@@ -4802,7 +5356,7 @@ lang_end (void)
       val = bfd_scan_vma (entry_symbol.name, &send, 0);
       if (*send == '\0')
        {
-         if (! bfd_set_start_address (output_bfd, val))
+         if (! bfd_set_start_address (link_info.output_bfd, val))
            einfo (_("%P%F: can't set start address\n"));
        }
       else
@@ -4811,17 +5365,17 @@ lang_end (void)
 
          /* Can't find the entry symbol, and it's not a number.  Use
             the first address in the text section.  */
-         ts = bfd_get_section_by_name (output_bfd, entry_section);
+         ts = bfd_get_section_by_name (link_info.output_bfd, entry_section);
          if (ts != NULL)
            {
              if (warn)
                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,
-                                          bfd_get_section_vma (output_bfd,
-                                                               ts)))
+                      bfd_get_section_vma (link_info.output_bfd, ts));
+             if (!(bfd_set_start_address
+                   (link_info.output_bfd,
+                    bfd_get_section_vma (link_info.output_bfd, ts))))
                einfo (_("%P%F: can't set start address\n"));
            }
          else
@@ -4862,7 +5416,7 @@ lang_check (void)
     {
       input_bfd = file->input_statement.the_bfd;
       compatible
-       = bfd_arch_get_compatible (input_bfd, output_bfd,
+       = bfd_arch_get_compatible (input_bfd, link_info.output_bfd,
                                   command_line.accept_unknown_input_arch);
 
       /* In general it is not possible to perform a relocatable
@@ -4873,23 +5427,24 @@ lang_check (void)
         relocs for other link purposes than a final link).  */
       if ((link_info.relocatable || link_info.emitrelocations)
          && (compatible == NULL
-             || bfd_get_flavour (input_bfd) != bfd_get_flavour (output_bfd))
+             || (bfd_get_flavour (input_bfd)
+                 != bfd_get_flavour (link_info.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"),
                 bfd_get_target (input_bfd), input_bfd,
-                bfd_get_target (output_bfd), output_bfd);
+                bfd_get_target (link_info.output_bfd), link_info.output_bfd);
          /* einfo with %F exits.  */
        }
 
       if (compatible == NULL)
        {
          if (command_line.warn_mismatch)
-           einfo (_("%P: warning: %s architecture of input file `%B'"
+           einfo (_("%P%X: %s architecture of input file `%B'"
                     " is incompatible with %s output\n"),
                   bfd_printable_name (input_bfd), input_bfd,
-                  bfd_printable_name (output_bfd));
+                  bfd_printable_name (link_info.output_bfd));
        }
       else if (bfd_count_sections (input_bfd))
        {
@@ -4905,7 +5460,7 @@ lang_check (void)
             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 (! bfd_merge_private_bfd_data (input_bfd, link_info.output_bfd))
            {
              if (command_line.warn_mismatch)
                einfo (_("%P%X: failed to merge target specific data"
@@ -4919,7 +5474,7 @@ lang_check (void)
 
 /* Look through all the global common symbols and attach them to the
    correct section.  The -sort-common command line switch may be used
-   to roughly sort the entries by size.  */
+   to roughly sort the entries by alignment.  */
 
 static void
 lang_common (void)
@@ -4934,10 +5489,24 @@ lang_common (void)
     bfd_link_hash_traverse (link_info.hash, lang_one_common, NULL);
   else
     {
-      int power;
+      unsigned int power;
+
+      if (config.sort_common == sort_descending)
+       {
+         for (power = 4; power > 0; power--)
+           bfd_link_hash_traverse (link_info.hash, lang_one_common, &power);
+
+         power = 0;
+         bfd_link_hash_traverse (link_info.hash, lang_one_common, &power);
+       }
+      else
+       {
+         for (power = 0; power <= 4; power++)
+           bfd_link_hash_traverse (link_info.hash, lang_one_common, &power);
 
-      for (power = 4; power >= 0; power--)
-       bfd_link_hash_traverse (link_info.hash, lang_one_common, &power);
+         power = UINT_MAX;
+         bfd_link_hash_traverse (link_info.hash, lang_one_common, &power);
+       }
     }
 }
 
@@ -4956,8 +5525,11 @@ lang_one_common (struct bfd_link_hash_entry *h, void *info)
   size = h->u.c.size;
   power_of_two = h->u.c.p->alignment_power;
 
-  if (config.sort_common
-      && power_of_two < (unsigned int) *(int *) info)
+  if (config.sort_common == sort_descending
+      && power_of_two < *(unsigned int *) info)
+    return TRUE;
+  else if (config.sort_common == sort_ascending
+          && power_of_two > *(unsigned int *) info)
     return TRUE;
 
   section = h->u.c.p->section;
@@ -4997,10 +5569,19 @@ lang_one_common (struct bfd_link_hash_entry *h, void *info)
          header_printed = TRUE;
        }
 
-      name = demangle (h->root.string);
-      minfo ("%s", name);
-      len = strlen (name);
-      free (name);
+      name = bfd_demangle (link_info.output_bfd, h->root.string,
+                          DMGL_ANSI | DMGL_PARAMS);
+      if (name == NULL)
+       {
+         minfo ("%s", h->root.string);
+         len = strlen (h->root.string);
+       }
+      else
+       {
+         minfo ("%s", name);
+         len = strlen (name);
+         free (name);
+       }
 
       if (len >= 19)
        {
@@ -5064,23 +5645,36 @@ lang_place_orphans (void)
                      || command_line.force_common_definition)
                    {
                      if (default_common_section == NULL)
-                       {
-                         default_common_section =
-                           lang_output_section_statement_lookup (".bss");
-
-                       }
+                       default_common_section
+                         = lang_output_section_statement_lookup (".bss", 0,
+                                                                 TRUE);
                      lang_add_section (&default_common_section->children, s,
                                        default_common_section);
                    }
                }
-             else if (ldemul_place_orphan (s))
-               ;
              else
                {
-                 lang_output_section_statement_type *os;
+                 const char *name = s->name;
+
+                 if ((config.unique_orphan_sections
+                      || unique_section_p (s))
+                     && bfd_get_section_by_name (link_info.output_bfd,
+                                                 name) != NULL)
+                   {
+                     static int count = 1;
+                     name = bfd_get_unique_section_name (link_info.output_bfd,
+                                                         name, &count);
+                     if (name == NULL)
+                       einfo ("%F%P: place_orphan failed: %E\n");
+                   }
 
-                 os = lang_output_section_statement_lookup (s->name);
-                 lang_add_section (&os->children, s, os);
+                 if (!ldemul_place_orphan (s, name))
+                   {
+                     lang_output_section_statement_type *os;
+                     os = lang_output_section_statement_lookup (name, 0,
+                                                                TRUE);
+                     lang_add_section (&os->children, s, os);
+                   }
                }
            }
        }
@@ -5156,8 +5750,6 @@ lang_for_each_file (void (*func) (lang_input_statement_type *))
 void
 ldlang_add_file (lang_input_statement_type *entry)
 {
-  bfd **pp;
-
   lang_statement_append (&file_chain,
                         (lang_statement_union_type *) entry,
                         &entry->next);
@@ -5165,10 +5757,10 @@ ldlang_add_file (lang_input_statement_type *entry)
   /* The BFD linker needs to have a list of all input BFDs involved in
      a link.  */
   ASSERT (entry->the_bfd->link_next == NULL);
-  ASSERT (entry->the_bfd != output_bfd);
-  for (pp = &link_info.input_bfds; *pp != NULL; pp = &(*pp)->link_next)
-    ;
-  *pp = entry->the_bfd;
+  ASSERT (entry->the_bfd != link_info.output_bfd);
+
+  *link_info.input_bfds_tail = entry->the_bfd;
+  link_info.input_bfds_tail = &entry->the_bfd->link_next;
   entry->the_bfd->usrdata = entry;
   bfd_set_gp_size (entry->the_bfd, g_switch_value);
 
@@ -5228,11 +5820,9 @@ lang_enter_output_section_statement (const char *output_section_statement_name,
 {
   lang_output_section_statement_type *os;
 
-   os = lang_output_section_statement_lookup_1 (output_section_statement_name,
-                                               constraint);
-   current_section = os;
-
-  /* Make next things chain into subchain of this.  */
+  os = lang_output_section_statement_lookup (output_section_statement_name,
+                                            constraint, TRUE);
+  current_section = os;
 
   if (os->addr_tree == NULL)
     {
@@ -5244,6 +5834,8 @@ lang_enter_output_section_statement (const char *output_section_statement_name,
   else
     os->flags = SEC_NEVER_LOAD;
   os->block_value = 1;
+
+  /* Make next things chain into subchain of this.  */
   stat_ptr = &os->children;
 
   os->subsection_alignment =
@@ -5275,16 +5867,19 @@ lang_reset_memory_regions (void)
 
   for (p = lang_memory_region_list; p != NULL; p = p->next)
     {
-      p->old_length = (bfd_size_type) (p->current - p->origin);
       p->current = p->origin;
+      p->last_os = NULL;
     }
 
   for (os = &lang_output_section_statement.head->output_section_statement;
        os != NULL;
        os = os->next)
-    os->processed = FALSE;
+    {
+      os->processed_vma = FALSE;
+      os->processed_lma = FALSE;
+    }
 
-  for (o = output_bfd->sections; o != NULL; o = o->next)
+  for (o = link_info.output_bfd->sections; o != NULL; o = o->next)
     {
       /* Save the last size for possible use by bfd_relax_section.  */
       o->rawsize = o->size;
@@ -5337,30 +5932,10 @@ lang_gc_sections_1 (lang_statement_union_type *s)
 static void
 lang_gc_sections (void)
 {
-  struct bfd_link_hash_entry *h;
-  ldlang_undef_chain_list_type *ulist;
-
   /* Keep all sections so marked in the link script.  */
 
   lang_gc_sections_1 (statement_list.head);
 
-  /* Keep all sections containing symbols undefined on the command-line,
-     and the section containing the entry symbol.  */
-
-  for (ulist = link_info.gc_sym_list; ulist; ulist = ulist->next)
-    {
-      h = bfd_link_hash_lookup (link_info.hash, ulist->name,
-                               FALSE, FALSE, FALSE);
-
-      if (h != NULL
-         && (h->type == bfd_link_hash_defined
-             || h->type == bfd_link_hash_defweak)
-         && ! bfd_is_abs_section (h->u.def.section))
-       {
-         h->u.def.section->flags |= SEC_KEEP;
-       }
-    }
-
   /* 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.  */
@@ -5376,12 +5951,124 @@ lang_gc_sections (void)
     }
 
   if (link_info.gc_sections)
-    bfd_gc_sections (output_bfd, &link_info);
+    bfd_gc_sections (link_info.output_bfd, &link_info);
+}
+
+/* Worker for lang_find_relro_sections_1.  */
+
+static void
+find_relro_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 *data)
+{
+  /* Discarded, excluded and ignored sections effectively have zero
+     size.  */
+  if (section->output_section != NULL
+      && section->output_section->owner == link_info.output_bfd
+      && (section->output_section->flags & SEC_EXCLUDE) == 0
+      && !IGNORE_SECTION (section)
+      && section->size != 0)
+    {
+      bfd_boolean *has_relro_section = (bfd_boolean *) data;
+      *has_relro_section = TRUE;
+    }
+}
+
+/* Iterate over sections for relro sections.  */
+
+static void
+lang_find_relro_sections_1 (lang_statement_union_type *s,
+                           bfd_boolean *has_relro_section)
+{
+  if (*has_relro_section)
+    return;
+
+  for (; s != NULL; s = s->header.next)
+    {
+      if (s == expld.dataseg.relro_end_stat)
+       break;
+
+      switch (s->header.type)
+       {
+       case lang_wild_statement_enum:
+         walk_wild (&s->wild_statement,
+                    find_relro_section_callback,
+                    has_relro_section);
+         break;
+       case lang_constructors_statement_enum:
+         lang_find_relro_sections_1 (constructor_list.head,
+                                     has_relro_section);
+         break;
+       case lang_output_section_statement_enum:
+         lang_find_relro_sections_1 (s->output_section_statement.children.head,
+                                     has_relro_section);
+         break;
+       case lang_group_statement_enum:
+         lang_find_relro_sections_1 (s->group_statement.children.head,
+                                     has_relro_section);
+         break;
+       default:
+         break;
+       }
+    }
+}
+
+static void
+lang_find_relro_sections (void)
+{
+  bfd_boolean has_relro_section = FALSE;
+
+  /* Check all sections in the link script.  */
+
+  lang_find_relro_sections_1 (expld.dataseg.relro_start_stat,
+                             &has_relro_section);
+
+  if (!has_relro_section)
+    link_info.relro = FALSE;
+}
+
+/* Relax all sections until bfd_relax_section gives up.  */
+
+static void
+relax_sections (void)
+{
+  /* Keep relaxing until bfd_relax_section gives up.  */
+  bfd_boolean relax_again;
+
+  link_info.relax_trip = -1;
+  do
+    {
+      relax_again = FALSE;
+      link_info.relax_trip++;
+
+      /* Note: pe-dll.c does something like this also.  If you find
+        you need to change this code, you probably need to change
+        pe-dll.c also.  DJ  */
+
+      /* Do all the assignments with our current guesses as to
+        section sizes.  */
+      lang_do_assignments ();
+
+      /* We must do this after lang_do_assignments, because it uses
+        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 (&relax_again, FALSE);
+    }
+  while (relax_again);
 }
 
 void
 lang_process (void)
 {
+  /* Finalize dynamic list.  */
+  if (link_info.dynamic_list)
+    lang_finalize_version_expr_head (&link_info.dynamic_list->head);
+
   current_target = default_target;
 
   /* Open the output file.  */
@@ -5436,6 +6123,8 @@ lang_process (void)
      to the correct output sections.  */
   map_input_to_output_sections (statement_list.head, NULL, NULL);
 
+  process_insert_statements ();
+
   /* Find any sections not attached explicitly and handle them.  */
   lang_place_orphans ();
 
@@ -5447,10 +6136,10 @@ lang_process (void)
         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);
+      bfd_merge_sections (link_info.output_bfd, &link_info);
 
       /* Look for a text section and set the readonly attribute in it.  */
-      found = bfd_get_section_by_name (output_bfd, ".text");
+      found = bfd_get_section_by_name (link_info.output_bfd, ".text");
 
       if (found != NULL)
        {
@@ -5469,44 +6158,27 @@ lang_process (void)
      section positions, since they will affect SIZEOF_HEADERS.  */
   lang_record_phdrs ();
 
+  /* Check relro sections.  */
+  if (link_info.relro && ! link_info.relocatable)
+    lang_find_relro_sections ();
+
   /* Size up the sections.  */
   lang_size_sections (NULL, !command_line.relax);
 
   /* Now run around and relax if we can.  */
   if (command_line.relax)
     {
-      /* Keep relaxing until bfd_relax_section gives up.  */
-      bfd_boolean relax_again;
-
-      do
-       {
-         relax_again = FALSE;
-
-         /* Note: pe-dll.c does something like this also.  If you find
-            you need to change this code, you probably need to change
-            pe-dll.c also.  DJ  */
-
-         /* Do all the assignments with our current guesses as to
-            section sizes.  */
-         lang_do_assignments ();
-
-         /* We must do this after lang_do_assignments, because it uses
-            size.  */
-         lang_reset_memory_regions ();
+      /* We may need more than one relaxation pass.  */
+      int i = link_info.relax_pass;
 
-         /* Perform another relax pass - this time we know where the
-            globals are, so can make a better guess.  */
-         lang_size_sections (&relax_again, FALSE);
+      /* The backend can use it to determine the current pass.  */
+      link_info.relax_pass = 0;
 
-         /* If the normal relax is done and the relax finalize pass
-            is not performed yet, we perform another relax pass.  */
-         if (!relax_again && link_info.need_relax_finalize)
-           {
-             link_info.need_relax_finalize = FALSE;
-             relax_again = TRUE;
-           }
+      while (i--)
+       {
+         relax_sections ();
+         link_info.relax_pass++;
        }
-      while (relax_again);
 
       /* Final extra sizing to report errors.  */
       lang_do_assignments ();
@@ -5526,13 +6198,13 @@ lang_process (void)
 
   lang_do_assignments ();
 
+  ldemul_finish ();
+
   /* Make sure that the section addresses make sense.  */
   if (! link_info.relocatable
       && command_line.check_section_addresses)
     lang_check_section_addresses ();
 
-  /* Final stuffs.  */
-  ldemul_finish ();
   lang_end ();
 }
 
@@ -5797,11 +6469,11 @@ lang_abs_symbol_at_beginning_of (const char *secname, const char *name)
 
       h->type = bfd_link_hash_defined;
 
-      sec = bfd_get_section_by_name (output_bfd, secname);
+      sec = bfd_get_section_by_name (link_info.output_bfd, secname);
       if (sec == NULL)
        h->u.def.value = 0;
       else
-       h->u.def.value = bfd_get_section_vma (output_bfd, sec);
+       h->u.def.value = bfd_get_section_vma (link_info.output_bfd, sec);
 
       h->u.def.section = bfd_abs_section_ptr;
     }
@@ -5828,11 +6500,11 @@ lang_abs_symbol_at_end_of (const char *secname, const char *name)
 
       h->type = bfd_link_hash_defined;
 
-      sec = bfd_get_section_by_name (output_bfd, secname);
+      sec = bfd_get_section_by_name (link_info.output_bfd, secname);
       if (sec == NULL)
        h->u.def.value = 0;
       else
-       h->u.def.value = (bfd_get_section_vma (output_bfd, sec)
+       h->u.def.value = (bfd_get_section_vma (link_info.output_bfd, sec)
                          + TO_ADDR (sec->size));
 
       h->u.def.section = bfd_abs_section_ptr;
@@ -5869,6 +6541,17 @@ lang_add_output_format (const char *format,
     }
 }
 
+void
+lang_add_insert (const char *where, int is_before)
+{
+  lang_insert_statement_type *new;
+
+  new = new_stat (lang_insert_statement, stat_ptr);
+  new->where = where;
+  new->is_before = is_before;
+  saved_script_handle = previous_script_handle;
+}
+
 /* Enter a group.  This creates a new lang_group_statement, and sets
    stat_ptr to build new statements within the group.  */
 
@@ -5935,6 +6618,7 @@ lang_record_phdrs (void)
   alc = 10;
   secs = xmalloc (alc * sizeof (asection *));
   last = NULL;
+
   for (l = lang_phdr_list; l != NULL; l = l->next)
     {
       unsigned int c;
@@ -5948,7 +6632,7 @@ lang_record_phdrs (void)
        {
          lang_output_section_phdr_list *pl;
 
-         if (os->constraint == -1)
+         if (os->constraint < 0)
            continue;
 
          pl = os->phdrs;
@@ -5960,6 +6644,28 @@ lang_record_phdrs (void)
                  || os->bfd_section == NULL
                  || (os->bfd_section->flags & SEC_ALLOC) == 0)
                continue;
+
+             if (last == NULL)
+               {
+                 lang_output_section_statement_type * tmp_os;
+
+                 /* If we have not run across a section with a program
+                    header assigned to it yet, then scan forwards to find
+                    one.  This prevents inconsistencies in the linker's
+                    behaviour when a script has specified just a single
+                    header and there are sections in that script which are
+                    not assigned to it, and which occur before the first
+                    use of that header. See here for more details:
+                    http://sourceware.org/ml/binutils/2007-02/msg00291.html  */
+                 for (tmp_os = os; tmp_os; tmp_os = tmp_os->next)
+                   if (tmp_os->phdrs)
+                     {
+                       last = tmp_os->phdrs;
+                       break;
+                     }
+                 if (last == NULL)
+                   einfo (_("%F%P: no sections assigned to phdrs\n"));
+               }
              pl = last;
            }
 
@@ -5992,7 +6698,7 @@ lang_record_phdrs (void)
       else
        at = exp_get_vma (l->at, 0, "phdr load address");
 
-      if (! bfd_record_phdr (output_bfd, l->type,
+      if (! bfd_record_phdr (link_info.output_bfd, l->type,
                             l->flags != NULL, flags, l->at != NULL,
                             at, l->filehdr, l->phdrs, c, secs))
        einfo (_("%F%P: bfd_record_phdr failed: %E\n"));
@@ -6007,7 +6713,7 @@ lang_record_phdrs (void)
     {
       lang_output_section_phdr_list *pl;
 
-      if (os->constraint == -1
+      if (os->constraint < 0
          || os->bfd_section == NULL)
        continue;
 
@@ -6079,7 +6785,7 @@ lang_enter_overlay_section (const char *name)
   struct overlay_list *n;
   etree_type *size;
 
-  lang_enter_output_section_statement (name, overlay_vma, normal_section,
+  lang_enter_output_section_statement (name, overlay_vma, overlay_section,
                                       0, overlay_subalign, 0, 0);
 
   /* If this is the first section, then base the VMA of future
@@ -6134,15 +6840,17 @@ lang_leave_overlay_section (fill_type *fill,
 
   buf = xmalloc (strlen (clean) + sizeof "__load_start_");
   sprintf (buf, "__load_start_%s", clean);
-  lang_add_assignment (exp_assop ('=', buf,
-                                 exp_nameop (LOADADDR, name)));
+  lang_add_assignment (exp_provide (buf,
+                                   exp_nameop (LOADADDR, name),
+                                   FALSE));
 
   buf = xmalloc (strlen (clean) + sizeof "__load_stop_");
   sprintf (buf, "__load_stop_%s", clean);
-  lang_add_assignment (exp_assop ('=', buf,
-                                 exp_binop ('+',
-                                            exp_nameop (LOADADDR, name),
-                                            exp_nameop (SIZEOF, name))));
+  lang_add_assignment (exp_provide (buf,
+                                   exp_binop ('+',
+                                              exp_nameop (LOADADDR, name),
+                                              exp_nameop (SIZEOF, name)),
+                                   FALSE));
 
   free (clean);
 }
@@ -6191,12 +6899,10 @@ lang_leave_overlay (etree_type *lma_expr,
         The base address is not needed (and should be null) if
         an LMA region was specified.  */
       if (l->next == 0)
-       l->os->load_base = lma_expr;
-      else if (lma_region == 0)
-       l->os->load_base = exp_binop ('+',
-                                     exp_nameop (LOADADDR, l->next->os->name),
-                                     exp_nameop (SIZEOF, l->next->os->name));
-
+       {
+         l->os->load_base = lma_expr;
+         l->os->sectype = normal_section;
+       }
       if (phdrs != NULL && l->os->phdrs == NULL)
        l->os->phdrs = phdrs;
 
@@ -6261,44 +6967,44 @@ lang_vers_match (struct bfd_elf_version_expr_head *head,
 
       switch (prev ? prev->mask : 0)
        {
-         case 0:
-           if (head->mask & BFD_ELF_VERSION_C_TYPE)
-             {
-               e.symbol = sym;
-               expr = htab_find (head->htab, &e);
-               while (expr && strcmp (expr->symbol, sym) == 0)
-                 if (expr->mask == BFD_ELF_VERSION_C_TYPE)
-                   goto out_ret;
-                 else
-                   expr = expr->next;
-             }
-           /* Fallthrough */
-         case BFD_ELF_VERSION_C_TYPE:
-           if (head->mask & BFD_ELF_VERSION_CXX_TYPE)
-             {
-               e.symbol = cxx_sym;
-               expr = htab_find (head->htab, &e);
-               while (expr && strcmp (expr->symbol, cxx_sym) == 0)
-                 if (expr->mask == BFD_ELF_VERSION_CXX_TYPE)
-                   goto out_ret;
-                 else
-                   expr = expr->next;
-             }
-           /* Fallthrough */
-         case BFD_ELF_VERSION_CXX_TYPE:
-           if (head->mask & BFD_ELF_VERSION_JAVA_TYPE)
-             {
-               e.symbol = java_sym;
-               expr = htab_find (head->htab, &e);
-               while (expr && strcmp (expr->symbol, java_sym) == 0)
-                 if (expr->mask == BFD_ELF_VERSION_JAVA_TYPE)
-                   goto out_ret;
-                 else
-                   expr = expr->next;
-             }
-           /* Fallthrough */
-         default:
-           break;
+       case 0:
+         if (head->mask & BFD_ELF_VERSION_C_TYPE)
+           {
+             e.symbol = sym;
+             expr = htab_find (head->htab, &e);
+             while (expr && strcmp (expr->symbol, sym) == 0)
+               if (expr->mask == BFD_ELF_VERSION_C_TYPE)
+                 goto out_ret;
+               else
+                 expr = expr->next;
+           }
+         /* Fallthrough */
+       case BFD_ELF_VERSION_C_TYPE:
+         if (head->mask & BFD_ELF_VERSION_CXX_TYPE)
+           {
+             e.symbol = cxx_sym;
+             expr = htab_find (head->htab, &e);
+             while (expr && strcmp (expr->symbol, cxx_sym) == 0)
+               if (expr->mask == BFD_ELF_VERSION_CXX_TYPE)
+                 goto out_ret;
+               else
+                 expr = expr->next;
+           }
+         /* Fallthrough */
+       case BFD_ELF_VERSION_CXX_TYPE:
+         if (head->mask & BFD_ELF_VERSION_JAVA_TYPE)
+           {
+             e.symbol = java_sym;
+             expr = htab_find (head->htab, &e);
+             while (expr && strcmp (expr->symbol, java_sym) == 0)
+               if (expr->mask == BFD_ELF_VERSION_JAVA_TYPE)
+                 goto out_ret;
+               else
+                 expr = expr->next;
+           }
+         /* Fallthrough */
+       default:
+         break;
        }
     }
 
@@ -6327,7 +7033,7 @@ lang_vers_match (struct bfd_elf_version_expr_head *head,
        break;
     }
 
-out_ret:
+ out_ret:
   if (cxx_sym != sym)
     free ((char *) cxx_sym);
   if (java_sym != sym)
@@ -6687,7 +7393,7 @@ lang_do_version_exports_section (void)
       /* Do not free the contents, as we used them creating the regex.  */
 
       /* Do not include this section in the link.  */
-      sec->flags |= SEC_EXCLUDE;
+      sec->flags |= SEC_EXCLUDE | SEC_KEEP;
     }
 
   lreg = lang_new_vers_pattern (NULL, "*", NULL, FALSE);
@@ -6709,3 +7415,69 @@ lang_add_unique (const char *name)
   ent->next = unique_section_list;
   unique_section_list = ent;
 }
+
+/* Append the list of dynamic symbols to the existing one.  */
+
+void
+lang_append_dynamic_list (struct bfd_elf_version_expr *dynamic)
+{
+  if (link_info.dynamic_list)
+    {
+      struct bfd_elf_version_expr *tail;
+      for (tail = dynamic; tail->next != NULL; tail = tail->next)
+       ;
+      tail->next = link_info.dynamic_list->head.list;
+      link_info.dynamic_list->head.list = dynamic;
+    }
+  else
+    {
+      struct bfd_elf_dynamic_list *d;
+
+      d = xcalloc (1, sizeof *d);
+      d->head.list = dynamic;
+      d->match = lang_vers_match;
+      link_info.dynamic_list = d;
+    }
+}
+
+/* Append the list of C++ typeinfo dynamic symbols to the existing
+   one.  */
+
+void
+lang_append_dynamic_list_cpp_typeinfo (void)
+{
+  const char * symbols [] =
+    {
+      "typeinfo name for*",
+      "typeinfo for*"
+    };
+  struct bfd_elf_version_expr *dynamic = NULL;
+  unsigned int i;
+
+  for (i = 0; i < ARRAY_SIZE (symbols); i++)
+    dynamic = lang_new_vers_pattern (dynamic, symbols [i], "C++",
+                                    FALSE);
+
+  lang_append_dynamic_list (dynamic);
+}
+
+/* Append the list of C++ operator new and delete dynamic symbols to the
+   existing one.  */
+
+void
+lang_append_dynamic_list_cpp_new (void)
+{
+  const char * symbols [] =
+    {
+      "operator new*",
+      "operator delete*"
+    };
+  struct bfd_elf_version_expr *dynamic = NULL;
+  unsigned int i;
+
+  for (i = 0; i < ARRAY_SIZE (symbols); i++)
+    dynamic = lang_new_vers_pattern (dynamic, symbols [i], "C++",
+                                    FALSE);
+
+  lang_append_dynamic_list (dynamic);
+}
This page took 0.070366 seconds and 4 git commands to generate.