gas: sparc: fix collision of registers and pseudo-ops.
[deliverable/binutils-gdb.git] / ld / ldlang.c
index 7fc4ff3650978b01106a9c5fa45ef18bddfb9c78..b841408a0315b78d0208c68cac73772bcab4027c 100644 (file)
@@ -1,7 +1,5 @@
 /* Linker command language support.
-   Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-   2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
-   Free Software Foundation, Inc.
+   Copyright (C) 1991-2016 Free Software Foundation, Inc.
 
    This file is part of the GNU Binutils.
 
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
    MA 02110-1301, USA.  */
 
-#include <limits.h>
-
 #include "sysdep.h"
 #include "bfd.h"
 #include "libiberty.h"
+#include "filenames.h"
 #include "safe-ctype.h"
 #include "obstack.h"
 #include "bfdlink.h"
 #include "fnmatch.h"
 #include "demangle.h"
 #include "hashtab.h"
+#include "libbfd.h"
+#include "elf-bfd.h"
+#ifdef ENABLE_PLUGINS
+#include "plugin.h"
+#endif /* ENABLE_PLUGINS */
 
 #ifndef offsetof
 #define offsetof(TYPE, MEMBER) ((size_t) & (((TYPE*) 0)->MEMBER))
 #endif
 
-/* Locals variables.  */
+/* Convert between addresses in bytes and sizes in octets.
+   For currently supported targets, octets_per_byte is always a power
+   of two, so we can use shifts.  */
+#define TO_ADDR(X) ((X) >> opb_shift)
+#define TO_SIZE(X) ((X) << opb_shift)
+
+/* Local variables.  */
 static struct obstack stat_obstack;
 static struct obstack map_obstack;
 
 #define obstack_chunk_alloc xmalloc
 #define obstack_chunk_free free
-static const char *startup_file;
+static const char *entry_symbol_default = "start";
 static bfd_boolean placed_commons = FALSE;
-static bfd_boolean stripped_excluded_sections = FALSE;
+static bfd_boolean map_head_is_link_order = FALSE;
 static lang_output_section_statement_type *default_common_section;
 static bfd_boolean map_option_f;
 static bfd_vma print_dot;
 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 bfd_hash_table lang_definedness_table;
+static lang_statement_list_type *stat_save[10];
+static lang_statement_list_type **stat_save_ptr = &stat_save[0];
+static struct unique_sections *unique_section_list;
+static struct asneeded_minfo *asneeded_list_head;
+static unsigned int opb_shift = 0;
 
 /* 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 struct bfd_hash_entry *lang_definedness_newfunc
- (struct bfd_hash_entry *, struct bfd_hash_table *, const char *);
 static void insert_undefined (const char *);
 static bfd_boolean sort_def_symbol (struct bfd_link_hash_entry *, void *);
 static void print_statement (lang_statement_union_type *,
@@ -78,38 +86,38 @@ static void print_statement (lang_statement_union_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 void print_input_section (asection *, bfd_boolean);
 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 *);
+static void lang_do_memory_regions (void);
 
 /* Exported variables.  */
+const char *output_target;
 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";
+struct lang_input_statement_flags input_flags;
 bfd_boolean entry_from_cmdline;
+bfd_boolean undef_from_cmdline;
 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;
+struct asneeded_minfo **asneeded_list_tail;
 
  /* Functions that traverse the linker script and might evaluate
-    DEFINED() need to increment this.  */
+    DEFINED() need to increment this at the start of the traversal.  */
 int lang_statement_iteration = 0;
 
-etree_type *base; /* Relocation base - or null */
-
 /* Return TRUE if the PATTERN argument is a wildcard pattern.
    Although backslashes are treated specially if a pattern contains
    wildcards, we do not consider the mere presence of a backslash to
@@ -193,16 +201,18 @@ input_statement_is_archive_path (const char *file_spec, char *sep,
   return match;
 }
 
-bfd_boolean
-unique_section_p (const asection *sec)
+static bfd_boolean
+unique_section_p (const asection *sec,
+                 const lang_output_section_statement_type *os)
 {
   struct unique_sections *unam;
   const char *secnam;
 
-  if (link_info.relocatable
+  if (bfd_link_relocatable (&link_info)
       && sec->owner != NULL
       && bfd_is_group_section (sec->owner, sec))
-    return TRUE;
+    return !(os != NULL
+            && strcmp (os->name, DISCARD_SECTION_NAME) == 0);
 
   secnam = sec->name;
   for (unam = unique_section_list; unam; unam = unam->next)
@@ -255,7 +265,7 @@ walk_wild_consider_section (lang_wild_statement_type *ptr,
        return;
     }
 
-  (*callback) (ptr, sec, s, file, data);
+  (*callback) (ptr, sec, s, ptr->section_flag_list, file, data);
 }
 
 /* Lowest common denominator routine that can handle everything correctly,
@@ -274,7 +284,7 @@ walk_wild_section_general (lang_wild_statement_type *ptr,
     {
       sec = ptr->section_list;
       if (sec == NULL)
-       (*callback) (ptr, sec, s, file, data);
+       (*callback) (ptr, sec, s, ptr->section_flag_list, file, data);
 
       while (sec != NULL)
        {
@@ -305,9 +315,9 @@ typedef struct
 } section_iterator_callback_data;
 
 static bfd_boolean
-section_iterator_callback (bfd *bfd ATTRIBUTE_UNUSED, asection *s, void *data)
+section_iterator_callback (bfd *abfd ATTRIBUTE_UNUSED, asection *s, void *data)
 {
-  section_iterator_callback_data *d = data;
+  section_iterator_callback_data *d = (section_iterator_callback_data *) data;
 
   if (d->found_section != NULL)
     {
@@ -363,18 +373,70 @@ match_simple_wild (const char *pattern, const char *name)
   return TRUE;
 }
 
+/* Return the numerical value of the init_priority attribute from
+   section name NAME.  */
+
+static unsigned long
+get_init_priority (const char *name)
+{
+  char *end;
+  unsigned long init_priority;
+
+  /* GCC uses the following section names for the init_priority
+     attribute with numerical values 101 and 65535 inclusive. A
+     lower value means a higher priority.
+
+     1: .init_array.NNNN/.fini_array.NNNN: Where NNNN is the
+       decimal numerical value of the init_priority attribute.
+       The order of execution in .init_array is forward and
+       .fini_array is backward.
+     2: .ctors.NNNN/.dtors.NNNN: Where NNNN is 65535 minus the
+       decimal numerical value of the init_priority attribute.
+       The order of execution in .ctors is backward and .dtors
+       is forward.
+   */
+  if (strncmp (name, ".init_array.", 12) == 0
+      || strncmp (name, ".fini_array.", 12) == 0)
+    {
+      init_priority = strtoul (name + 12, &end, 10);
+      return *end ? 0 : init_priority;
+    }
+  else if (strncmp (name, ".ctors.", 7) == 0
+          || strncmp (name, ".dtors.", 7) == 0)
+    {
+      init_priority = strtoul (name + 7, &end, 10);
+      return *end ? 0 : 65535 - init_priority;
+    }
+
+  return 0;
+}
+
 /* Compare sections ASEC and BSEC according to SORT.  */
 
 static int
 compare_section (sort_type sort, asection *asec, asection *bsec)
 {
   int ret;
+  unsigned long ainit_priority, binit_priority;
 
   switch (sort)
     {
     default:
       abort ();
 
+    case by_init_priority:
+      ainit_priority
+       = get_init_priority (bfd_get_section_name (asec->owner, asec));
+      binit_priority
+       = get_init_priority (bfd_get_section_name (bsec->owner, bsec));
+      if (ainit_priority == 0 || binit_priority == 0)
+       goto sort_by_name;
+      ret = ainit_priority - binit_priority;
+      if (ret)
+       break;
+      else
+       goto sort_by_name;
+
     case by_alignment_name:
       ret = (bfd_section_alignment (bsec->owner, bsec)
             - bfd_section_alignment (asec->owner, asec));
@@ -383,6 +445,7 @@ compare_section (sort_type sort, asection *asec, asection *bsec)
       /* Fall through.  */
 
     case by_name:
+sort_by_name:
       ret = strcmp (bfd_get_section_name (asec->owner, asec),
                    bfd_get_section_name (bsec->owner, bsec));
       break;
@@ -443,16 +506,20 @@ static void
 output_section_callback_fast (lang_wild_statement_type *ptr,
                              struct wildcard_list *sec,
                              asection *section,
+                             struct flag_info *sflag_list ATTRIBUTE_UNUSED,
                              lang_input_statement_type *file,
-                             void *output ATTRIBUTE_UNUSED)
+                             void *output)
 {
   lang_section_bst_type *node;
   lang_section_bst_type **tree;
+  lang_output_section_statement_type *os;
+
+  os = (lang_output_section_statement_type *) output;
 
-  if (unique_section_p (section))
+  if (unique_section_p (section, os))
     return;
 
-  node = xmalloc (sizeof (lang_section_bst_type));
+  node = (lang_section_bst_type *) xmalloc (sizeof (lang_section_bst_type));
   node->left = 0;
   node->right = 0;
   node->section = section;
@@ -472,7 +539,7 @@ output_section_callback_tree_to_list (lang_wild_statement_type *ptr,
   if (tree->left)
     output_section_callback_tree_to_list (ptr, tree->left, output);
 
-  lang_add_section (&ptr->children, tree->section,
+  lang_add_section (&ptr->children, tree->section, NULL,
                    (lang_output_section_statement_type *) output);
 
   if (tree->right)
@@ -664,7 +731,7 @@ walk_wild_section (lang_wild_statement_type *ptr,
                   callback_t callback,
                   void *data)
 {
-  if (file->just_syms_flag)
+  if (file->flags.just_syms)
     return;
 
   (*ptr->walk_wild_section_handler) (ptr, file, callback, data);
@@ -795,7 +862,7 @@ walk_wild_file (lang_wild_statement_type *s,
                void *data)
 {
   if (f->the_bfd == NULL
-      || ! bfd_check_format (f->the_bfd, bfd_archive))
+      || !bfd_check_format (f->the_bfd, bfd_archive))
     walk_wild_section (s, f, callback, data);
   else
     {
@@ -813,7 +880,9 @@ walk_wild_file (lang_wild_statement_type *s,
             lang_input_statement.  */
          if (member->usrdata != NULL)
            {
-             walk_wild_section (s, member->usrdata, callback, data);
+             walk_wild_section (s,
+                                (lang_input_statement_type *) member->usrdata,
+                                callback, data);
            }
 
          member = bfd_openr_next_archived_file (f->the_bfd, member);
@@ -863,9 +932,10 @@ walk_wild (lang_wild_statement_type *s, callback_t callback, void *data)
 }
 
 /* lang_for_each_statement walks the parse tree and calls the provided
-   function for each node.  */
+   function for each node, except those inside output section statements
+   with constraint set to -1.  */
 
-static void
+void
 lang_for_each_statement_worker (void (*func) (lang_statement_union_type *),
                                lang_statement_union_type *s)
 {
@@ -879,8 +949,9 @@ lang_for_each_statement_worker (void (*func) (lang_statement_union_type *),
          lang_for_each_statement_worker (func, constructor_list.head);
          break;
        case lang_output_section_statement_enum:
-         lang_for_each_statement_worker
-           (func, s->output_section_statement.children.head);
+         if (s->output_section_statement.constraint != -1)
+           lang_for_each_statement_worker
+             (func, s->output_section_statement.children.head);
          break;
        case lang_wild_statement_enum:
          lang_for_each_statement_worker (func,
@@ -925,6 +996,23 @@ lang_list_init (lang_statement_list_type *list)
   list->tail = &list->head;
 }
 
+void
+push_stat_ptr (lang_statement_list_type *new_ptr)
+{
+  if (stat_save_ptr >= stat_save + sizeof (stat_save) / sizeof (stat_save[0]))
+    abort ();
+  *stat_save_ptr++ = stat_ptr;
+  stat_ptr = new_ptr;
+}
+
+void
+pop_stat_ptr (void)
+{
+  if (stat_save_ptr <= stat_save)
+    abort ();
+  stat_ptr = *--stat_save_ptr;
+}
+
 /* Build a new statement node for the parse tree.  */
 
 static lang_statement_union_type *
@@ -932,13 +1020,13 @@ new_statement (enum statement_enum type,
               size_t size,
               lang_statement_list_type *list)
 {
-  lang_statement_union_type *new;
+  lang_statement_union_type *new_stmt;
 
-  new = stat_alloc (size);
-  new->header.type = type;
-  new->header.next = NULL;
-  lang_statement_append (list, new, &new->header.next);
-  return new;
+  new_stmt = (lang_statement_union_type *) stat_alloc (size);
+  new_stmt->header.type = type;
+  new_stmt->header.next = NULL;
+  lang_statement_append (list, new_stmt, &new_stmt->header.next);
+  return new_stmt;
 }
 
 /* Build a new input file node for the language.  There are several
@@ -958,90 +1046,72 @@ new_afile (const char *name,
 {
   lang_input_statement_type *p;
 
+  lang_has_input_file = TRUE;
+
   if (add_to_list)
-    p = new_stat (lang_input_statement, stat_ptr);
+    p = (lang_input_statement_type *) new_stat (lang_input_statement, stat_ptr);
   else
     {
-      p = stat_alloc (sizeof (lang_input_statement_type));
+      p = (lang_input_statement_type *)
+         stat_alloc (sizeof (lang_input_statement_type));
       p->header.type = lang_input_statement_enum;
       p->header.next = NULL;
     }
 
-  lang_has_input_file = TRUE;
+  memset (&p->the_bfd, 0,
+         sizeof (*p) - offsetof (lang_input_statement_type, the_bfd));
   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;
-    }
+  p->flags.dynamic = input_flags.dynamic;
+  p->flags.add_DT_NEEDED_for_dynamic = input_flags.add_DT_NEEDED_for_dynamic;
+  p->flags.add_DT_NEEDED_for_regular = input_flags.add_DT_NEEDED_for_regular;
+  p->flags.whole_archive = input_flags.whole_archive;
+  p->flags.sysrooted = input_flags.sysrooted;
 
   switch (file_type)
     {
     case lang_input_file_is_symbols_only_enum:
       p->filename = name;
-      p->is_archive = FALSE;
-      p->real = TRUE;
       p->local_sym_name = name;
-      p->just_syms_flag = TRUE;
-      p->search_dirs_flag = FALSE;
+      p->flags.real = TRUE;
+      p->flags.just_syms = TRUE;
       break;
     case lang_input_file_is_fake_enum:
       p->filename = name;
-      p->is_archive = FALSE;
-      p->real = FALSE;
       p->local_sym_name = name;
-      p->just_syms_flag = FALSE;
-      p->search_dirs_flag = FALSE;
       break;
     case lang_input_file_is_l_enum:
-      p->is_archive = TRUE;
-      p->filename = name;
-      p->real = TRUE;
+      if (name[0] == ':' && name[1] != '\0')
+       {
+         p->filename = name + 1;
+         p->flags.full_name_provided = TRUE;
+       }
+      else
+       p->filename = name;
       p->local_sym_name = concat ("-l", name, (const char *) NULL);
-      p->just_syms_flag = FALSE;
-      p->search_dirs_flag = TRUE;
+      p->flags.maybe_archive = TRUE;
+      p->flags.real = TRUE;
+      p->flags.search_dirs = TRUE;
       break;
     case lang_input_file_is_marker_enum:
       p->filename = name;
-      p->is_archive = FALSE;
-      p->real = FALSE;
       p->local_sym_name = name;
-      p->just_syms_flag = FALSE;
-      p->search_dirs_flag = TRUE;
+      p->flags.search_dirs = TRUE;
       break;
     case lang_input_file_is_search_file_enum:
-      p->sysrooted = ldlang_sysrooted_script;
       p->filename = name;
-      p->is_archive = FALSE;
-      p->real = TRUE;
       p->local_sym_name = name;
-      p->just_syms_flag = FALSE;
-      p->search_dirs_flag = TRUE;
+      p->flags.real = TRUE;
+      p->flags.search_dirs = TRUE;
       break;
     case lang_input_file_is_file_enum:
       p->filename = name;
-      p->is_archive = FALSE;
-      p->real = TRUE;
       p->local_sym_name = name;
-      p->just_syms_flag = FALSE;
-      p->search_dirs_flag = FALSE;
+      p->flags.real = TRUE;
       break;
     default:
       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;
-  p->whole_archive = whole_archive;
-  p->loaded = FALSE;
+
   lang_statement_append (&input_file_chain,
                         (lang_statement_union_type *) p,
                         &p->next_real_file);
@@ -1053,6 +1123,26 @@ lang_add_input_file (const char *name,
                     lang_input_file_enum_type file_type,
                     const char *target)
 {
+  if (name != NULL && *name == '=')
+    {
+      lang_input_statement_type *ret;
+      char *sysrooted_name
+       = concat (ld_sysroot, name + 1, (const char *) NULL);
+
+      /* We've now forcibly prepended the sysroot, making the input
+        file independent of the context.  Therefore, temporarily
+        force a non-sysrooted context for this statement, so it won't
+        get the sysroot prepended again when opened.  (N.B. if it's a
+        script, any child nodes with input files starting with "/"
+        will be handled as "sysrooted" as they'll be found to be
+        within the sysroot subdirectory.)  */
+      unsigned int outer_sysrooted = input_flags.sysrooted;
+      input_flags.sysrooted = 0;
+      ret = new_afile (sysrooted_name, file_type, target, TRUE);
+      input_flags.sysrooted = outer_sysrooted;
+      return ret;
+    }
+
   return new_afile (name, file_type, target, TRUE);
 }
 
@@ -1079,7 +1169,8 @@ output_section_statement_newfunc (struct bfd_hash_entry *entry,
 
   if (entry == NULL)
     {
-      entry = bfd_hash_allocate (table, sizeof (*ret));
+      entry = (struct bfd_hash_entry *) bfd_hash_allocate (table,
+                                                          sizeof (*ret));
       if (entry == NULL)
        return entry;
     }
@@ -1151,21 +1242,12 @@ 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;
 
-  /* The value "3" is ad-hoc, somewhat related to the expected number of
-     DEFINED expressions in a linker script.  For most default linker
-     scripts, there are none.  Why a hash table then?  Well, it's somewhat
-     simpler to re-use working machinery than using a linked list in terms
-     of code-complexity here in ld, besides the initialization which just
-     looks like other code here.  */
-  if (!bfd_hash_table_init_n (&lang_definedness_table,
-                             lang_definedness_newfunc,
-                             sizeof (struct lang_definedness_hash_entry),
-                             3))
-    einfo (_("%P%F: can not create hash table: %E\n"));
+  asneeded_list_head = NULL;
+  asneeded_list_tail = &asneeded_list_head;
 }
 
 void
@@ -1188,7 +1270,13 @@ lang_finish (void)
   In this case it is probably an error to create a region that has
   already been created.  If we are not inside a MEMORY block it is
   dubious to use an undeclared region name (except DEFAULT_MEMORY_REGION)
-  and so we issue a warning.  */
+  and so we issue a warning.
+
+  Each region has at least one name.  The first name is either
+  DEFAULT_MEMORY_REGION or the name given in the MEMORY block.  You can add
+  alias names to an existing region within a script with
+  REGION_ALIAS (alias, region_name).  Each name corresponds to at most one
+  region.  */
 
 static lang_memory_region_type *lang_memory_region_list;
 static lang_memory_region_type **lang_memory_region_list_tail
@@ -1197,41 +1285,89 @@ static lang_memory_region_type **lang_memory_region_list_tail
 lang_memory_region_type *
 lang_memory_region_lookup (const char *const name, bfd_boolean create)
 {
-  lang_memory_region_type *p;
-  lang_memory_region_type *new;
+  lang_memory_region_name *n;
+  lang_memory_region_type *r;
+  lang_memory_region_type *new_region;
 
   /* NAME is NULL for LMA memspecs if no region was specified.  */
   if (name == NULL)
     return NULL;
 
-  for (p = lang_memory_region_list; p != NULL; p = p->next)
-    if (strcmp (p->name, name) == 0)
-      {
-       if (create)
-         einfo (_("%P:%S: warning: redeclaration of memory region '%s'\n"),
-                name);
-       return p;
-      }
+  for (r = lang_memory_region_list; r != NULL; r = r->next)
+    for (n = &r->name_list; n != NULL; n = n->next)
+      if (strcmp (n->name, name) == 0)
+       {
+         if (create)
+           einfo (_("%P:%S: warning: redeclaration of memory region `%s'\n"),
+                  NULL, name);
+         return r;
+       }
 
   if (!create && strcmp (name, DEFAULT_MEMORY_REGION))
-    einfo (_("%P:%S: warning: memory region %s not declared\n"), name);
+    einfo (_("%P:%S: warning: memory region `%s' not declared\n"),
+          NULL, name);
+
+  new_region = (lang_memory_region_type *)
+      stat_alloc (sizeof (lang_memory_region_type));
+
+  new_region->name_list.name = xstrdup (name);
+  new_region->name_list.next = NULL;
+  new_region->next = NULL;
+  new_region->origin_exp = NULL;
+  new_region->origin = 0;
+  new_region->length_exp = NULL;
+  new_region->length = ~(bfd_size_type) 0;
+  new_region->current = 0;
+  new_region->last_os = NULL;
+  new_region->flags = 0;
+  new_region->not_flags = 0;
+  new_region->had_full_message = FALSE;
+
+  *lang_memory_region_list_tail = new_region;
+  lang_memory_region_list_tail = &new_region->next;
+
+  return new_region;
+}
 
-  new = stat_alloc (sizeof (lang_memory_region_type));
+void
+lang_memory_region_alias (const char *alias, const char *region_name)
+{
+  lang_memory_region_name *n;
+  lang_memory_region_type *r;
+  lang_memory_region_type *region;
 
-  new->name = xstrdup (name);
-  new->next = NULL;
-  new->origin = 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;
+  /* The default region must be unique.  This ensures that it is not necessary
+     to iterate through the name list if someone wants the check if a region is
+     the default memory region.  */
+  if (strcmp (region_name, DEFAULT_MEMORY_REGION) == 0
+      || strcmp (alias, DEFAULT_MEMORY_REGION) == 0)
+    einfo (_("%F%P:%S: error: alias for default memory region\n"), NULL);
+
+  /* Look for the target region and check if the alias is not already
+     in use.  */
+  region = NULL;
+  for (r = lang_memory_region_list; r != NULL; r = r->next)
+    for (n = &r->name_list; n != NULL; n = n->next)
+      {
+       if (region == NULL && strcmp (n->name, region_name) == 0)
+         region = r;
+       if (strcmp (n->name, alias) == 0)
+         einfo (_("%F%P:%S: error: redefinition of memory region "
+                  "alias `%s'\n"),
+                NULL, alias);
+      }
 
-  *lang_memory_region_list_tail = new;
-  lang_memory_region_list_tail = &new->next;
+  /* Check if the target region exists.  */
+  if (region == NULL)
+    einfo (_("%F%P:%S: error: memory region `%s' "
+            "for alias `%s' does not exist\n"),
+          NULL, region_name, alias);
 
-  return new;
+  /* Add alias to region name list.  */
+  n = (lang_memory_region_name *) stat_alloc (sizeof (lang_memory_region_name));
+  n->name = xstrdup (alias);
+  n->next = region->name_list.next;
+  region->name_list.next = n;
 }
 
 static lang_memory_region_type *
@@ -1256,45 +1392,33 @@ lang_memory_default (asection *section)
   return lang_memory_region_lookup (DEFAULT_MEMORY_REGION, FALSE);
 }
 
+/* Get the output section statement directly from the userdata.  */
+
 lang_output_section_statement_type *
-lang_output_section_find (const char *const name)
+lang_output_section_get (const asection *output_section)
 {
-  struct out_section_hash_entry *entry;
-  unsigned long hash;
-
-  entry = ((struct out_section_hash_entry *)
-          bfd_hash_lookup (&output_section_statement_table, name,
-                           FALSE, FALSE));
-  if (entry == NULL)
-    return NULL;
-
-  hash = entry->root.hash;
-  do
-    {
-      if (entry->s.output_section_statement.constraint != -1)
-       return &entry->s.output_section_statement;
-      entry = (struct out_section_hash_entry *) entry->root.next;
-    }
-  while (entry != NULL
-        && entry->root.hash == hash
-        && strcmp (name, entry->s.output_section_statement.name) == 0);
-
-  return NULL;
+  return get_userdata (output_section);
 }
 
-static lang_output_section_statement_type *
-lang_output_section_statement_lookup_1 (const char *const name, int constraint)
+/* Find or create an output_section_statement with the given NAME.
+   If CONSTRAINT is non-zero match one with that constraint, otherwise
+   match any non-negative constraint.  If CREATE, always make a
+   new output_section_statement for SPECIAL CONSTRAINT.  */
+
+lang_output_section_statement_type *
+lang_output_section_statement_lookup (const char *name,
+                                     int constraint,
+                                     bfd_boolean create)
 {
   struct out_section_hash_entry *entry;
-  struct out_section_hash_entry *last_ent;
-  unsigned long hash;
 
   entry = ((struct out_section_hash_entry *)
           bfd_hash_lookup (&output_section_statement_table, name,
-                           TRUE, FALSE));
+                           create, 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;
     }
 
@@ -1302,20 +1426,29 @@ lang_output_section_statement_lookup_1 (const char *const name, int constraint)
     {
       /* We have a section of this name, but it might not have the correct
         constraint.  */
-      hash = entry->root.hash;
-      do
-       {
-         if (entry->s.output_section_statement.constraint != -1
-             && (constraint == 0
-                 || (constraint == entry->s.output_section_statement.constraint
-                     && constraint != SPECIAL)))
-           return &entry->s.output_section_statement;
-         last_ent = entry;
-         entry = (struct out_section_hash_entry *) entry->root.next;
-       }
-      while (entry != NULL
-            && entry->root.hash == hash
-            && strcmp (name, entry->s.output_section_statement.name) == 0);
+      struct out_section_hash_entry *last_ent;
+
+      name = entry->s.output_section_statement.name;
+      if (create && constraint == SPECIAL)
+       /* Not traversing to the end reverses the order of the second
+          and subsequent SPECIAL sections in the hash table chain,
+          but that shouldn't matter.  */
+       last_ent = entry;
+      else
+       do
+         {
+           if (constraint == entry->s.output_section_statement.constraint
+               || (constraint == 0
+                   && entry->s.output_section_statement.constraint >= 0))
+             return &entry->s.output_section_statement;
+           last_ent = entry;
+           entry = (struct out_section_hash_entry *) entry->root.next;
+         }
+       while (entry != NULL
+              && name == entry->s.output_section_statement.name);
+
+      if (!create)
+       return NULL;
 
       entry
        = ((struct out_section_hash_entry *)
@@ -1336,10 +1469,34 @@ lang_output_section_statement_lookup_1 (const char *const name, int constraint)
   return &entry->s.output_section_statement;
 }
 
+/* Find the next output_section_statement with the same name as OS.
+   If CONSTRAINT is non-zero, find one with that constraint otherwise
+   match any non-negative constraint.  */
+
 lang_output_section_statement_type *
-lang_output_section_statement_lookup (const char *const name)
+next_matching_output_section_statement (lang_output_section_statement_type *os,
+                                       int constraint)
 {
-  return lang_output_section_statement_lookup_1 (name, 0);
+  /* All output_section_statements are actually part of a
+     struct out_section_hash_entry.  */
+  struct out_section_hash_entry *entry = (struct out_section_hash_entry *)
+    ((char *) os
+     - offsetof (struct out_section_hash_entry, s.output_section_statement));
+  const char *name = os->name;
+
+  ASSERT (name == entry->root.string);
+  do
+    {
+      entry = (struct out_section_hash_entry *) entry->root.next;
+      if (entry == NULL
+         || name != entry->s.output_section_statement.name)
+       return NULL;
+    }
+  while (constraint != entry->s.output_section_statement.constraint
+        && (constraint != 0
+            || entry->s.output_section_statement.constraint < 0));
+
+  return &entry->s.output_section_statement;
 }
 
 /* A variant of lang_output_section_find used by place_orphan.
@@ -1349,11 +1506,12 @@ lang_output_section_statement_lookup (const char *const name)
 
 lang_output_section_statement_type *
 lang_output_section_find_by_flags (const asection *sec,
+                                  flagword sec_flags,
                                   lang_output_section_statement_type **exact,
                                   lang_match_sec_type_func match_type)
 {
   lang_output_section_statement_type *first, *look, *found;
-  flagword flags;
+  flagword look_flags, differ;
 
   /* We know the first statement on this list is *ABS*.  May as well
      skip it.  */
@@ -1364,18 +1522,18 @@ lang_output_section_find_by_flags (const asection *sec,
   found = NULL;
   for (look = first; look; look = look->next)
     {
-      flags = look->flags;
+      look_flags = look->flags;
       if (look->bfd_section != NULL)
        {
-         flags = look->bfd_section->flags;
+         look_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_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY
-                    | SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
+      differ = look_flags ^ sec_flags;
+      if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY
+                     | SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
        found = look;
     }
   if (found != NULL)
@@ -1385,113 +1543,166 @@ lang_output_section_find_by_flags (const asection *sec,
       return found;
     }
 
-  if (sec->flags & SEC_CODE)
+  if ((sec_flags & SEC_CODE) != 0
+      && (sec_flags & SEC_ALLOC) != 0)
     {
       /* Try for a rw code section.  */
       for (look = first; look; look = look->next)
        {
-         flags = look->flags;
+         look_flags = look->flags;
          if (look->bfd_section != NULL)
            {
-             flags = look->bfd_section->flags;
+             look_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_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
-                        | SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
+         differ = look_flags ^ sec_flags;
+         if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+                         | SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
            found = look;
        }
     }
-  else if (sec->flags & (SEC_READONLY | SEC_THREAD_LOCAL))
+  else if ((sec_flags & SEC_READONLY) != 0
+          && (sec_flags & SEC_ALLOC) != 0)
     {
       /* .rodata can go after .text, .sdata2 after .rodata.  */
       for (look = first; look; look = look->next)
        {
-         flags = look->flags;
+         look_flags = look->flags;
          if (look->bfd_section != NULL)
            {
-             flags = look->bfd_section->flags;
+             look_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_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
-                        | SEC_READONLY))
-             && !(look->flags & (SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
+         differ = look_flags ^ sec_flags;
+         if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+                         | SEC_READONLY | SEC_SMALL_DATA))
+             || (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+                             | SEC_READONLY))
+                 && !(look_flags & SEC_SMALL_DATA)))
+           found = look;
+       }
+    }
+  else if ((sec_flags & SEC_THREAD_LOCAL) != 0
+          && (sec_flags & SEC_ALLOC) != 0)
+    {
+      /* .tdata can go after .data, .tbss after .tdata.  Treat .tbss
+        as if it were a loaded section, and don't use match_type.  */
+      bfd_boolean seen_thread_local = FALSE;
+
+      match_type = NULL;
+      for (look = first; look; look = look->next)
+       {
+         look_flags = look->flags;
+         if (look->bfd_section != NULL)
+           look_flags = look->bfd_section->flags;
+
+         differ = look_flags ^ (sec_flags | SEC_LOAD | SEC_HAS_CONTENTS);
+         if (!(differ & (SEC_THREAD_LOCAL | SEC_ALLOC)))
+           {
+             /* .tdata and .tbss must be adjacent and in that order.  */
+             if (!(look_flags & SEC_LOAD)
+                 && (sec_flags & SEC_LOAD))
+               /* ..so if we're at a .tbss section and we're placing
+                  a .tdata section stop looking and return the
+                  previous section.  */
+               break;
+             found = look;
+             seen_thread_local = TRUE;
+           }
+         else if (seen_thread_local)
+           break;
+         else if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD)))
            found = look;
        }
     }
-  else if (sec->flags & SEC_SMALL_DATA)
+  else if ((sec_flags & SEC_SMALL_DATA) != 0
+          && (sec_flags & SEC_ALLOC) != 0)
     {
       /* .sdata goes after .data, .sbss after .sdata.  */
       for (look = first; look; look = look->next)
        {
-         flags = look->flags;
+         look_flags = look->flags;
          if (look->bfd_section != NULL)
            {
-             flags = look->bfd_section->flags;
+             look_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_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
-                        | SEC_THREAD_LOCAL))
-             || ((look->flags & SEC_SMALL_DATA)
-                 && !(sec->flags & SEC_HAS_CONTENTS)))
+         differ = look_flags ^ sec_flags;
+         if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+                         | SEC_THREAD_LOCAL))
+             || ((look_flags & SEC_SMALL_DATA)
+                 && !(sec_flags & SEC_HAS_CONTENTS)))
            found = look;
        }
     }
-  else if (sec->flags & SEC_HAS_CONTENTS)
+  else if ((sec_flags & SEC_HAS_CONTENTS) != 0
+          && (sec_flags & SEC_ALLOC) != 0)
     {
       /* .data goes after .rodata.  */
       for (look = first; look; look = look->next)
        {
-         flags = look->flags;
+         look_flags = look->flags;
          if (look->bfd_section != NULL)
            {
-             flags = look->bfd_section->flags;
+             look_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_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
-                        | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
+         differ = look_flags ^ sec_flags;
+         if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+                         | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
            found = look;
        }
     }
-  else
+  else if ((sec_flags & SEC_ALLOC) != 0)
     {
-      /* .bss goes last.  */
+      /* .bss goes after any other alloc section.  */
       for (look = first; look; look = look->next)
        {
-         flags = look->flags;
+         look_flags = look->flags;
          if (look->bfd_section != NULL)
            {
-             flags = look->bfd_section->flags;
+             look_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))
+         differ = look_flags ^ sec_flags;
+         if (!(differ & SEC_ALLOC))
+           found = look;
+       }
+    }
+  else
+    {
+      /* non-alloc go last.  */
+      for (look = first; look; look = look->next)
+       {
+         look_flags = look->flags;
+         if (look->bfd_section != NULL)
+           look_flags = look->bfd_section->flags;
+         differ = look_flags ^ sec_flags;
+         if (!(differ & SEC_DEBUGGING))
            found = look;
        }
+      return found;
     }
 
   if (found || !match_type)
     return found;
 
-  return lang_output_section_find_by_flags (sec, NULL, NULL);
+  return lang_output_section_find_by_flags (sec, sec_flags, NULL, NULL);
 }
 
 /* Find the last output section before given output statement.
@@ -1504,7 +1715,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)
@@ -1518,10 +1729,12 @@ output_prev_sec_find (lang_output_section_statement_type *os)
    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.  */
+   are assumed to belong to it, except in two cases;  The first
+   assignment to dot, and assignments before non-alloc sections.
+   Otherwise we might put an orphan before . = . + SIZEOF_HEADERS or
+   similar assignments that set the initial address, or we might
+   insert non-alloc note sections among assignments setting end of
+   image symbols.  */
 
 static lang_statement_union_type **
 insert_os_after (lang_output_section_statement_type *after)
@@ -1565,7 +1778,14 @@ insert_os_after (lang_output_section_statement_type *after)
          continue;
        case lang_output_section_statement_enum:
          if (assign != NULL)
-           where = assign;
+           {
+             asection *s = (*where)->output_section_statement.bfd_section;
+
+             if (s == NULL
+                 || s->map_head.s == NULL
+                 || (s->flags & SEC_ALLOC) != 0)
+               where = assign;
+           }
          break;
        case lang_input_statement_enum:
        case lang_address_statement_enum:
@@ -1584,89 +1804,102 @@ insert_os_after (lang_output_section_statement_type *after)
 lang_output_section_statement_type *
 lang_insert_orphan (asection *s,
                    const char *secname,
+                   int constraint,
                    lang_output_section_statement_type *after,
                    struct orphan_save *place,
                    etree_type *address,
                    lang_statement_list_type *add_child)
 {
-  lang_statement_list_type *old;
   lang_statement_list_type add;
   const char *ps;
+  lang_assignment_statement_type *start_assign;
   lang_output_section_statement_type *os;
   lang_output_section_statement_type **os_tail;
 
-  /* Start building a list of statements for this section.
-     First save the current statement pointer.  */
-  old = stat_ptr;
-
   /* If we have found an appropriate place for the output section
      statements for this orphan, add them to our own private list,
      inserting them later into the global statement list.  */
   if (after != NULL)
     {
-      stat_ptr = &add;
-      lang_list_init (stat_ptr);
+      lang_list_init (&add);
+      push_stat_ptr (&add);
     }
 
+  if (bfd_link_relocatable (&link_info)
+      || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0)
+    address = exp_intop (0);
+
+  os_tail = ((lang_output_section_statement_type **)
+            lang_output_section_statement.tail);
+  os = lang_enter_output_section_statement (secname, address, normal_section,
+                                           NULL, NULL, NULL, constraint, 0);
+
   ps = NULL;
-  if (config.build_constructors)
+  start_assign = NULL;
+  if (config.build_constructors && *os_tail == os)
     {
       /* If the name of the section is representable in C, then create
         symbols to mark the start and the end of the section.  */
       for (ps = secname; *ps != '\0'; ps++)
-       if (! ISALNUM ((unsigned char) *ps) && *ps != '_')
+       if (!ISALNUM ((unsigned char) *ps) && *ps != '_')
          break;
       if (*ps == '\0')
        {
          char *symname;
-         etree_type *e_align;
 
          symname = (char *) xmalloc (ps - secname + sizeof "__start_" + 1);
          symname[0] = bfd_get_symbol_leading_char (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_provide (symname,
-                                           exp_nameop (NAME, "."),
-                                           FALSE));
+         start_assign
+           = 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);
-
-  os_tail = ((lang_output_section_statement_type **)
-            lang_output_section_statement.tail);
-  os = lang_enter_output_section_statement (secname, address, 0, NULL, NULL,
-                                           NULL, 0);
-
   if (add_child == NULL)
     add_child = &os->children;
-  lang_add_section (add_child, s, os);
+  lang_add_section (add_child, s, NULL, os);
 
-  lang_leave_output_section_statement (0, "*default*", NULL, NULL);
+  if (after && (s->flags & (SEC_LOAD | SEC_ALLOC)) != 0)
+    {
+      const char *region = (after->region
+                           ? after->region->name_list.name
+                           : DEFAULT_MEMORY_REGION);
+      const char *lma_region = (after->lma_region
+                               ? after->lma_region->name_list.name
+                               : NULL);
+      lang_leave_output_section_statement (NULL, region, after->phdrs,
+                                          lma_region);
+    }
+  else
+    lang_leave_output_section_statement (NULL, DEFAULT_MEMORY_REGION, NULL,
+                                        NULL);
 
-  if (config.build_constructors && *ps == '\0')
+  if (start_assign != NULL)
     {
       char *symname;
-
-      /* lang_leave_ouput_section_statement resets stat_ptr.
-        Put stat_ptr back where we want it.  */
-      if (after != NULL)
-       stat_ptr = &add;
+      lang_assignment_statement_type *stop_assign;
+      bfd_vma dot;
 
       symname = (char *) xmalloc (ps - secname + sizeof "__stop_" + 1);
       symname[0] = bfd_get_symbol_leading_char (link_info.output_bfd);
       sprintf (symname + (symname[0] != 0), "__stop_%s", secname);
-      lang_add_assignment (exp_provide (symname,
-                                       exp_nameop (NAME, "."),
-                                       FALSE));
+      stop_assign
+       = lang_add_assignment (exp_provide (symname,
+                                           exp_nameop (NAME, "."),
+                                           FALSE));
+      /* Evaluate the expression to define the symbol if referenced,
+        before sizing dynamic sections.  */
+      dot = os->bfd_section->vma;
+      exp_fold_tree (start_assign->exp, os->bfd_section, &dot);
+      dot += TO_ADDR (s->size);
+      exp_fold_tree (stop_assign->exp, os->bfd_section, &dot);
     }
 
   /* Restore the global list pointer.  */
   if (after != NULL)
-    stat_ptr = old;
+    pop_stat_ptr ();
 
   if (after != NULL && os->bfd_section != NULL)
     {
@@ -1752,8 +1985,8 @@ lang_insert_orphan (asection *s,
 
          /* Fix the global list pointer if we happened to tack our
             new list at the tail.  */
-         if (*old->tail == add.head)
-           old->tail = add.tail;
+         if (*stat_ptr->tail == add.head)
+           stat_ptr->tail = add.tail;
 
          /* Save the end of this list.  */
          place->stmt = add.tail;
@@ -1783,6 +2016,43 @@ lang_insert_orphan (asection *s,
   return os;
 }
 
+static void
+lang_print_asneeded (void)
+{
+  struct asneeded_minfo *m;
+  char buf[100];
+
+  if (asneeded_list_head == NULL)
+    return;
+
+  sprintf (buf, _("\nAs-needed library included "
+                 "to satisfy reference by file (symbol)\n\n"));
+  minfo ("%s", buf);
+
+  for (m = asneeded_list_head; m != NULL; m = m->next)
+    {
+      size_t len;
+
+      minfo ("%s", m->soname);
+      len = strlen (m->soname);
+
+      if (len >= 29)
+       {
+         print_nl ();
+         len = 0;
+       }
+      while (len < 30)
+       {
+         print_space ();
+         ++len;
+       }
+
+      if (m->ref != NULL)
+       minfo ("%B ", m->ref);
+      minfo ("(%T)\n", m->name);
+    }
+}
+
 static void
 lang_map_flags (flagword flag)
 {
@@ -1807,14 +2077,13 @@ 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)
+         || file->flags.just_syms)
        continue;
 
       for (s = file->the_bfd->sections; s != NULL; s = s->next)
@@ -1822,13 +2091,13 @@ lang_map (void)
             || s->output_section->owner != link_info.output_bfd)
            && (s->flags & (SEC_LINKER_CREATED | SEC_KEEP)) == 0)
          {
-           if (! dis_header_printed)
+           if (!dis_header_printed)
              {
                fprintf (config.map_file, _("\nDiscarded input sections\n\n"));
                dis_header_printed = TRUE;
              }
 
-           print_input_section (s);
+           print_input_section (s, TRUE);
          }
     }
 
@@ -1841,7 +2110,7 @@ lang_map (void)
       char buf[100];
       int len;
 
-      fprintf (config.map_file, "%-16s ", m->name);
+      fprintf (config.map_file, "%-16s ", m->name_list.name);
 
       sprintf_vma (buf, m->origin);
       minfo ("0x%s ", buf);
@@ -1876,56 +2145,47 @@ lang_map (void)
 
   fprintf (config.map_file, _("\nLinker script and memory map\n\n"));
 
-  if (! link_info.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 ++;
+  lang_statement_iteration++;
   print_statements ();
-}
 
-static void
-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
-                                     (sizeof (fat_section_userdata_type))));
-
-  ASSERT (get_userdata (sec) == NULL);
-  get_userdata (sec) = new_data;
-  new_data->map_symbol_def_tail = &new_data->map_symbol_def_head;
+  ldemul_extra_map_file_text (link_info.output_bfd, &link_info,
+                             config.map_file);
 }
 
 static bfd_boolean
 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)
+  if ((hash_entry->type == bfd_link_hash_defined
+       || hash_entry->type == bfd_link_hash_defweak)
+      && hash_entry->u.def.section->owner != link_info.output_bfd
+      && hash_entry->u.def.section->owner != NULL)
     {
-      struct fat_user_section_struct *ud;
+      input_section_userdata_type *ud;
       struct map_symbol_def *def;
 
-      ud = get_userdata (hash_entry->u.def.section);
-      if  (! ud)
+      ud = ((input_section_userdata_type *)
+           get_userdata (hash_entry->u.def.section));
+      if (!ud)
        {
-         /* ??? What do we have to do to initialize this beforehand?  */
-         /* The first time we get here is bfd_abs_section...  */
-         init_map_userdata (0, hash_entry->u.def.section, 0);
-         ud = get_userdata (hash_entry->u.def.section);
+         ud = (input_section_userdata_type *) stat_alloc (sizeof (*ud));
+         get_userdata (hash_entry->u.def.section) = ud;
+         ud->map_symbol_def_tail = &ud->map_symbol_def_head;
+         ud->map_symbol_def_count = 0;
        }
-      else if  (!ud->map_symbol_def_tail)
+      else if (!ud->map_symbol_def_tail)
        ud->map_symbol_def_tail = &ud->map_symbol_def_head;
 
-      def = obstack_alloc (&map_obstack, sizeof *def);
+      def = (struct map_symbol_def *) obstack_alloc (&map_obstack, sizeof *def);
       def->entry = hash_entry;
       *(ud->map_symbol_def_tail) = def;
       ud->map_symbol_def_tail = &def->next;
+      ud->map_symbol_def_count++;
     }
   return TRUE;
 }
@@ -1933,34 +2193,28 @@ sort_def_symbol (struct bfd_link_hash_entry *hash_entry,
 /* Initialize an output section.  */
 
 static void
-init_os (lang_output_section_statement_type *s, asection *isec,
-        flagword flags)
+init_os (lang_output_section_statement_type *s, flagword flags)
 {
-  if (s->bfd_section != NULL)
-    return;
-
   if (strcmp (s->name, DISCARD_SECTION_NAME) == 0)
     einfo (_("%P%F: Illegal use of `%s' section\n"), DISCARD_SECTION_NAME);
 
-  s->bfd_section = bfd_get_section_by_name (link_info.output_bfd, s->name);
+  if (s->constraint != SPECIAL)
+    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_with_flags (link_info.output_bfd,
-                                                 s->name, flags);
+    s->bfd_section = bfd_make_section_anyway_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"),
+      einfo (_("%P%F: output format %s cannot represent section"
+              " called %s: %E\n"),
             link_info.output_bfd->xvec->name, s->name);
     }
   s->bfd_section->output_section = s->bfd_section;
   s->bfd_section->output_offset = 0;
 
-  if (!link_info.reduce_memory_overheads)
-    {
-      fat_section_userdata_type *new
-       = stat_alloc (sizeof (fat_section_userdata_type));
-      memset (new, 0, sizeof (fat_section_userdata_type));
-      get_userdata (s->bfd_section) = new;
-    }
+  /* Set the userdata of the output section to the output section
+     statement to avoid lookup.  */
+  get_userdata (s->bfd_section) = s;
 
   /* If there is a base address, make sure that any sections it might
      mention are initialized.  */
@@ -1973,11 +2227,6 @@ init_os (lang_output_section_statement_type *s, asection *isec,
   /* If supplied an alignment, set it.  */
   if (s->section_alignment != -1)
     s->bfd_section->alignment_power = s->section_alignment;
-
-  if (isec)
-    bfd_init_private_section_data (isec->owner, isec,
-                                  link_info.output_bfd, s->bfd_section,
-                                  &link_info);
 }
 
 /* Make sure that all output sections mentioned in an expression are
@@ -2023,7 +2272,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, 0);
+             init_os (os, 0);
          }
        }
       break;
@@ -2036,11 +2285,11 @@ exp_init_os (etree_type *exp)
 static void
 section_already_linked (bfd *abfd, asection *sec, void *data)
 {
-  lang_input_statement_type *entry = data;
+  lang_input_statement_type *entry = (lang_input_statement_type *) data;
 
   /* If we are only reading symbols from this object, then we want to
      discard all sections.  */
-  if (entry->just_syms_flag)
+  if (entry->flags.just_syms)
     {
       bfd_link_just_syms (abfd, sec, &link_info);
       return;
@@ -2057,16 +2306,19 @@ section_already_linked (bfd *abfd, asection *sec, void *data)
    foo.o(.text, .data).  */
 
 /* Add SECTION to the output section OUTPUT.  Do this by creating a
-   lang_input_section statement which is placed at PTR.  FILE is the
-   input file which holds SECTION.  */
+   lang_input_section statement which is placed at PTR.  */
 
 void
 lang_add_section (lang_statement_list_type *ptr,
                  asection *section,
+                 struct flag_info *sflag_info,
                  lang_output_section_statement_type *output)
 {
   flagword flags = section->flags;
+
   bfd_boolean discard;
+  lang_input_section_type *new_section;
+  bfd *abfd = link_info.output_bfd;
 
   /* Discard sections marked with SEC_EXCLUDE.  */
   discard = (flags & SEC_EXCLUDE) != 0;
@@ -2092,113 +2344,121 @@ lang_add_section (lang_statement_list_type *ptr,
       return;
     }
 
-  if (section->output_section == NULL)
+  if (sflag_info)
     {
-      bfd_boolean first;
-      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;
+      bfd_boolean keep;
 
-      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, flags);
+      keep = bfd_lookup_section_flags (&link_info, sflag_info, section);
+      if (!keep)
+       return;
+    }
 
-      first = ! output->bfd_section->linker_has_input;
-      output->bfd_section->linker_has_input = 1;
+  if (section->output_section != NULL)
+    return;
 
-      if (!link_info.relocatable
-         && !stripped_excluded_sections)
-       {
-         asection *s = output->bfd_section->map_tail.s;
-         output->bfd_section->map_tail.s = section;
-         section->map_head.s = NULL;
-         section->map_tail.s = s;
-         if (s != NULL)
-           s->map_head.s = section;
-         else
-           output->bfd_section->map_head.s = section;
-       }
+  /* 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;
 
-      /* Add a section reference to the list.  */
-      new = new_stat (lang_input_section, ptr);
+  /* 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
+     to see .text with SEC_LINK_ONCE set.  */
 
-      new->section = section;
-      section->output_section = output->bfd_section;
+  if (!bfd_link_relocatable (&link_info))
+    flags &= ~(SEC_LINK_ONCE | SEC_LINK_DUPLICATES | SEC_RELOC);
 
-      /* 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
-        to see .text with SEC_LINK_ONCE set.  */
+  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;
+      /* Unfortunately GNU ld has managed to evolve two different
+        meanings to NOLOAD in scripts.  ELF gets a .bss style noload,
+        alloc, no contents section.  All others get a noload, noalloc
+        section.  */
+      if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour)
+       flags &= ~SEC_HAS_CONTENTS;
+      else
+       flags &= ~SEC_ALLOC;
+      break;
+    }
 
-      if (! link_info.relocatable)
-       flags &= ~ (SEC_LINK_ONCE | SEC_LINK_DUPLICATES);
+  if (output->bfd_section == NULL)
+    init_os (output, flags);
 
-      /* If this is not the first input section, and the SEC_READONLY
-        flag is not currently set, then don't set it just because the
-        input section has it set.  */
+  /* If SEC_READONLY is not set in the input section, then clear
+     it from the output section.  */
+  output->bfd_section->flags &= flags | ~SEC_READONLY;
 
-      if (! first && (output->bfd_section->flags & SEC_READONLY) == 0)
-       flags &= ~ SEC_READONLY;
+  if (output->bfd_section->linker_has_input)
+    {
+      /* Only set SEC_READONLY flag on the first input section.  */
+      flags &= ~ SEC_READONLY;
 
       /* Keep SEC_MERGE and SEC_STRINGS only if they are the same.  */
-      if (! first
-         && ((output->bfd_section->flags & (SEC_MERGE | SEC_STRINGS))
-             != (flags & (SEC_MERGE | SEC_STRINGS))
-             || ((flags & SEC_MERGE)
-                 && output->bfd_section->entsize != section->entsize)))
+      if ((output->bfd_section->flags & (SEC_MERGE | SEC_STRINGS))
+         != (flags & (SEC_MERGE | SEC_STRINGS))
+         || ((flags & SEC_MERGE) != 0
+             && output->bfd_section->entsize != section->entsize))
        {
          output->bfd_section->flags &= ~ (SEC_MERGE | SEC_STRINGS);
          flags &= ~ (SEC_MERGE | SEC_STRINGS);
        }
+    }
+  output->bfd_section->flags |= flags;
 
-      output->bfd_section->flags |= flags;
-
-      if (flags & SEC_MERGE)
+  if (!output->bfd_section->linker_has_input)
+    {
+      output->bfd_section->linker_has_input = 1;
+      /* This must happen after flags have been updated.  The output
+        section may have been created before we saw its first input
+        section, eg. for a data statement.  */
+      bfd_init_private_section_data (section->owner, section,
+                                    link_info.output_bfd,
+                                    output->bfd_section,
+                                    &link_info);
+      if ((flags & SEC_MERGE) != 0)
        output->bfd_section->entsize = section->entsize;
+    }
 
-      /* If SEC_READONLY is not set in the input section, then clear
-        it from the output section.  */
-      if ((section->flags & SEC_READONLY) == 0)
-       output->bfd_section->flags &= ~SEC_READONLY;
+  if ((flags & SEC_TIC54X_BLOCK) != 0
+      && bfd_get_arch (section->owner) == bfd_arch_tic54x)
+    {
+      /* FIXME: This value should really be obtained from the bfd...  */
+      output->block_value = 128;
+    }
 
-      /* Copy over SEC_SMALL_DATA.  */
-      if (section->flags & SEC_SMALL_DATA)
-       output->bfd_section->flags |= SEC_SMALL_DATA;
+  if (section->alignment_power > output->bfd_section->alignment_power)
+    output->bfd_section->alignment_power = section->alignment_power;
 
-      if (section->alignment_power > output->bfd_section->alignment_power)
-       output->bfd_section->alignment_power = section->alignment_power;
+  section->output_section = output->bfd_section;
 
-      if (bfd_get_arch (section->owner) == bfd_arch_tic54x
-         && (section->flags & SEC_TIC54X_BLOCK) != 0)
-       {
-         output->bfd_section->flags |= SEC_TIC54X_BLOCK;
-         /* FIXME: This value should really be obtained from the bfd...  */
-         output->block_value = 128;
-       }
+  if (!map_head_is_link_order)
+    {
+      asection *s = output->bfd_section->map_tail.s;
+      output->bfd_section->map_tail.s = section;
+      section->map_head.s = NULL;
+      section->map_tail.s = s;
+      if (s != NULL)
+       s->map_head.s = section;
+      else
+       output->bfd_section->map_head.s = section;
     }
+
+  /* Add a section reference to the list.  */
+  new_section = new_stat (lang_input_section, ptr);
+  new_section->section = section;
 }
 
 /* Handle wildcard sorting.  This returns the lang_input_section which
@@ -2212,14 +2472,12 @@ wild_sort (lang_wild_statement_type *wild,
           lang_input_statement_type *file,
           asection *section)
 {
-  const char *section_name;
   lang_statement_union_type *l;
 
   if (!wild->filenames_sorted
       && (sec == NULL || sec->spec.sorted == none))
     return NULL;
 
-  section_name = bfd_get_section_name (file->the_bfd, section);
   for (l = wild->children.head; l != NULL; l = l->header.next)
     {
       lang_input_section_type *ls;
@@ -2243,9 +2501,9 @@ wild_sort (lang_wild_statement_type *wild,
             archive.  */
 
          if (file->the_bfd != NULL
-             && bfd_my_archive (file->the_bfd) != NULL)
+             && file->the_bfd->my_archive != NULL)
            {
-             fn = bfd_get_filename (bfd_my_archive (file->the_bfd));
+             fn = bfd_get_filename (file->the_bfd->my_archive);
              fa = TRUE;
            }
          else
@@ -2254,9 +2512,9 @@ wild_sort (lang_wild_statement_type *wild,
              fa = FALSE;
            }
 
-         if (bfd_my_archive (ls->section->owner) != NULL)
+         if (ls->section->owner->my_archive != NULL)
            {
-             ln = bfd_get_filename (bfd_my_archive (ls->section->owner));
+             ln = bfd_get_filename (ls->section->owner->my_archive);
              la = TRUE;
            }
          else
@@ -2265,7 +2523,7 @@ wild_sort (lang_wild_statement_type *wild,
              la = FALSE;
            }
 
-         i = strcmp (fn, ln);
+         i = filename_cmp (fn, ln);
          if (i > 0)
            continue;
          else if (i < 0)
@@ -2278,7 +2536,7 @@ wild_sort (lang_wild_statement_type *wild,
              if (la)
                ln = ls->section->owner->filename;
 
-             i = strcmp (fn, ln);
+             i = filename_cmp (fn, ln);
              if (i > 0)
                continue;
              else if (i < 0)
@@ -2289,7 +2547,9 @@ wild_sort (lang_wild_statement_type *wild,
       /* Here either the files are not sorted by name, or we are
         looking at the sections for this file.  */
 
-      if (sec != NULL && sec->spec.sorted != none)
+      if (sec != NULL
+         && sec->spec.sorted != none
+         && sec->spec.sorted != by_none)
        if (compare_section (sec->spec.sorted, section, ls->section) < 0)
          break;
     }
@@ -2304,13 +2564,17 @@ static void
 output_section_callback (lang_wild_statement_type *ptr,
                         struct wildcard_list *sec,
                         asection *section,
+                        struct flag_info *sflag_info,
                         lang_input_statement_type *file,
                         void *output)
 {
   lang_statement_union_type *before;
+  lang_output_section_statement_type *os;
+
+  os = (lang_output_section_statement_type *) output;
 
   /* Exclude sections that match UNIQUE_SECTION_LIST.  */
-  if (unique_section_p (section))
+  if (unique_section_p (section, os))
     return;
 
   before = wild_sort (ptr, sec, file, section);
@@ -2321,16 +2585,14 @@ output_section_callback (lang_wild_statement_type *ptr,
      of the current list.  */
 
   if (before == NULL)
-    lang_add_section (&ptr->children, section,
-                     (lang_output_section_statement_type *) output);
+    lang_add_section (&ptr->children, section, sflag_info, os);
   else
     {
       lang_statement_list_type list;
       lang_statement_union_type **pp;
 
       lang_list_init (&list);
-      lang_add_section (&list, section,
-                       (lang_output_section_statement_type *) output);
+      lang_add_section (&list, section, sflag_info, os);
 
       /* If we are discarding the section, LIST.HEAD will
         be NULL.  */
@@ -2356,15 +2618,20 @@ static void
 check_section_callback (lang_wild_statement_type *ptr ATTRIBUTE_UNUSED,
                        struct wildcard_list *sec ATTRIBUTE_UNUSED,
                        asection *section,
+                       struct flag_info *sflag_info ATTRIBUTE_UNUSED,
                        lang_input_statement_type *file ATTRIBUTE_UNUSED,
-                       void *data)
+                       void *output)
 {
+  lang_output_section_statement_type *os;
+
+  os = (lang_output_section_statement_type *) output;
+
   /* Exclude sections that match UNIQUE_SECTION_LIST.  */
-  if (unique_section_p (section))
+  if (unique_section_p (section, os))
     return;
 
   if (section->output_section == NULL && (section->flags & SEC_READONLY) == 0)
-    ((lang_output_section_statement_type *) data)->all_input_readonly = FALSE;
+    os->all_input_readonly = FALSE;
 }
 
 /* This is passed a file name which must have been seen already and
@@ -2386,7 +2653,7 @@ lookup_name (const char *name)
       const char *filename = search->local_sym_name;
 
       if (filename != NULL
-         && strcmp (filename, name) == 0)
+         && filename_cmp (filename, name) == 0)
        break;
     }
 
@@ -2396,10 +2663,10 @@ lookup_name (const char *name)
 
   /* If we have already added this file, or this file is not real
      don't add this file.  */
-  if (search->loaded || !search->real)
+  if (search->flags.loaded || !search->flags.real)
     return search;
 
-  if (! load_symbols (search, NULL))
+  if (!load_symbols (search, NULL))
     return NULL;
 
   return search;
@@ -2425,9 +2692,9 @@ add_excluded_libs (const char *list)
       end = strpbrk (p, ",:");
       if (end == NULL)
        end = p + strlen (p);
-      entry = xmalloc (sizeof (*entry));
+      entry = (struct excluded_lib *) xmalloc (sizeof (*entry));
       entry->next = excluded_libs;
-      entry->name = xmalloc (end - p + 1);
+      entry->name = (char *) xmalloc (end - p + 1);
       memcpy (entry->name, p, end - p);
       entry->name[end - p] = '\0';
       excluded_libs = entry;
@@ -2453,7 +2720,7 @@ check_excluded_libs (bfd *abfd)
          return;
        }
 
-      if (strncmp (lib->name, filename, len) == 0
+      if (filename_ncmp (lib->name, filename, len) == 0
          && (filename[len] == '\0'
              || (filename[len] == '.' && filename[len + 1] == 'a'
                  && filename[len + 2] == '\0')))
@@ -2474,19 +2741,21 @@ load_symbols (lang_input_statement_type *entry,
 {
   char **matching;
 
-  if (entry->loaded)
+  if (entry->flags.loaded)
     return TRUE;
 
   ldfile_open_file (entry);
 
-  if (! bfd_check_format (entry->the_bfd, bfd_archive)
-      && ! bfd_check_format_matches (entry->the_bfd, bfd_object, &matching))
+  /* Do not process further if the file was missing.  */
+  if (entry->flags.missing_file)
+    return TRUE;
+
+  if (!bfd_check_format (entry->the_bfd, bfd_archive)
+      && !bfd_check_format_matches (entry->the_bfd, bfd_object, &matching))
     {
       bfd_error_type err;
-      lang_statement_list_type *hold;
-      bfd_boolean bad_load = TRUE;
-      bfd_boolean save_ldlang_sysrooted_script;
-      bfd_boolean save_as_needed, save_add_needed;
+      struct lang_input_statement_flags save_flags;
+      extern FILE *yyin;
 
       err = bfd_get_error ();
 
@@ -2507,38 +2776,38 @@ 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);
-      else
-       bad_load = FALSE;
 
       bfd_close (entry->the_bfd);
       entry->the_bfd = NULL;
 
       /* Try to interpret the file as a linker script.  */
+      save_flags = input_flags;
       ldfile_open_command_file (entry->filename);
 
-      hold = stat_ptr;
-      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;
+      push_stat_ptr (place);
+      input_flags.add_DT_NEEDED_for_regular
+       = entry->flags.add_DT_NEEDED_for_regular;
+      input_flags.add_DT_NEEDED_for_dynamic
+       = entry->flags.add_DT_NEEDED_for_dynamic;
+      input_flags.whole_archive = entry->flags.whole_archive;
+      input_flags.dynamic = entry->flags.dynamic;
 
       ldfile_assumed_script = TRUE;
       parser_input = input_script;
-      /* We want to use the same -Bdynamic/-Bstatic as the one for
-        ENTRY.  */
-      config.dynamic_link = entry->dynamic;
       yyparse ();
       ldfile_assumed_script = FALSE;
 
-      ldlang_sysrooted_script = save_ldlang_sysrooted_script;
-      as_needed = save_as_needed;
-      add_needed = save_add_needed;
-      stat_ptr = hold;
+      /* missing_file is sticky.  sysrooted will already have been
+        restored when seeing EOF in yyparse, but no harm to restore
+        again.  */
+      save_flags.missing_file |= input_flags.missing_file;
+      input_flags = save_flags;
+      pop_stat_ptr ();
+      fclose (yyin);
+      yyin = NULL;
+      entry->flags.loaded = TRUE;
 
-      return ! bad_load;
+      return TRUE;
     }
 
   if (ldemul_recognized_file (entry))
@@ -2554,56 +2823,62 @@ load_symbols (lang_input_statement_type *entry,
       break;
 
     case bfd_object:
-      ldlang_add_file (entry);
-      if (trace_files || trace_file_tries)
+      if (!entry->flags.reload)
+       ldlang_add_file (entry);
+      if (trace_files || verbose)
        info_msg ("%I\n", entry);
       break;
 
     case bfd_archive:
       check_excluded_libs (entry->the_bfd);
 
-      if (entry->whole_archive)
+      if (entry->flags.whole_archive)
        {
          bfd *member = NULL;
          bfd_boolean loaded = TRUE;
 
          for (;;)
            {
+             bfd *subsbfd;
              member = bfd_openr_next_archived_file (entry->the_bfd, member);
 
              if (member == NULL)
                break;
 
-             if (! bfd_check_format (member, bfd_object))
+             if (!bfd_check_format (member, bfd_object))
                {
                  einfo (_("%F%B: member %B in archive is not an object\n"),
                         entry->the_bfd, member);
                  loaded = FALSE;
                }
 
-             if (! ((*link_info.callbacks->add_archive_element)
-                    (&link_info, member, "--whole-archive")))
+             subsbfd = member;
+             if (!(*link_info.callbacks
+                   ->add_archive_element) (&link_info, member,
+                                           "--whole-archive", &subsbfd))
                abort ();
 
-             if (! bfd_link_add_symbols (member, &link_info))
+             /* Potentially, the add_archive_element hook may have set a
+                substitute BFD for us.  */
+             if (!bfd_link_add_symbols (subsbfd, &link_info))
                {
-                 einfo (_("%F%B: could not read symbols: %E\n"), member);
+                 einfo (_("%F%B: error adding symbols: %E\n"), member);
                  loaded = FALSE;
                }
            }
 
-         entry->loaded = loaded;
+         entry->flags.loaded = loaded;
          return loaded;
        }
       break;
     }
 
   if (bfd_link_add_symbols (entry->the_bfd, &link_info))
-    entry->loaded = TRUE;
+    entry->flags.loaded = TRUE;
   else
-    einfo (_("%F%B: could not read symbols: %E\n"), entry->the_bfd);
+    einfo (_("%F%B: error adding symbols: %E\n"), entry->the_bfd);
 
-  return entry->loaded;
+  return entry->flags.loaded;
 }
 
 /* Handle a wild statement.  S->FILENAME or S->SECTION_LIST or both
@@ -2653,7 +2928,7 @@ wild (lang_wild_statement_type *s,
 static int
 get_target (const bfd_target *target, void *data)
 {
-  const char *sought = data;
+  const char *sought = (const char *) data;
 
   return strcmp (target->name, sought) == 0;
 }
@@ -2700,8 +2975,8 @@ name_compare (char *first, char *second)
   char *copy2;
   int result;
 
-  copy1 = xmalloc (strlen (first) + 1);
-  copy2 = xmalloc (strlen (second) + 1);
+  copy1 = (char *) xmalloc (strlen (first) + 1);
+  copy2 = (char *) xmalloc (strlen (second) + 1);
 
   /* Convert the names to lower case.  */
   stricpy (copy1, first);
@@ -2739,7 +3014,7 @@ static const bfd_target *winner;
 static int
 closest_target_match (const bfd_target *target, void *data)
 {
-  const bfd_target *original = data;
+  const bfd_target *original = (const bfd_target *) data;
 
   if (command_line.endian == ENDIAN_BIG
       && target->byteorder != BFD_ENDIAN_BIG)
@@ -2787,7 +3062,7 @@ get_first_input_target (void)
   LANG_FOR_EACH_INPUT_STATEMENT (s)
     {
       if (s->header.type == lang_input_statement_enum
-         && s->real)
+         && s->flags.real)
        {
          ldfile_open_file (s);
 
@@ -2816,7 +3091,7 @@ lang_get_output_target (void)
 
   /* No - has the current target been set to something other than
      the default?  */
-  if (current_target != default_target)
+  if (current_target != default_target && current_target != NULL)
     return current_target;
 
   /* No - can we determine the format of the first input file?  */
@@ -2896,9 +3171,9 @@ open_output (const char *name)
 
   delete_output_file_on_failure = TRUE;
 
-  if (! bfd_set_format (link_info.output_bfd, 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 (link_info.output_bfd,
+  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);
@@ -2919,7 +3194,8 @@ ldlang_open_output (lang_statement_union_type *statement)
       ASSERT (link_info.output_bfd == NULL);
       open_output (statement->output_statement.name);
       ldemul_set_output_arch ();
-      if (config.magic_demand_paged && !link_info.relocatable)
+      if (config.magic_demand_paged
+         && !bfd_link_relocatable (&link_info))
        link_info.output_bfd->flags |= D_PAGED;
       else
        link_info.output_bfd->flags &= ~D_PAGED;
@@ -2941,15 +3217,6 @@ ldlang_open_output (lang_statement_union_type *statement)
     }
 }
 
-/* Convert between addresses in bytes and sizes in octets.
-   For currently supported targets, octets_per_byte is always a power
-   of two, so we can use shifts.  */
-#define TO_ADDR(X) ((X) >> opb_shift)
-#define TO_SIZE(X) ((X) << opb_shift)
-
-/* Support the above.  */
-static unsigned int opb_shift = 0;
-
 static void
 init_opb (void)
 {
@@ -2967,26 +3234,37 @@ init_opb (void)
 
 /* Open all the input files.  */
 
+enum open_bfd_mode
+  {
+    OPEN_BFD_NORMAL = 0,
+    OPEN_BFD_FORCE = 1,
+    OPEN_BFD_RESCAN = 2
+  };
+#ifdef ENABLE_PLUGINS
+static lang_input_statement_type *plugin_insert = NULL;
+#endif
+
 static void
-open_input_bfds (lang_statement_union_type *s, bfd_boolean force)
+open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
 {
   for (; s != NULL; s = s->header.next)
     {
       switch (s->header.type)
        {
        case lang_constructors_statement_enum:
-         open_input_bfds (constructor_list.head, force);
+         open_input_bfds (constructor_list.head, mode);
          break;
        case lang_output_section_statement_enum:
-         open_input_bfds (s->output_section_statement.children.head, force);
+         open_input_bfds (s->output_section_statement.children.head, mode);
          break;
        case lang_wild_statement_enum:
          /* Maybe we should load the file's symbols.  */
-         if (s->wild_statement.filename
+         if ((mode & OPEN_BFD_RESCAN) == 0
+             && 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);
+         open_input_bfds (s->wild_statement.children.head, mode);
          break;
        case lang_group_statement_enum:
          {
@@ -2999,7 +3277,8 @@ open_input_bfds (lang_statement_union_type *s, bfd_boolean force)
            do
              {
                undefs = link_info.hash->undefs_tail;
-               open_input_bfds (s->group_statement.children.head, TRUE);
+               open_input_bfds (s->group_statement.children.head,
+                                mode | OPEN_BFD_FORCE);
              }
            while (undefs != link_info.hash->undefs_tail);
          }
@@ -3008,118 +3287,87 @@ open_input_bfds (lang_statement_union_type *s, bfd_boolean force)
          current_target = s->target_statement.target;
          break;
        case lang_input_statement_enum:
-         if (s->input_statement.real)
+         if (s->input_statement.flags.real)
            {
+             lang_statement_union_type **os_tail;
              lang_statement_list_type add;
+             bfd *abfd;
 
              s->input_statement.target = current_target;
 
              /* If we are being called from within a group, and this
                 is an archive which has already been searched, then
                 force it to be researched unless the whole archive
-                has been loaded already.  */
-             if (force
-                 && !s->input_statement.whole_archive
-                 && s->input_statement.loaded
-                 && bfd_check_format (s->input_statement.the_bfd,
-                                      bfd_archive))
-               s->input_statement.loaded = FALSE;
+                has been loaded already.  Do the same for a rescan.
+                Likewise reload --as-needed shared libs.  */
+             if (mode != OPEN_BFD_NORMAL
+#ifdef ENABLE_PLUGINS
+                 && ((mode & OPEN_BFD_RESCAN) == 0
+                     || plugin_insert == NULL)
+#endif
+                 && s->input_statement.flags.loaded
+                 && (abfd = s->input_statement.the_bfd) != NULL
+                 && ((bfd_get_format (abfd) == bfd_archive
+                      && !s->input_statement.flags.whole_archive)
+                     || (bfd_get_format (abfd) == bfd_object
+                         && ((abfd->flags) & DYNAMIC) != 0
+                         && s->input_statement.flags.add_DT_NEEDED_for_regular
+                         && bfd_get_flavour (abfd) == bfd_target_elf_flavour
+                         && (elf_dyn_lib_class (abfd) & DYN_AS_NEEDED) != 0)))
+               {
+                 s->input_statement.flags.loaded = FALSE;
+                 s->input_statement.flags.reload = TRUE;
+               }
 
+             os_tail = lang_output_section_statement.tail;
              lang_list_init (&add);
 
-             if (! load_symbols (&s->input_statement, &add))
+             if (!load_symbols (&s->input_statement, &add))
                config.make_executable = FALSE;
 
              if (add.head != NULL)
                {
-                 *add.tail = s->header.next;
-                 s->header.next = add.head;
+                 /* If this was a script with output sections then
+                    tack any added statements on to the end of the
+                    list.  This avoids having to reorder the output
+                    section statement list.  Very likely the user
+                    forgot -T, and whatever we do here will not meet
+                    naive user expectations.  */
+                 if (os_tail != lang_output_section_statement.tail)
+                   {
+                     einfo (_("%P: warning: %s contains output sections;"
+                              " did you forget -T?\n"),
+                            s->input_statement.filename);
+                     *stat_ptr->tail = add.head;
+                     stat_ptr->tail = add.tail;
+                   }
+                 else
+                   {
+                     *add.tail = s->header.next;
+                     s->header.next = add.head;
+                   }
                }
            }
+#ifdef ENABLE_PLUGINS
+         /* If we have found the point at which a plugin added new
+            files, clear plugin_insert to enable archive rescan.  */
+         if (&s->input_statement == plugin_insert)
+           plugin_insert = NULL;
+#endif
+         break;
+       case lang_assignment_statement_enum:
+         if (s->assignment_statement.exp->assign.defsym)
+           /* This is from a --defsym on the command line.  */
+           exp_fold_tree_no_dot (s->assignment_statement.exp);
          break;
        default:
          break;
        }
     }
-}
-
-/* Add a symbol to a hash of symbols used in DEFINED (NAME) expressions.  */
-
-void
-lang_track_definedness (const char *name)
-{
-  if (bfd_hash_lookup (&lang_definedness_table, name, TRUE, FALSE) == NULL)
-    einfo (_("%P%F: bfd_hash_lookup failed creating symbol %s\n"), name);
-}
-
-/* New-function for the definedness hash table.  */
-
-static struct bfd_hash_entry *
-lang_definedness_newfunc (struct bfd_hash_entry *entry,
-                         struct bfd_hash_table *table ATTRIBUTE_UNUSED,
-                         const char *name ATTRIBUTE_UNUSED)
-{
-  struct lang_definedness_hash_entry *ret
-    = (struct lang_definedness_hash_entry *) entry;
-
-  if (ret == NULL)
-    ret = (struct lang_definedness_hash_entry *)
-      bfd_hash_allocate (table, sizeof (struct lang_definedness_hash_entry));
-
-  if (ret == NULL)
-    einfo (_("%P%F: bfd_hash_allocate failed creating symbol %s\n"), name);
-
-  ret->iteration = -1;
-  return &ret->root;
-}
-
-/* Return the iteration when the definition of NAME was last updated.  A
-   value of -1 means that the symbol is not defined in the linker script
-   or the command line, but may be defined in the linker symbol table.  */
-
-int
-lang_symbol_definition_iteration (const char *name)
-{
-  struct lang_definedness_hash_entry *defentry
-    = (struct lang_definedness_hash_entry *)
-    bfd_hash_lookup (&lang_definedness_table, name, FALSE, FALSE);
-
-  /* We've already created this one on the presence of DEFINED in the
-     script, so it can't be NULL unless something is borked elsewhere in
-     the code.  */
-  if (defentry == NULL)
-    FAIL ();
-
-  return defentry->iteration;
-}
-
-/* Update the definedness state of NAME.  */
-
-void
-lang_update_definedness (const char *name, struct bfd_link_hash_entry *h)
-{
-  struct lang_definedness_hash_entry *defentry
-    = (struct lang_definedness_hash_entry *)
-    bfd_hash_lookup (&lang_definedness_table, name, FALSE, FALSE);
-
-  /* We don't keep track of symbols not tested with DEFINED.  */
-  if (defentry == NULL)
-    return;
-
-  /* If the symbol was already defined, and not from an earlier statement
-     iteration, don't update the definedness iteration, because that'd
-     make the symbol seem defined in the linker script at this point, and
-     it wasn't; it was defined in some object.  If we do anyway, DEFINED
-     would start to yield false before this point and the construct "sym =
-     DEFINED (sym) ? sym : X;" would change sym to X despite being defined
-     in an object.  */
-  if (h->type != bfd_link_hash_undefined
-      && h->type != bfd_link_hash_common
-      && h->type != bfd_link_hash_new
-      && defentry->iteration == -1)
-    return;
-
-  defentry->iteration = lang_statement_iteration;
+
+  /* Exit if any of the files were missing.  */
+  if (input_flags.missing_file)
+    einfo ("%F");
 }
 
 /* Add the supplied name to the symbol table as an undefined reference.
@@ -3133,18 +3381,19 @@ typedef struct bfd_sym_chain ldlang_undef_chain_list_type;
 #define ldlang_undef_chain_list_head entry_symbol.next
 
 void
-ldlang_add_undef (const char *const name)
+ldlang_add_undef (const char *const name, bfd_boolean cmdline)
 {
-  ldlang_undef_chain_list_type *new =
-    stat_alloc (sizeof (ldlang_undef_chain_list_type));
+  ldlang_undef_chain_list_type *new_undef;
 
-  new->next = ldlang_undef_chain_list_head;
-  ldlang_undef_chain_list_head = new;
+  undef_from_cmdline = undef_from_cmdline || cmdline;
+  new_undef = (ldlang_undef_chain_list_type *) stat_alloc (sizeof (*new_undef));
+  new_undef->next = ldlang_undef_chain_list_head;
+  ldlang_undef_chain_list_head = new_undef;
 
-  new->name = xstrdup (name);
+  new_undef->name = xstrdup (name);
 
   if (link_info.output_bfd != NULL)
-    insert_undefined (new->name);
+    insert_undefined (new_undef->name);
 }
 
 /* Insert NAME as undefined in the symbol table.  */
@@ -3178,6 +3427,55 @@ lang_place_undefineds (void)
     insert_undefined (ptr->name);
 }
 
+/* Structure used to build the list of symbols that the user has required
+   be defined.  */
+
+struct require_defined_symbol
+{
+  const char *name;
+  struct require_defined_symbol *next;
+};
+
+/* The list of symbols that the user has required be defined.  */
+
+static struct require_defined_symbol *require_defined_symbol_list;
+
+/* Add a new symbol NAME to the list of symbols that are required to be
+   defined.  */
+
+void
+ldlang_add_require_defined (const char *const name)
+{
+  struct require_defined_symbol *ptr;
+
+  ldlang_add_undef (name, TRUE);
+  ptr = (struct require_defined_symbol *) stat_alloc (sizeof (*ptr));
+  ptr->next = require_defined_symbol_list;
+  ptr->name = strdup (name);
+  require_defined_symbol_list = ptr;
+}
+
+/* Check that all symbols the user required to be defined, are defined,
+   raise an error if we find a symbol that is not defined.  */
+
+static void
+ldlang_check_require_defined_symbols (void)
+{
+  struct require_defined_symbol *ptr;
+
+  for (ptr = require_defined_symbol_list; ptr != NULL; ptr = ptr->next)
+    {
+      struct bfd_link_hash_entry *h;
+
+      h = bfd_link_hash_lookup (link_info.hash, ptr->name,
+                               FALSE, FALSE, TRUE);
+      if (h == NULL
+         || (h->type != bfd_link_hash_defined
+             && h->type != bfd_link_hash_defweak))
+       einfo(_("%P%X: required symbol `%s' not defined\n"), ptr->name);
+    }
+}
+
 /* Check for all readonly or some readwrite sections.  */
 
 static void
@@ -3192,19 +3490,19 @@ check_input_sections
        case lang_wild_statement_enum:
          walk_wild (&s->wild_statement, check_section_callback,
                     output_section_statement);
-         if (! output_section_statement->all_input_readonly)
+         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)
+         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)
+         if (!output_section_statement->all_input_readonly)
            return;
          break;
        default:
@@ -3238,7 +3536,6 @@ update_wild_statements (lang_statement_union_type *s)
              break;
 
            case lang_wild_statement_enum:
-             sec = s->wild_statement.section_list;
              for (sec = s->wild_statement.section_list; sec != NULL;
                   sec = sec->next)
                {
@@ -3266,8 +3563,11 @@ update_wild_statements (lang_statement_union_type *s)
              break;
 
            case lang_output_section_statement_enum:
-             update_wild_statements
-               (s->output_section_statement.children.head);
+             /* Don't sort .init/.fini sections.  */
+             if (strcmp (s->output_section_statement.name, ".init") != 0
+                 && strcmp (s->output_section_statement.name, ".fini") != 0)
+               update_wild_statements
+                 (s->output_section_statement.children.head);
              break;
 
            case lang_group_statement_enum:
@@ -3286,10 +3586,11 @@ 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)
     {
+      lang_output_section_statement_type *tos;
+      flagword flags;
+
       switch (s->header.type)
        {
        case lang_wild_statement_enum:
@@ -3301,27 +3602,23 @@ map_input_to_output_sections
                                        os);
          break;
        case lang_output_section_statement_enum:
-         if (s->output_section_statement.constraint)
+         tos = &s->output_section_statement;
+         if (tos->constraint != 0)
            {
-             if (s->output_section_statement.constraint != ONLY_IF_RW
-                 && s->output_section_statement.constraint != ONLY_IF_RO)
+             if (tos->constraint != ONLY_IF_RW
+                 && tos->constraint != ONLY_IF_RO)
                break;
-             s->output_section_statement.all_input_readonly = TRUE;
-             check_input_sections (s->output_section_statement.children.head,
-                                   &s->output_section_statement);
-             if ((s->output_section_statement.all_input_readonly
-                  && s->output_section_statement.constraint == ONLY_IF_RW)
-                 || (!s->output_section_statement.all_input_readonly
-                     && s->output_section_statement.constraint == ONLY_IF_RO))
+             tos->all_input_readonly = TRUE;
+             check_input_sections (tos->children.head, tos);
+             if (tos->all_input_readonly != (tos->constraint == ONLY_IF_RO))
                {
-                 s->output_section_statement.constraint = -1;
+                 tos->constraint = -1;
                  break;
                }
            }
-
-         map_input_to_output_sections (s->output_section_statement.children.head,
+         map_input_to_output_sections (tos->children.head,
                                        target,
-                                       &s->output_section_statement);
+                                       tos);
          break;
        case lang_output_statement_enum:
          break;
@@ -3337,13 +3634,27 @@ map_input_to_output_sections
          /* Make sure that any sections mentioned in the expression
             are initialized.  */
          exp_init_os (s->data_statement.exp);
-         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.  */
-         if (!(os->flags & SEC_NEVER_LOAD))
-           flags |= SEC_ALLOC | SEC_LOAD;
+         /* The output section gets CONTENTS, ALLOC and LOAD, but
+            these may be overridden by the script.  */
+         flags = SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD;
+         switch (os->sectype)
+           {
+           case normal_section:
+           case overlay_section:
+             break;
+           case noalloc_section:
+             flags = SEC_HAS_CONTENTS;
+             break;
+           case noload_section:
+             if (bfd_get_flavour (link_info.output_bfd)
+                 == bfd_target_elf_flavour)
+               flags = SEC_NEVER_LOAD | SEC_ALLOC;
+             else
+               flags = SEC_NEVER_LOAD | SEC_HAS_CONTENTS;
+             break;
+           }
          if (os->bfd_section == NULL)
-           init_os (os, NULL, flags);
+           init_os (os, flags);
          else
            os->bfd_section->flags |= flags;
          break;
@@ -3355,11 +3666,11 @@ map_input_to_output_sections
        case lang_padding_statement_enum:
        case lang_input_statement_enum:
          if (os != NULL && os->bfd_section == NULL)
-           init_os (os, NULL, 0);
+           init_os (os, 0);
          break;
        case lang_assignment_statement_enum:
          if (os != NULL && os->bfd_section == NULL)
-           init_os (os, NULL, 0);
+           init_os (os, 0);
 
          /* Make sure that any sections mentioned in the assignment
             are initialized.  */
@@ -3378,13 +3689,18 @@ map_input_to_output_sections
          if (!s->address_statement.segment
              || !s->address_statement.segment->used)
            {
-             lang_output_section_statement_type *aos
-               = (lang_output_section_statement_lookup
-                  (s->address_statement.section_name));
-
-             if (aos->bfd_section == NULL)
-               init_os (aos, NULL, 0);
-             aos->addr_tree = s->address_statement.address;
+             const char *name = s->address_statement.section_name;
+
+             /* Create the output section statement here so that
+                orphans with a set address will be placed after other
+                script sections.  If we let the orphan placement code
+                place them in amongst other sections then the address
+                will affect following script sections, which is
+                likely to surprise naive users.  */
+             tos = lang_output_section_statement_lookup (name, 0, TRUE);
+             tos->addr_tree = s->address_statement.address;
+             if (tos->bfd_section == NULL)
+               init_os (tos, 0);
            }
          break;
        case lang_insert_statement_enum:
@@ -3405,6 +3721,7 @@ 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
@@ -3416,7 +3733,16 @@ process_insert_statements (void)
        {
          /* 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;
+         os = &(*s)->output_section_statement;
+
+         ASSERT (last_os == NULL || last_os->next == os);
+         last_os = os;
+
+         /* 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;
        }
@@ -3424,7 +3750,6 @@ process_insert_statements (void)
        {
          lang_insert_statement_type *i = &(*s)->insert_statement;
          lang_output_section_statement_type *where;
-         lang_output_section_statement_type *os;
          lang_statement_union_type **ptr;
          lang_statement_union_type *first;
 
@@ -3433,21 +3758,12 @@ process_insert_statements (void)
            {
              do
                where = where->prev;
-             while (where != NULL && where->constraint == -1);
+             while (where != NULL && where->constraint < 0);
            }
          if (where == NULL)
            {
-             einfo (_("%X%P: %s not found for insert\n"), i->where);
-             continue;
-           }
-         /* You can't insert into the list you are moving.  */
-         for (os = first_os; os != NULL; os = os->next)
-           if (os == where || os == last_os)
-             break;
-         if (os == where)
-           {
-             einfo (_("%X%P: %s not found for insert\n"), i->where);
-             continue;
+             einfo (_("%F%P: %s not found for insert\n"), i->where);
+             return;
            }
 
          /* Deal with reordering the output section statement list.  */
@@ -3484,6 +3800,7 @@ process_insert_statements (void)
              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)
                    {
@@ -3544,6 +3861,14 @@ process_insert_statements (void)
          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
@@ -3571,7 +3896,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;
@@ -3592,34 +3917,62 @@ strip_excluded_output_sections (void)
          asection *s;
 
          for (s = output_section->map_head.s; s != NULL; s = s->map_head.s)
-           if ((s->flags & SEC_LINKER_CREATED) != 0
-               && (s->flags & SEC_EXCLUDE) == 0)
+           if ((s->flags & SEC_EXCLUDE) == 0
+               && ((s->flags & SEC_LINKER_CREATED) != 0
+                   || link_info.emitrelocations))
              {
                exclude = FALSE;
                break;
              }
        }
 
-      /* TODO: Don't just junk map_head.s, turn them into link_orders.  */
-      output_section->map_head.link_order = NULL;
-      output_section->map_tail.link_order = NULL;
-
       if (exclude)
        {
          /* We don't set bfd_section to NULL since bfd_section of the
             removed output section statement may still be used.  */
-         if (!os->section_relative_symbol
-             && !os->update_dot_tree)
+         if (!os->update_dot)
            os->ignored = TRUE;
          output_section->flags |= SEC_EXCLUDE;
          bfd_section_list_remove (link_info.output_bfd, output_section);
          link_info.output_bfd->section_count--;
        }
     }
+}
+
+/* Called from ldwrite to clear out asection.map_head and
+   asection.map_tail for use as link_orders in ldwrite.
+   FIXME: Except for sh64elf.em which starts creating link_orders in
+   its after_allocation routine so needs to call it early.  */
+
+void
+lang_clear_os_map (void)
+{
+  lang_output_section_statement_type *os;
+
+  if (map_head_is_link_order)
+    return;
+
+  for (os = &lang_output_section_statement.head->output_section_statement;
+       os != NULL;
+       os = os->next)
+    {
+      asection *output_section;
+
+      if (os->constraint < 0)
+       continue;
+
+      output_section = os->bfd_section;
+      if (output_section == NULL)
+       continue;
+
+      /* TODO: Don't just junk map_head.s, turn them into link_orders.  */
+      output_section->map_head.link_order = NULL;
+      output_section->map_tail.link_order = NULL;
+    }
 
   /* Stop future calls to lang_add_section from messing with map_head
      and map_tail link_order fields.  */
-  stripped_excluded_sections = TRUE;
+  map_head_is_link_order = TRUE;
 }
 
 static void
@@ -3649,10 +4002,14 @@ print_output_section_statement
              ++len;
            }
 
-         minfo ("0x%V %W", section->vma, section->size);
+         minfo ("0x%V %W", section->vma, TO_ADDR (section->size));
 
          if (section->vma != section->lma)
            minfo (_(" load address 0x%V"), section->lma);
+
+         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 ();
@@ -3662,64 +4019,14 @@ print_output_section_statement
                        output_section_statement);
 }
 
-/* Scan for the use of the destination in the right hand side
-   of an expression.  In such cases we will not compute the
-   correct expression, since the value of DST that is used on
-   the right hand side will be its final value, not its value
-   just before this expression is evaluated.  */
-
-static bfd_boolean
-scan_for_self_assignment (const char * dst, etree_type * rhs)
-{
-  if (rhs == NULL || dst == NULL)
-    return FALSE;
-
-  switch (rhs->type.node_class)
-    {
-    case etree_binary:
-      return scan_for_self_assignment (dst, rhs->binary.lhs)
-       ||   scan_for_self_assignment (dst, rhs->binary.rhs);
-
-    case etree_trinary:
-      return scan_for_self_assignment (dst, rhs->trinary.lhs)
-       ||   scan_for_self_assignment (dst, rhs->trinary.rhs);
-
-    case etree_assign:
-    case etree_provided:
-    case etree_provide:
-      if (strcmp (dst, rhs->assign.dst) == 0)
-       return TRUE;
-      return scan_for_self_assignment (dst, rhs->assign.src);
-
-    case etree_unary:
-      return scan_for_self_assignment (dst, rhs->unary.child);
-
-    case etree_value:
-      if (rhs->value.str)
-       return strcmp (dst, rhs->value.str) == 0;
-      return FALSE;
-
-    case etree_name:
-      if (rhs->name.name)
-       return strcmp (dst, rhs->name.name) == 0;
-      return FALSE;
-
-    default:
-      break;
-    }
-
-  return FALSE;
-}
-
-
 static void
 print_assignment (lang_assignment_statement_type *assignment,
                  lang_output_section_statement_type *output_section)
 {
   unsigned int i;
   bfd_boolean is_dot;
-  bfd_boolean computation_is_valid = TRUE;
   etree_type *tree;
+  asection *osec;
 
   for (i = 0; i < SECTION_NAME_MAP_LENGTH; i++)
     print_space ();
@@ -3728,27 +4035,37 @@ print_assignment (lang_assignment_statement_type *assignment,
     {
       is_dot = FALSE;
       tree = assignment->exp->assert_s.child;
-      computation_is_valid = TRUE;
     }
   else
     {
       const char *dst = assignment->exp->assign.dst;
 
       is_dot = (dst[0] == '.' && dst[1] == 0);
+      if (!is_dot)
+       expld.assign_name = dst;
       tree = assignment->exp->assign.src;
-      computation_is_valid = is_dot || (scan_for_self_assignment (dst, tree) == FALSE);
     }
 
-  exp_fold_tree (tree, output_section->bfd_section, &print_dot);
+  osec = output_section->bfd_section;
+  if (osec == NULL)
+    osec = bfd_abs_section_ptr;
+
+  if (assignment->exp->type.node_class != etree_provide)
+    exp_fold_tree (tree, osec, &print_dot);
+  else
+    expld.result.valid_p = FALSE;
+
   if (expld.result.valid_p)
     {
       bfd_vma value;
 
-      if (computation_is_valid)
+      if (assignment->exp->type.node_class == etree_assert
+         || is_dot
+         || expld.assign_name != NULL)
        {
          value = expld.result.value;
 
-         if (expld.result.section)
+         if (expld.result.section != NULL)
            value += expld.result.section->vma;
 
          minfo ("0x%V", value);
@@ -3764,9 +4081,8 @@ print_assignment (lang_assignment_statement_type *assignment,
          if (h)
            {
              value = h->u.def.value;
-
-             if (expld.result.section)
-               value += expld.result.section->vma;
+             value += h->u.def.section->output_section->vma;
+             value += h->u.def.section->output_offset;
 
              minfo ("[0x%V]", value);
            }
@@ -3776,11 +4092,15 @@ print_assignment (lang_assignment_statement_type *assignment,
     }
   else
     {
-      minfo ("*undef*   ");
+      if (assignment->exp->type.node_class == etree_provide)
+       minfo ("[!provide]");
+      else
+       minfo ("*undef*   ");
 #ifdef BFD64
       minfo ("        ");
 #endif
     }
+  expld.assign_name = NULL;
 
   minfo ("                ");
   exp_print_tree (assignment->exp);
@@ -3802,7 +4122,7 @@ print_input_statement (lang_input_statement_type *statm)
 static bfd_boolean
 print_one_symbol (struct bfd_link_hash_entry *hash_entry, void *ptr)
 {
-  asection *sec = ptr;
+  asection *sec = (asection *) ptr;
 
   if ((hash_entry->type == bfd_link_hash_defined
        || hash_entry->type == bfd_link_hash_defweak)
@@ -3823,24 +4143,56 @@ print_one_symbol (struct bfd_link_hash_entry *hash_entry, void *ptr)
   return TRUE;
 }
 
+static int
+hash_entry_addr_cmp (const void *a, const void *b)
+{
+  const struct bfd_link_hash_entry *l = *(const struct bfd_link_hash_entry **)a;
+  const struct bfd_link_hash_entry *r = *(const struct bfd_link_hash_entry **)b;
+
+  if (l->u.def.value < r->u.def.value)
+    return -1;
+  else if (l->u.def.value > r->u.def.value)
+    return 1;
+  else
+    return 0;
+}
+
 static void
 print_all_symbols (asection *sec)
 {
-  struct fat_user_section_struct *ud = get_userdata (sec);
+  input_section_userdata_type *ud
+    = (input_section_userdata_type *) get_userdata (sec);
   struct map_symbol_def *def;
+  struct bfd_link_hash_entry **entries;
+  unsigned int i;
 
   if (!ud)
     return;
 
   *ud->map_symbol_def_tail = 0;
-  for (def = ud->map_symbol_def_head; def; def = def->next)
-    print_one_symbol (def->entry, sec);
+
+  /* Sort the symbols by address.  */
+  entries = (struct bfd_link_hash_entry **)
+      obstack_alloc (&map_obstack,
+                    ud->map_symbol_def_count * sizeof (*entries));
+
+  for (i = 0, def = ud->map_symbol_def_head; def; def = def->next, i++)
+    entries[i] = def->entry;
+
+  qsort (entries, ud->map_symbol_def_count, sizeof (*entries),
+        hash_entry_addr_cmp);
+
+  /* Print the symbols.  */
+  for (i = 0; i < ud->map_symbol_def_count; i++)
+    print_one_symbol (entries[i], sec);
+
+  obstack_free (&map_obstack, entries);
 }
 
 /* Print information about an input section to the map file.  */
 
 static void
-print_input_section (asection *i)
+print_input_section (asection *i, bfd_boolean is_discarded)
 {
   bfd_size_type size = i->size;
   int len;
@@ -3869,10 +4221,11 @@ print_input_section (asection *i)
   else
     {
       addr = print_dot;
-      size = 0;
+      if (!is_discarded)
+       size = 0;
     }
 
-  minfo ("0x%V %W %B\n", addr, TO_ADDR (size), i->owner);
+  minfo ("0x%V %W %B\n", addr, size, i->owner);
 
   if (size != i->rawsize && i->rawsize != 0)
     {
@@ -3899,7 +4252,11 @@ print_input_section (asection *i)
       else
        print_all_symbols (i);
 
-      print_dot = addr + TO_ADDR (size);
+      /* 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);
     }
 }
 
@@ -3956,7 +4313,9 @@ print_data_statement (lang_data_statement_type *data)
       break;
     }
 
-  minfo ("0x%V %W %s 0x%v", addr, size, name, data->value);
+  if (size < TO_SIZE ((unsigned) 1))
+    size = TO_SIZE ((unsigned) 1);
+  minfo ("0x%V %W %s 0x%v", addr, TO_ADDR (size), name, data->value);
 
   if (data->exp->type.node_class != etree_value)
     {
@@ -3999,7 +4358,7 @@ print_reloc_statement (lang_reloc_statement_type *reloc)
 
   size = bfd_get_reloc_size (reloc->howto);
 
-  minfo ("0x%V %W RELOC %s ", addr, size, reloc->howto->name);
+  minfo ("0x%V %W RELOC %s ", addr, TO_ADDR (size), reloc->howto->name);
 
   if (reloc->name != NULL)
     minfo ("%s+", reloc->name);
@@ -4032,7 +4391,7 @@ print_padding_statement (lang_padding_statement_type *s)
   addr = s->output_offset;
   if (s->output_section != NULL)
     addr += s->output_section->vma;
-  minfo ("0x%V %W ", addr, (bfd_vma) s->size);
+  minfo ("0x%V %W ", addr, TO_ADDR (s->size));
 
   if (s->fill->size != 0)
     {
@@ -4160,7 +4519,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.section);
+      print_input_section (s->input_section.section, FALSE);
       break;
     case lang_padding_statement_enum:
       print_padding_statement (&s->padding_statement);
@@ -4229,11 +4588,11 @@ dprint_statement (lang_statement_union_type *s, int n)
 static void
 insert_pad (lang_statement_union_type **ptr,
            fill_type *fill,
-           unsigned int alignment_needed,
+           bfd_size_type alignment_needed,
            asection *output_section,
            bfd_vma dot)
 {
-  static fill_type zero_fill = { 1, { 0 } };
+  static fill_type zero_fill;
   lang_statement_union_type *pad = NULL;
 
   if (ptr != &statement_list.head)
@@ -4254,7 +4613,8 @@ insert_pad (lang_statement_union_type **ptr,
   else
     {
       /* Make a new padding statement, linked into existing chain.  */
-      pad = stat_alloc (sizeof (lang_padding_statement_type));
+      pad = (lang_statement_union_type *)
+         stat_alloc (sizeof (lang_padding_statement_type));
       pad->header.next = *ptr;
       *ptr = pad;
       pad->header.type = lang_padding_statement_enum;
@@ -4265,7 +4625,8 @@ insert_pad (lang_statement_union_type **ptr,
     }
   pad->padding_statement.output_offset = dot - output_section->vma;
   pad->padding_statement.size = alignment_needed;
-  output_section->size += alignment_needed;
+  output_section->size = TO_SIZE (dot + TO_ADDR (alignment_needed)
+                                 - output_section->vma);
 }
 
 /* Work out how much this section will move the dot point.  */
@@ -4279,12 +4640,15 @@ size_input_section
 {
   lang_input_section_type *is = &((*this_ptr)->input_section);
   asection *i = is->section;
+  asection *o = output_section_statement->bfd_section;
 
-  if (!((lang_input_statement_type *) i->owner->usrdata)->just_syms_flag
-      && (i->flags & SEC_EXCLUDE) == 0)
+  if (i->sec_info_type == SEC_INFO_TYPE_JUST_SYMS)
+    i->output_offset = i->vma - o->vma;
+  else if ((i->flags & SEC_EXCLUDE) != 0)
+    i->output_offset = dot - o->vma;
+  else
     {
-      unsigned int alignment_needed;
-      asection *o;
+      bfd_size_type alignment_needed;
 
       /* Align this section first to the input sections requirement,
         then to the output section's requirement.  If this alignment
@@ -4294,7 +4658,6 @@ size_input_section
       if (output_section_statement->subsection_alignment != -1)
        i->alignment_power = output_section_statement->subsection_alignment;
 
-      o = output_section_statement->bfd_section;
       if (o->alignment_power < i->alignment_power)
        o->alignment_power = i->alignment_power;
 
@@ -4307,32 +4670,49 @@ size_input_section
        }
 
       /* Remember where in the output section this input section goes.  */
-
       i->output_offset = dot - o->vma;
 
       /* Mark how big the output section must be to contain this now.  */
       dot += TO_ADDR (i->size);
       o->size = TO_SIZE (dot - o->vma);
     }
-  else
-    {
-      i->output_offset = i->vma - output_section_statement->bfd_section->vma;
-    }
 
   return dot;
 }
 
+struct check_sec
+{
+  asection *sec;
+  bfd_boolean warned;
+};
+
 static int
 sort_sections_by_lma (const void *arg1, const void *arg2)
 {
-  const asection *sec1 = *(const asection **) arg1;
-  const asection *sec2 = *(const asection **) arg2;
+  const asection *sec1 = ((const struct check_sec *) arg1)->sec;
+  const asection *sec2 = ((const struct check_sec *) arg2)->sec;
+
+  if (sec1->lma < sec2->lma)
+    return -1;
+  else if (sec1->lma > sec2->lma)
+    return 1;
+  else if (sec1->id < sec2->id)
+    return -1;
+  else if (sec1->id > sec2->id)
+    return 1;
+
+  return 0;
+}
+
+static int
+sort_sections_by_vma (const void *arg1, const void *arg2)
+{
+  const asection *sec1 = ((const struct check_sec *) arg1)->sec;
+  const asection *sec2 = ((const struct check_sec *) arg2)->sec;
 
-  if (bfd_section_lma (sec1->owner, sec1)
-      < bfd_section_lma (sec2->owner, sec2))
+  if (sec1->vma < sec2->vma)
     return -1;
-  else if (bfd_section_lma (sec1->owner, sec1)
-          > bfd_section_lma (sec2->owner, sec2))
+  else if (sec1->vma > sec2->vma)
     return 1;
   else if (sec1->id < sec2->id)
     return -1;
@@ -4342,11 +4722,11 @@ sort_sections_by_lma (const void *arg1, const void *arg2)
   return 0;
 }
 
+#define IS_TBSS(s) \
+  ((s->flags & (SEC_LOAD | SEC_THREAD_LOCAL)) == SEC_THREAD_LOCAL)
+
 #define IGNORE_SECTION(s) \
-  ((s->flags & SEC_NEVER_LOAD) != 0                            \
-   || (s->flags & SEC_ALLOC) == 0                              \
-   || ((s->flags & SEC_THREAD_LOCAL) != 0                      \
-       && (s->flags & SEC_LOAD) == 0))
+  ((s->flags & SEC_ALLOC) == 0 || IS_TBSS (s))
 
 /* Check to see if any allocated sections overlap with other allocated
    sections.  This can happen if a linker script specifies the output
@@ -4356,60 +4736,112 @@ sort_sections_by_lma (const void *arg1, const void *arg2)
 static void
 lang_check_section_addresses (void)
 {
-  asection *s, *os;
-  asection **sections, **spp;
-  unsigned int count;
+  asection *s, *p;
+  struct check_sec *sections;
+  size_t i, count;
   bfd_vma s_start;
   bfd_vma s_end;
-  bfd_vma os_start;
-  bfd_vma os_end;
-  bfd_size_type amt;
+  bfd_vma p_start = 0;
+  bfd_vma p_end = 0;
   lang_memory_region_type *m;
+  bfd_boolean overlays;
 
   if (bfd_count_sections (link_info.output_bfd) <= 1)
     return;
 
-  amt = bfd_count_sections (link_info.output_bfd) * sizeof (asection *);
-  sections = xmalloc (amt);
+  count = bfd_count_sections (link_info.output_bfd);
+  sections = XNEWVEC (struct check_sec, count);
 
   /* Scan all sections in the output list.  */
   count = 0;
   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)
+      if (IGNORE_SECTION (s)
+         || s->size == 0)
        continue;
 
-      sections[count] = s;
+      sections[count].sec = s;
+      sections[count].warned = FALSE;
       count++;
     }
 
   if (count <= 1)
-    return;
+    {
+      free (sections);
+      return;
+    }
 
-  qsort (sections, (size_t) count, sizeof (asection *),
-        sort_sections_by_lma);
+  qsort (sections, count, sizeof (*sections), sort_sections_by_lma);
 
-  spp = sections;
-  s = *spp++;
-  s_start = bfd_section_lma (link_info.output_bfd, s);
-  s_end = s_start + TO_ADDR (s->size) - 1;
-  for (count--; count; count--)
+  /* First check section LMAs.  There should be no overlap of LMAs on
+     loadable sections, even with overlays.  */
+  for (p = NULL, i = 0; i < count; i++)
     {
-      /* We must check the sections' LMA addresses not their VMA
-        addresses because overlay sections can have overlapping VMAs
-        but they must have distinct LMAs.  */
-      os = s;
-      os_start = s_start;
-      os_end = s_end;
-      s = *spp++;
-      s_start = bfd_section_lma (link_info.output_bfd, s);
-      s_end = s_start + TO_ADDR (s->size) - 1;
+      s = sections[i].sec;
+      if ((s->flags & SEC_LOAD) != 0)
+       {
+         s_start = s->lma;
+         s_end = s_start + TO_ADDR (s->size) - 1;
+
+         /* Look for an overlap.  We have sorted sections by lma, so
+            we know that s_start >= p_start.  Besides the obvious
+            case of overlap when the current section starts before
+            the previous one ends, we also must have overlap if the
+            previous section wraps around the address space.  */
+         if (p != NULL
+             && (s_start <= p_end
+                 || p_end < p_start))
+           {
+             einfo (_("%X%P: section %s LMA [%V,%V]"
+                      " overlaps section %s LMA [%V,%V]\n"),
+                    s->name, s_start, s_end, p->name, p_start, p_end);
+             sections[i].warned = TRUE;
+           }
+         p = s;
+         p_start = s_start;
+         p_end = s_end;
+       }
+    }
+
+  /* If any non-zero size allocated section (excluding tbss) starts at
+     exactly the same VMA as another such section, then we have
+     overlays.  Overlays generated by the OVERLAY keyword will have
+     this property.  It is possible to intentionally generate overlays
+     that fail this test, but it would be unusual.  */
+  qsort (sections, count, sizeof (*sections), sort_sections_by_vma);
+  overlays = FALSE;
+  p_start = sections[0].sec->vma;
+  for (i = 1; i < count; i++)
+    {
+      s_start = sections[i].sec->vma;
+      if (p_start == s_start)
+       {
+         overlays = TRUE;
+         break;
+       }
+      p_start = s_start;
+    }
 
-      /* Look for an overlap.  */
-      if (s_end >= os_start && s_start <= os_end)
-       einfo (_("%X%P: section %s [%V -> %V] overlaps section %s [%V -> %V]\n"),
-              s->name, s_start, s_end, os->name, os_start, os_end);
+  /* Now check section VMAs if no overlays were detected.  */
+  if (!overlays)
+    {
+      for (p = NULL, i = 0; i < count; i++)
+       {
+         s = sections[i].sec;
+         s_start = s->vma;
+         s_end = s_start + TO_ADDR (s->size) - 1;
+
+         if (p != NULL
+             && !sections[i].warned
+             && (s_start <= p_end
+                 || p_end < p_start))
+           einfo (_("%X%P: section %s VMA [%V,%V]"
+                    " overlaps section %s VMA [%V,%V]\n"),
+                  s->name, s_start, s_end, p->name, p_start, p_end);
+         p = s;
+         p_start = s_start;
+         p_end = s_end;
+       }
     }
 
   free (sections);
@@ -4424,9 +4856,8 @@ lang_check_section_addresses (void)
      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)));
-
+      einfo (_("%X%P: region `%s' overflowed by %ld bytes\n"),
+            m->name_list.name, (long)(m->current - (m->origin + m->length)));
 }
 
 /* Make sure the new address is within the region.  We explicitly permit the
@@ -4438,30 +4869,30 @@ static void
 os_region_check (lang_output_section_statement_type *os,
                 lang_memory_region_type *region,
                 etree_type *tree,
-                bfd_vma base)
+                bfd_vma rbase)
 {
   if ((region->current < region->origin
        || (region->current - region->origin > region->length))
       && ((region->current != region->origin + region->length)
-         || base == 0))
+         || rbase == 0))
     {
       if (tree != NULL)
        {
-         einfo (_("%X%P: address 0x%v of %B section %s"
-                  " is not within region %s\n"),
+         einfo (_("%X%P: address 0x%v of %B section `%s'"
+                  " is not within region `%s'\n"),
                 region->current,
                 os->bfd_section->owner,
                 os->bfd_section->name,
-                region->name);
+                region->name_list.name);
        }
       else if (!region->had_full_message)
        {
          region->had_full_message = TRUE;
 
-         einfo (_("%X%P: %B section %s will not fit in region %s\n"),
+         einfo (_("%X%P: %B section `%s' will not fit in region `%s'\n"),
                 os->bfd_section->owner,
                 os->bfd_section->name,
-                region->name);
+                region->name_list.name);
        }
     }
 }
@@ -4470,37 +4901,54 @@ os_region_check (lang_output_section_statement_type *os,
 
 static bfd_vma
 lang_size_sections_1
-  (lang_statement_union_type *s,
+  (lang_statement_union_type **prev,
    lang_output_section_statement_type *output_section_statement,
-   lang_statement_union_type **prev,
    fill_type *fill,
    bfd_vma dot,
    bfd_boolean *relax,
    bfd_boolean check_regions)
 {
+  lang_statement_union_type *s;
+
   /* Size up the sections from their constituent parts.  */
-  for (; s != NULL; s = s->header.next)
+  for (s = *prev; s != NULL; s = s->header.next)
     {
       switch (s->header.type)
        {
        case lang_output_section_statement_enum:
          {
-           bfd_vma newdot, after;
+           bfd_vma newdot, after, dotdelta;
            lang_output_section_statement_type *os;
            lang_memory_region_type *r;
+           int section_alignment = 0;
 
            os = &s->output_section_statement;
+           if (os->constraint == -1)
+             break;
+
+           /* FIXME: We shouldn't need to zero section vmas for ld -r
+              here, in lang_insert_orphan, or in the default linker scripts.
+              This is covering for coff backend linker bugs.  See PR6945.  */
+           if (os->addr_tree == NULL
+               && bfd_link_relocatable (&link_info)
+               && (bfd_get_flavour (link_info.output_bfd)
+                   == bfd_target_coff_flavour))
+             os->addr_tree = exp_intop (0);
            if (os->addr_tree != NULL)
              {
                os->processed_vma = FALSE;
                exp_fold_tree (os->addr_tree, bfd_abs_section_ptr, &dot);
 
                if (expld.result.valid_p)
-                 dot = expld.result.value + expld.result.section->vma;
+                 {
+                   dot = expld.result.value;
+                   if (expld.result.section != NULL)
+                     dot += 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);
+                        os->addr_tree, os->name);
              }
 
            if (os->bfd_section == NULL)
@@ -4535,6 +4983,7 @@ lang_size_sections_1
              }
 
            newdot = dot;
+           dotdelta = 0;
            if (bfd_is_abs_section (os->bfd_section))
              {
                /* No matter what happens, an abs section starts at zero.  */
@@ -4542,16 +4991,14 @@ lang_size_sections_1
              }
            else
              {
-               int align;
-
                if (os->addr_tree == NULL)
                  {
                    /* No address specified for this section, get one
                       from the region specification.  */
                    if (os->region == NULL
                        || ((os->bfd_section->flags & (SEC_ALLOC | SEC_LOAD))
-                           && os->region->name[0] == '*'
-                           && strcmp (os->region->name,
+                           && os->region->name_list.name[0] == '*'
+                           && strcmp (os->region->name_list.name,
                                       DEFAULT_MEMORY_REGION) == 0))
                      {
                        os->region = lang_memory_default (os->bfd_section);
@@ -4562,12 +5009,12 @@ lang_size_sections_1
                       defined, issue an error message.  */
                    if (!os->ignored
                        && !IGNORE_SECTION (os->bfd_section)
-                       && ! link_info.relocatable
+                       && !bfd_link_relocatable (&link_info)
                        && check_regions
-                       && strcmp (os->region->name,
+                       && strcmp (os->region->name_list.name,
                                   DEFAULT_MEMORY_REGION) == 0
                        && lang_memory_region_list != NULL
-                       && (strcmp (lang_memory_region_list->name,
+                       && (strcmp (lang_memory_region_list->name_list.name,
                                    DEFAULT_MEMORY_REGION) != 0
                            || lang_memory_region_list->next != NULL)
                        && expld.phase != lang_mark_phase_enum)
@@ -4594,24 +5041,25 @@ lang_size_sections_1
                      }
 
                    newdot = os->region->current;
-                   align = os->bfd_section->alignment_power;
+                   section_alignment = os->bfd_section->alignment_power;
                  }
                else
-                 align = os->section_alignment;
+                 section_alignment = os->section_alignment;
 
                /* Align to what the section needs.  */
-               if (align > 0)
+               if (section_alignment > 0)
                  {
                    bfd_vma savedot = newdot;
-                   newdot = align_power (newdot, align);
+                   newdot = align_power (newdot, section_alignment);
 
-                   if (newdot != savedot
+                   dotdelta = newdot - savedot;
+                   if (dotdelta != 0
                        && (config.warn_section_align
                            || os->addr_tree != NULL)
                        && expld.phase != lang_mark_phase_enum)
                      einfo (_("%P: warning: changing start of section"
                               " %s by %lu bytes\n"),
-                            os->name, (unsigned long) (newdot - savedot));
+                            os->name, (unsigned long) dotdelta);
                  }
 
                bfd_set_section_vma (0, os->bfd_section, newdot);
@@ -4619,7 +5067,7 @@ lang_size_sections_1
                os->bfd_section->output_offset = 0;
              }
 
-           lang_size_sections_1 (os->children.head, os, &os->children.head,
+           lang_size_sections_1 (&os->children.head, os,
                                  os->fill, newdot, relax, check_regions);
 
            os->processed_vma = TRUE;
@@ -4659,8 +5107,20 @@ lang_size_sections_1
              {
                bfd_vma lma = os->lma_region->current;
 
-               if (os->section_alignment != -1)
-                 lma = align_power (lma, os->section_alignment);
+               if (os->align_lma_with_input)
+                 lma += dotdelta;
+               else
+                 {
+                   /* When LMA_REGION is the same as REGION, align the LMA
+                      as we did for the VMA, possibly including alignment
+                      from the bfd section.  If a different region, then
+                      only align according to the value in the output
+                      statement.  */
+                   if (os->lma_region != os->region)
+                     section_alignment = os->section_alignment;
+                   if (section_alignment > 0)
+                     lma = align_power (lma, section_alignment);
+                 }
                os->bfd_section->lma = lma;
              }
            else if (r->last_os != NULL
@@ -4677,7 +5137,7 @@ lang_size_sections_1
                   create overlapping LMAs.  */
                if (dot < last->vma
                    && os->bfd_section->size != 0
-                   && dot + os->bfd_section->size <= last->vma)
+                   && dot + TO_ADDR (os->bfd_section->size) <= last->vma)
                  {
                    /* If dot moved backwards then leave lma equal to
                       vma.  This is the old default lma, which might
@@ -4686,23 +5146,23 @@ lang_size_sections_1
                       so people can fix their linker scripts.  */
 
                    if (last->vma != last->lma)
-                     einfo (_("%P: warning: dot moved backwards before `%s'\n"),
-                            os->name);
+                     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;
+                     lma = last->lma + TO_ADDR (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);
+                   if (section_alignment > 0)
+                     lma = align_power (lma, section_alignment);
                    os->bfd_section->lma = lma;
                  }
              }
@@ -4719,9 +5179,7 @@ lang_size_sections_1
               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
+           if (!IGNORE_SECTION (os->bfd_section)
                && (os->bfd_section->size != 0
                    || (r->last_os == NULL
                        && os->bfd_section->vma != os->bfd_section->lma)
@@ -4729,14 +5187,16 @@ lang_size_sections_1
                        && dot >= (r->last_os->output_section_statement
                                   .bfd_section->vma)))
                && os->lma_region == NULL
-               && !link_info.relocatable)
+               && !bfd_link_relocatable (&link_info))
              r->last_os = s;
 
            /* .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 (!IS_TBSS (os->bfd_section)
+               || bfd_link_relocatable (&link_info))
+             dotdelta = TO_ADDR (os->bfd_section->size);
+           else
+             dotdelta = 0;
+           dot += dotdelta;
 
            if (os->update_dot_tree != 0)
              exp_fold_tree (os->update_dot_tree, bfd_abs_section_ptr, &dot);
@@ -4744,14 +5204,9 @@ lang_size_sections_1
            /* Update dot in the region ?
               We only do this if the section is going to be allocated,
               since unallocated sections do not contribute to the region's
-              overall size in memory.
-
-              If the SEC_NEVER_LOAD bit is not set, it will affect the
-              addresses of sections after it. We have to update
-              dot.  */
+              overall size in memory.  */
            if (os->region != NULL
-               && ((os->bfd_section->flags & SEC_NEVER_LOAD) == 0
-                   || (os->bfd_section->flags & (SEC_ALLOC | SEC_LOAD))))
+               && (os->bfd_section->flags & (SEC_ALLOC | SEC_LOAD)))
              {
                os->region->current = dot;
 
@@ -4761,10 +5216,10 @@ lang_size_sections_1
                                   os->bfd_section->vma);
 
                if (os->lma_region != NULL && os->lma_region != os->region
-                   && (os->bfd_section->flags & SEC_LOAD))
+                   && ((os->bfd_section->flags & SEC_LOAD)
+                       || os->align_lma_with_input))
                  {
-                   os->lma_region->current
-                     = os->bfd_section->lma + TO_ADDR (os->bfd_section->size);
+                   os->lma_region->current = os->bfd_section->lma + dotdelta;
 
                    if (check_regions)
                      os_region_check (os, os->lma_region, NULL,
@@ -4775,9 +5230,8 @@ lang_size_sections_1
          break;
 
        case lang_constructors_statement_enum:
-         dot = lang_size_sections_1 (constructor_list.head,
+         dot = lang_size_sections_1 (&constructor_list.head,
                                      output_section_statement,
-                                     &s->wild_statement.children.head,
                                      fill, dot, relax, check_regions);
          break;
 
@@ -4815,7 +5269,9 @@ lang_size_sections_1
            if (size < TO_SIZE ((unsigned) 1))
              size = TO_SIZE ((unsigned) 1);
            dot += TO_ADDR (size);
-           output_section_statement->bfd_section->size += size;
+           output_section_statement->bfd_section->size
+             = TO_SIZE (dot - output_section_statement->bfd_section->vma);
+
          }
          break;
 
@@ -4829,14 +5285,14 @@ lang_size_sections_1
              output_section_statement->bfd_section;
            size = bfd_get_reloc_size (s->reloc_statement.howto);
            dot += TO_ADDR (size);
-           output_section_statement->bfd_section->size += size;
+           output_section_statement->bfd_section->size
+             = TO_SIZE (dot - output_section_statement->bfd_section->vma);
          }
          break;
 
        case lang_wild_statement_enum:
-         dot = lang_size_sections_1 (s->wild_statement.children.head,
+         dot = lang_size_sections_1 (&s->wild_statement.children.head,
                                      output_section_statement,
-                                     &s->wild_statement.children.head,
                                      fill, dot, relax, check_regions);
          break;
 
@@ -4853,18 +5309,18 @@ lang_size_sections_1
          {
            asection *i;
 
-           i = (*prev)->input_section.section;
+           i = s->input_section.section;
            if (relax)
              {
                bfd_boolean again;
 
-               if (! bfd_relax_section (i->owner, i, &link_info, &again))
+               if (!bfd_relax_section (i->owner, i, &link_info, &again))
                  einfo (_("%P%F: can't relax section: %E\n"));
                if (again)
                  *relax = TRUE;
              }
            dot = size_input_section (prev, output_section_statement,
-                                     output_section_statement->fill, dot);
+                                     fill, dot);
          }
          break;
 
@@ -4909,12 +5365,12 @@ lang_size_sections_1
              }
            expld.dataseg.relro = exp_dataseg_relro_none;
 
-           /* This symbol is relative to this section.  */
+           /* This symbol may be 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;
+             output_section_statement->update_dot = 1;
 
            if (!output_section_statement->ignored)
              {
@@ -4939,8 +5395,11 @@ lang_size_sections_1
                    /* If dot is advanced, this implies that the section
                       should have space allocated to it, unless the
                       user has explicitly stated that the section
-                      should never be loaded.  */
-                   if (!(output_section_statement->flags & SEC_NEVER_LOAD))
+                      should not be allocated.  */
+                   if (output_section_statement->sectype != noalloc_section
+                       && (output_section_statement->sectype != noload_section
+                           || (bfd_get_flavour (link_info.output_bfd)
+                               == bfd_target_elf_flavour)))
                      output_section_statement->bfd_section->flags |= SEC_ALLOC;
                  }
                dot = newdot;
@@ -4966,9 +5425,8 @@ lang_size_sections_1
          break;
 
        case lang_group_statement_enum:
-         dot = lang_size_sections_1 (s->group_statement.children.head,
+         dot = lang_size_sections_1 (&s->group_statement.children.head,
                                      output_section_statement,
-                                     &s->group_statement.children.head,
                                      fill, dot, relax, check_regions);
          break;
 
@@ -4994,14 +5452,14 @@ lang_size_sections_1
    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,
+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;
+  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.  */
@@ -5012,6 +5470,12 @@ ldlang_override_segment_assignment (struct bfd_link_info * info ATTRIBUTE_UNUSED
   if (current_section == NULL || previous_section == NULL)
     return new_segment;
 
+  /* If this flag is set, the target never wants code and non-code
+     sections comingled in the same segment.  */
+  if (config.separate_code
+      && ((current_section->flags ^ previous_section->flags) & SEC_CODE))
+    return TRUE;
+
   /* 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
@@ -5034,8 +5498,8 @@ void
 one_lang_size_sections_pass (bfd_boolean *relax, bfd_boolean check_regions)
 {
   lang_statement_iteration++;
-  lang_size_sections_1 (statement_list.head, abs_output_section,
-                       &statement_list.head, 0, 0, relax, check_regions);
+  lang_size_sections_1 (&statement_list.head, abs_output_section,
+                       0, 0, relax, check_regions);
 }
 
 void
@@ -5048,51 +5512,55 @@ lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions)
   if (expld.dataseg.phase == exp_dataseg_end_seen
       && link_info.relro && expld.dataseg.relro_end)
     {
-      /* If DATA_SEGMENT_ALIGN DATA_SEGMENT_RELRO_END pair was seen, try
-        to put expld.dataseg.relro on a (common) page boundary.  */
-      bfd_vma old_min_base, relro_end, maxpage;
+      bfd_vma initial_base, relro_end, desired_end;
+      asection *sec;
 
-      expld.dataseg.phase = exp_dataseg_relro_adjust;
-      old_min_base = expld.dataseg.min_base;
-      maxpage = expld.dataseg.maxpagesize;
-      expld.dataseg.base += (-expld.dataseg.relro_end
-                            & (expld.dataseg.pagesize - 1));
       /* Compute the expected PT_GNU_RELRO segment end.  */
       relro_end = ((expld.dataseg.relro_end + expld.dataseg.pagesize - 1)
                   & ~(expld.dataseg.pagesize - 1));
-      if (old_min_base + maxpage < expld.dataseg.base)
-       {
-         expld.dataseg.base -= maxpage;
-         relro_end -= maxpage;
-       }
+
+      /* Adjust by the offset arg of DATA_SEGMENT_RELRO_END.  */
+      desired_end = relro_end - expld.dataseg.relro_offset;
+
+      /* For sections in the relro segment..  */
+      for (sec = link_info.output_bfd->section_last; sec; sec = sec->prev)
+       if ((sec->flags & SEC_ALLOC) != 0
+           && sec->vma >= expld.dataseg.base
+           && sec->vma < expld.dataseg.relro_end - expld.dataseg.relro_offset)
+         {
+           /* Where do we want to put this section so that it ends as
+              desired?  */
+           bfd_vma start, end, bump;
+
+           end = start = sec->vma;
+           if (!IS_TBSS (sec))
+             end += TO_ADDR (sec->size);
+           bump = desired_end - end;
+           /* We'd like to increase START by BUMP, but we must heed
+              alignment so the increase might be less than optimum.  */
+           start += bump;
+           start &= ~(((bfd_vma) 1 << sec->alignment_power) - 1);
+           /* This is now the desired end for the previous section.  */
+           desired_end = start;
+         }
+
+      expld.dataseg.phase = exp_dataseg_relro_adjust;
+      ASSERT (desired_end >= expld.dataseg.base);
+      initial_base = expld.dataseg.base;
+      expld.dataseg.base = desired_end;
       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.  */
-         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 = 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)
-             max_alignment_power = sec->alignment_power;
-
-         if (((bfd_vma) 1 << max_alignment_power) < expld.dataseg.pagesize)
-           {
-             if (expld.dataseg.base - (1 << max_alignment_power)
-                 < old_min_base)
-               expld.dataseg.base += expld.dataseg.pagesize;
-             expld.dataseg.base -= (1 << max_alignment_power);
-             lang_reset_memory_regions ();
-             one_lang_size_sections_pass (relax, check_regions);
-           }
+         /* Assignments to dot, or to output section address in a
+            user script have increased padding over the original.
+            Revert.  */
+         expld.dataseg.base = initial_base;
+         lang_reset_memory_regions ();
+         one_lang_size_sections_pass (relax, check_regions);
        }
+
       link_info.relro_start = expld.dataseg.base;
       link_info.relro_end = expld.dataseg.relro_end;
     }
@@ -5113,18 +5581,25 @@ lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions)
          lang_reset_memory_regions ();
          one_lang_size_sections_pass (relax, check_regions);
        }
+      else
+       expld.dataseg.phase = exp_dataseg_done;
     }
-
-  expld.phase = lang_final_phase_enum;
+  else
+    expld.dataseg.phase = exp_dataseg_done;
 }
 
+static lang_output_section_statement_type *current_section;
+static lang_assignment_statement_type *current_assign;
+static bfd_boolean prefer_next_section;
+
 /* 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 *current_os,
                       fill_type *fill,
-                      bfd_vma dot)
+                      bfd_vma dot,
+                      bfd_boolean *found_end)
 {
   for (; s != NULL; s = s->header.next)
     {
@@ -5132,7 +5607,7 @@ lang_do_assignments_1 (lang_statement_union_type *s,
        {
        case lang_constructors_statement_enum:
          dot = lang_do_assignments_1 (constructor_list.head,
-                                      current_os, fill, dot);
+                                      current_os, fill, dot, found_end);
          break;
 
        case lang_output_section_statement_enum:
@@ -5140,17 +5615,27 @@ lang_do_assignments_1 (lang_statement_union_type *s,
            lang_output_section_statement_type *os;
 
            os = &(s->output_section_statement);
+           os->after_end = *found_end;
            if (os->bfd_section != NULL && !os->ignored)
              {
+               if ((os->bfd_section->flags & SEC_ALLOC) != 0)
+                 {
+                   current_section = os;
+                   prefer_next_section = FALSE;
+                 }
                dot = os->bfd_section->vma;
 
-               lang_do_assignments_1 (os->children.head, os, os->fill, dot);
+               lang_do_assignments_1 (os->children.head,
+                                      os, os->fill, dot, found_end);
 
                /* .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)
+               if (!IS_TBSS (os->bfd_section)
+                   || bfd_link_relocatable (&link_info))
                  dot += TO_ADDR (os->bfd_section->size);
+
+               if (os->update_dot_tree != NULL)
+                 exp_fold_tree (os->update_dot_tree, bfd_abs_section_ptr,
+                                &dot);
              }
          }
          break;
@@ -5158,7 +5643,7 @@ lang_do_assignments_1 (lang_statement_union_type *s,
        case lang_wild_statement_enum:
 
          dot = lang_do_assignments_1 (s->wild_statement.children.head,
-                                      current_os, fill, dot);
+                                      current_os, fill, dot, found_end);
          break;
 
        case lang_object_symbols_statement_enum:
@@ -5169,8 +5654,11 @@ lang_do_assignments_1 (lang_statement_union_type *s,
        case lang_data_statement_enum:
          exp_fold_tree (s->data_statement.exp, bfd_abs_section_ptr, &dot);
          if (expld.result.valid_p)
-           s->data_statement.value = (expld.result.value
-                                      + expld.result.section->vma);
+           {
+             s->data_statement.value = expld.result.value;
+             if (expld.result.section != NULL)
+               s->data_statement.value += expld.result.section->vma;
+           }
          else
            einfo (_("%F%P: invalid data statement\n"));
          {
@@ -5226,6 +5714,19 @@ lang_do_assignments_1 (lang_statement_union_type *s,
          break;
 
        case lang_assignment_statement_enum:
+         current_assign = &s->assignment_statement;
+         if (current_assign->exp->type.node_class != etree_assert)
+           {
+             const char *p = current_assign->exp->assign.dst;
+
+             if (current_os == abs_output_section && p[0] == '.' && p[1] == 0)
+               prefer_next_section = TRUE;
+
+             while (*p == '_')
+               ++p;
+             if (strcmp (p, "end") == 0)
+               *found_end = TRUE;
+           }
          exp_fold_tree (s->assignment_statement.exp,
                         current_os->bfd_section,
                         &dot);
@@ -5237,7 +5738,7 @@ lang_do_assignments_1 (lang_statement_union_type *s,
 
        case lang_group_statement_enum:
          dot = lang_do_assignments_1 (s->group_statement.children.head,
-                                      current_os, fill, dot);
+                                      current_os, fill, dot, found_end);
          break;
 
        case lang_insert_statement_enum:
@@ -5255,10 +5756,91 @@ lang_do_assignments_1 (lang_statement_union_type *s,
 }
 
 void
-lang_do_assignments (void)
+lang_do_assignments (lang_phase_type phase)
 {
+  bfd_boolean found_end = FALSE;
+
+  current_section = NULL;
+  prefer_next_section = FALSE;
+  expld.phase = phase;
   lang_statement_iteration++;
-  lang_do_assignments_1 (statement_list.head, abs_output_section, NULL, 0);
+  lang_do_assignments_1 (statement_list.head,
+                        abs_output_section, NULL, 0, &found_end);
+}
+
+/* For an assignment statement outside of an output section statement,
+   choose the best of neighbouring output sections to use for values
+   of "dot".  */
+
+asection *
+section_for_dot (void)
+{
+  asection *s;
+
+  /* Assignments belong to the previous output section, unless there
+     has been an assignment to "dot", in which case following
+     assignments belong to the next output section.  (The assumption
+     is that an assignment to "dot" is setting up the address for the
+     next output section.)  Except that past the assignment to "_end"
+     we always associate with the previous section.  This exception is
+     for targets like SH that define an alloc .stack or other
+     weirdness after non-alloc sections.  */
+  if (current_section == NULL || prefer_next_section)
+    {
+      lang_statement_union_type *stmt;
+      lang_output_section_statement_type *os;
+
+      for (stmt = (lang_statement_union_type *) current_assign;
+          stmt != NULL;
+          stmt = stmt->header.next)
+       if (stmt->header.type == lang_output_section_statement_enum)
+         break;
+
+      os = &stmt->output_section_statement;
+      while (os != NULL
+            && !os->after_end
+            && (os->bfd_section == NULL
+                || (os->bfd_section->flags & SEC_EXCLUDE) != 0
+                || bfd_section_removed_from_list (link_info.output_bfd,
+                                                  os->bfd_section)))
+       os = os->next;
+
+      if (current_section == NULL || os == NULL || !os->after_end)
+       {
+         if (os != NULL)
+           s = os->bfd_section;
+         else
+           s = link_info.output_bfd->section_last;
+         while (s != NULL
+                && ((s->flags & SEC_ALLOC) == 0
+                    || (s->flags & SEC_THREAD_LOCAL) != 0))
+           s = s->prev;
+         if (s != NULL)
+           return s;
+
+         return bfd_abs_section_ptr;
+       }
+    }
+
+  s = current_section->bfd_section;
+
+  /* The section may have been stripped.  */
+  while (s != NULL
+        && ((s->flags & SEC_EXCLUDE) != 0
+            || (s->flags & SEC_ALLOC) == 0
+            || (s->flags & SEC_THREAD_LOCAL) != 0
+            || bfd_section_removed_from_list (link_info.output_bfd, s)))
+    s = s->prev;
+  if (s == NULL)
+    s = link_info.output_bfd->sections;
+  while (s != NULL
+        && ((s->flags & SEC_ALLOC) == 0
+            || (s->flags & SEC_THREAD_LOCAL) != 0))
+    s = s->next;
+  if (s != NULL)
+    return s;
+
+  return bfd_abs_section_ptr;
 }
 
 /* Fix any .startof. or .sizeof. symbols.  When the assemblers see the
@@ -5273,7 +5855,7 @@ lang_set_startof (void)
 {
   asection *s;
 
-  if (link_info.relocatable)
+  if (bfd_link_relocatable (&link_info))
     return;
 
   for (s = link_info.output_bfd->sections; s != NULL; s = s->next)
@@ -5283,15 +5865,15 @@ lang_set_startof (void)
       struct bfd_link_hash_entry *h;
 
       secname = bfd_get_section_name (link_info.output_bfd, s);
-      buf = xmalloc (10 + strlen (secname));
+      buf = (char *) xmalloc (10 + strlen (secname));
 
       sprintf (buf, ".startof.%s", secname);
       h = bfd_link_hash_lookup (link_info.hash, buf, FALSE, FALSE, TRUE);
       if (h != NULL && h->type == bfd_link_hash_undefined)
        {
          h->type = bfd_link_hash_defined;
-         h->u.def.value = bfd_get_section_vma (link_info.output_bfd, s);
-         h->u.def.section = bfd_abs_section_ptr;
+         h->u.def.value = 0;
+         h->u.def.section = s;
        }
 
       sprintf (buf, ".sizeof.%s", secname);
@@ -5313,17 +5895,16 @@ lang_end (void)
   struct bfd_link_hash_entry *h;
   bfd_boolean warn;
 
-  if ((link_info.relocatable && !link_info.gc_sections)
-      || link_info.shared)
+  if ((bfd_link_relocatable (&link_info) && !link_info.gc_sections)
+      || bfd_link_dll (&link_info))
     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))
+  if (link_info.gc_sections && bfd_link_relocatable (&link_info)
+      && !(entry_from_cmdline || undef_from_cmdline))
     einfo (_("%P%F: gc-sections requires either an entry or "
             "an undefined symbol\n"));
 
@@ -5348,7 +5929,7 @@ lang_end (void)
             + 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 (link_info.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
@@ -5361,7 +5942,7 @@ lang_end (void)
       val = bfd_scan_vma (entry_symbol.name, &send, 0);
       if (*send == '\0')
        {
-         if (! bfd_set_start_address (link_info.output_bfd, val))
+         if (!bfd_set_start_address (link_info.output_bfd, val))
            einfo (_("%P%F: can't set start address\n"));
        }
       else
@@ -5392,9 +5973,6 @@ lang_end (void)
            }
        }
     }
-
-  /* Don't bfd_hash_table_free (&lang_definedness_table);
-     map file output may result in a call of lang_track_definedness.  */
 }
 
 /* This is a small function used when we want to ignore errors from
@@ -5419,6 +5997,11 @@ lang_check (void)
 
   for (file = file_chain.head; file != NULL; file = file->input_statement.next)
     {
+#ifdef ENABLE_PLUGINS
+      /* Don't check format of files claimed by plugin.  */
+      if (file->input_statement.flags.claimed)
+       continue;
+#endif /* ENABLE_PLUGINS */
       input_bfd = file->input_statement.the_bfd;
       compatible
        = bfd_arch_get_compatible (input_bfd, link_info.output_bfd,
@@ -5430,7 +6013,8 @@ lang_check (void)
         input format may not have equivalent representations in
         the output format (and besides BFD does not translate
         relocs for other link purposes than a final link).  */
-      if ((link_info.relocatable || link_info.emitrelocations)
+      if ((bfd_link_relocatable (&link_info)
+          || link_info.emitrelocations)
          && (compatible == NULL
              || (bfd_get_flavour (input_bfd)
                  != bfd_get_flavour (link_info.output_bfd)))
@@ -5463,15 +6047,15 @@ lang_check (void)
             function which will do nothing.  We still want to call
             bfd_merge_private_bfd_data, since it may set up
             information which is needed in the output file.  */
-         if (! command_line.warn_mismatch)
+         if (!command_line.warn_mismatch)
            pfn = bfd_set_error_handler (ignore_bfd_errors);
-         if (! bfd_merge_private_bfd_data (input_bfd, link_info.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"
                         " of file %B\n"), input_bfd);
            }
-         if (! command_line.warn_mismatch)
+         if (!command_line.warn_mismatch)
            bfd_set_error_handler (pfn);
        }
     }
@@ -5486,11 +6070,11 @@ lang_common (void)
 {
   if (command_line.inhibit_common_definition)
     return;
-  if (link_info.relocatable
-      && ! command_line.force_common_definition)
+  if (bfd_link_relocatable (&link_info)
+      && !command_line.force_common_definition)
     return;
 
-  if (! config.sort_common)
+  if (!config.sort_common)
     bfd_link_hash_traverse (link_info.hash, lang_one_common, NULL);
   else
     {
@@ -5509,7 +6093,7 @@ lang_common (void)
          for (power = 0; power <= 4; power++)
            bfd_link_hash_traverse (link_info.hash, lang_one_common, &power);
 
-         power = UINT_MAX;
+         power = (unsigned int) -1;
          bfd_link_hash_traverse (link_info.hash, lang_one_common, &power);
        }
     }
@@ -5538,27 +6122,9 @@ lang_one_common (struct bfd_link_hash_entry *h, void *info)
     return TRUE;
 
   section = h->u.c.p->section;
-
-  /* Increase the size of the section to align the common sym.  */
-  section->size += ((bfd_vma) 1 << (power_of_two + opb_shift)) - 1;
-  section->size &= (- (bfd_vma) 1 << (power_of_two + opb_shift));
-
-  /* Adjust the alignment if necessary.  */
-  if (power_of_two > section->alignment_power)
-    section->alignment_power = power_of_two;
-
-  /* Change the symbol from common to defined.  */
-  h->type = bfd_link_hash_defined;
-  h->u.def.section = section;
-  h->u.def.value = section->size;
-
-  /* Increase the size of the section.  */
-  section->size += size;
-
-  /* Make sure the section is allocated in memory, and make sure that
-     it is no longer a common section.  */
-  section->flags |= SEC_ALLOC;
-  section->flags &= ~SEC_IS_COMMON;
+  if (!bfd_define_common_symbol (link_info.output_bfd, &link_info, h))
+    einfo (_("%P%F: Could not define common symbol `%T': %E\n"),
+          h->root.string);
 
   if (config.map_file != NULL)
     {
@@ -5567,7 +6133,7 @@ lang_one_common (struct bfd_link_hash_entry *h, void *info)
       char *name;
       char buf[50];
 
-      if (! header_printed)
+      if (!header_printed)
        {
          minfo (_("\nAllocating common symbols\n"));
          minfo (_("Common symbol       size              file\n\n"));
@@ -5619,6 +6185,55 @@ lang_one_common (struct bfd_link_hash_entry *h, void *info)
   return TRUE;
 }
 
+/* Handle a single orphan section S, placing the orphan into an appropriate
+   output section.  The effects of the --orphan-handling command line
+   option are handled here.  */
+
+static void
+ldlang_place_orphan (asection *s)
+{
+  if (config.orphan_handling == orphan_handling_discard)
+    {
+      lang_output_section_statement_type *os;
+      os = lang_output_section_statement_lookup (DISCARD_SECTION_NAME, 0,
+                                                TRUE);
+      if (os->addr_tree == NULL
+         && (bfd_link_relocatable (&link_info)
+             || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0))
+       os->addr_tree = exp_intop (0);
+      lang_add_section (&os->children, s, NULL, os);
+    }
+  else
+    {
+      lang_output_section_statement_type *os;
+      const char *name = s->name;
+      int constraint = 0;
+
+      if (config.orphan_handling == orphan_handling_error)
+       einfo ("%X%P: error: unplaced orphan section `%A' from `%B'.\n",
+              s, s->owner);
+
+      if (config.unique_orphan_sections || unique_section_p (s, NULL))
+       constraint = SPECIAL;
+
+      os = ldemul_place_orphan (s, name, constraint);
+      if (os == NULL)
+       {
+         os = lang_output_section_statement_lookup (name, constraint, TRUE);
+         if (os->addr_tree == NULL
+             && (bfd_link_relocatable (&link_info)
+                 || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0))
+           os->addr_tree = exp_intop (0);
+         lang_add_section (&os->children, s, NULL, os);
+       }
+
+      if (config.orphan_handling == orphan_handling_warn)
+       einfo ("%P: warning: orphan section `%A' from `%B' being "
+              "placed in section `%s'.\n",
+              s, s->owner, os->name);
+    }
+}
+
 /* Run through the input files and ensure that every input section has
    somewhere to go.  If one is found without a destination then create
    an input request and place it into the statement tree.  */
@@ -5637,7 +6252,7 @@ lang_place_orphans (void)
              /* This section of the file is not attached, root
                 around for a sensible place for it to go.  */
 
-             if (file->just_syms_flag)
+             if (file->flags.just_syms)
                bfd_link_just_syms (file->the_bfd, s, &link_info);
              else if ((s->flags & SEC_EXCLUDE) != 0)
                s->output_section = bfd_abs_section_ptr;
@@ -5646,28 +6261,19 @@ lang_place_orphans (void)
                  /* This is a lonely common section which must have
                     come from an archive.  We attach to the section
                     with the wildcard.  */
-                 if (! link_info.relocatable
+                 if (!bfd_link_relocatable (&link_info)
                      || 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);
+                                       NULL, default_common_section);
                    }
                }
-             else if (ldemul_place_orphan (s))
-               ;
              else
-               {
-                 lang_output_section_statement_type *os;
-
-                 os = lang_output_section_statement_lookup (s->name);
-                 lang_add_section (&os->children, s, os);
-               }
+               ldlang_place_orphan (s);
            }
        }
     }
@@ -5679,10 +6285,18 @@ lang_set_flags (lang_memory_region_type *ptr, const char *flags, int invert)
   flagword *ptr_flags;
 
   ptr_flags = invert ? &ptr->not_flags : &ptr->flags;
+
   while (*flags)
     {
       switch (*flags)
        {
+         /* PR 17900: An exclamation mark in the attributes reverses
+            the sense of any of the attributes that follow.  */
+       case '!':
+         invert = !invert;
+         ptr_flags = invert ? &ptr->not_flags : &ptr->flags;
+         break;
+
        case 'A': case 'a':
          *ptr_flags |= SEC_ALLOC;
          break;
@@ -5705,7 +6319,8 @@ lang_set_flags (lang_memory_region_type *ptr, const char *flags, int invert)
          break;
 
        default:
-         einfo (_("%P%F: invalid syntax in flags\n"));
+         einfo (_("%P%F: invalid character %c (%d) in flags\n"),
+                *flags, *flags);
          break;
        }
       flags++;
@@ -5748,11 +6363,11 @@ 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->link.next == NULL);
   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;
+  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);
 
@@ -5780,8 +6395,6 @@ lang_add_output (const char *name, int from_script)
     }
 }
 
-static lang_output_section_statement_type *current_section;
-
 static int
 topower (int x)
 {
@@ -5808,16 +6421,15 @@ lang_enter_output_section_statement (const char *output_section_statement_name,
                                     etree_type *align,
                                     etree_type *subalign,
                                     etree_type *ebase,
-                                    int constraint)
+                                    int constraint,
+                                    int align_with_input)
 {
   lang_output_section_statement_type *os;
 
-  os = lang_output_section_statement_lookup_1 (output_section_statement_name,
-                                              constraint);
+  os = lang_output_section_statement_lookup (output_section_statement_name,
+                                            constraint, TRUE);
   current_section = os;
 
-  /* Make next things chain into subchain of this.  */
-
   if (os->addr_tree == NULL)
     {
       os->addr_tree = address_exp;
@@ -5828,7 +6440,14 @@ lang_enter_output_section_statement (const char *output_section_statement_name,
   else
     os->flags = SEC_NEVER_LOAD;
   os->block_value = 1;
-  stat_ptr = &os->children;
+
+  /* Make next things chain into subchain of this.  */
+  push_stat_ptr (&os->children);
+
+  os->align_lma_with_input = align_with_input == ALIGN_WITH_INPUT;
+  if (os->align_lma_with_input && align != NULL)
+    einfo (_("%F%P:%S: error: align with input and explicit align specified\n"),
+          NULL);
 
   os->subsection_alignment =
     topower (exp_get_value_int (subalign, -1, "subsection alignment"));
@@ -5842,10 +6461,10 @@ lang_enter_output_section_statement (const char *output_section_statement_name,
 void
 lang_final (void)
 {
-  lang_output_statement_type *new;
+  lang_output_statement_type *new_stmt;
 
-  new = new_stat (lang_output_statement, stat_ptr);
-  new->name = output_filename;
+  new_stmt = new_stat (lang_output_statement, stat_ptr);
+  new_stmt->name = output_filename;
 }
 
 /* Reset the current counters in the regions.  */
@@ -5885,6 +6504,7 @@ static void
 gc_section_callback (lang_wild_statement_type *ptr,
                     struct wildcard_list *sec ATTRIBUTE_UNUSED,
                     asection *section,
+                    struct flag_info *sflag_info ATTRIBUTE_UNUSED,
                     lang_input_statement_type *file ATTRIBUTE_UNUSED,
                     void *data ATTRIBUTE_UNUSED)
 {
@@ -5925,17 +6545,20 @@ static void
 lang_gc_sections (void)
 {
   /* Keep all sections so marked in the link script.  */
-
   lang_gc_sections_1 (statement_list.head);
 
   /* SEC_EXCLUDE is ignored when doing a relocatable link, except in
      the special case of debug info.  (See bfd/stabs.c)
      Twiddle the flag here, to simplify later linker code.  */
-  if (link_info.relocatable)
+  if (bfd_link_relocatable (&link_info))
     {
       LANG_FOR_EACH_INPUT_STATEMENT (f)
        {
          asection *sec;
+#ifdef ENABLE_PLUGINS
+         if (f->flags.claimed)
+           continue;
+#endif
          for (sec = f->the_bfd->sections; sec != NULL; sec = sec->next)
            if ((sec->flags & SEC_DEBUGGING) == 0)
              sec->flags &= ~SEC_EXCLUDE;
@@ -5952,6 +6575,7 @@ static void
 find_relro_section_callback (lang_wild_statement_type *ptr ATTRIBUTE_UNUSED,
                             struct wildcard_list *sec ATTRIBUTE_UNUSED,
                             asection *section,
+                            struct flag_info *sflag_info ATTRIBUTE_UNUSED,
                             lang_input_statement_type *file ATTRIBUTE_UNUSED,
                             void *data)
 {
@@ -6023,35 +6647,163 @@ lang_find_relro_sections (void)
 
 /* Relax all sections until bfd_relax_section gives up.  */
 
-static void
-relax_sections (void)
+void
+lang_relax_sections (bfd_boolean need_layout)
 {
-  /* Keep relaxing until bfd_relax_section gives up.  */
-  bfd_boolean relax_again;
-
-  link_info.relax_trip = -1;
-  do
+  if (RELAXATION_ENABLED)
     {
-      relax_again = FALSE;
-      link_info.relax_trip++;
+      /* We may need more than one relaxation pass.  */
+      int i = link_info.relax_pass;
 
-      /* 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  */
+      /* The backend can use it to determine the current pass.  */
+      link_info.relax_pass = 0;
+
+      while (i--)
+       {
+         /* Keep relaxing until bfd_relax_section gives up.  */
+         bfd_boolean relax_again;
+
+         link_info.relax_trip = -1;
+         do
+           {
+             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 ();
+             /* Do all the assignments with our current guesses as to
+                section sizes.  */
+             lang_do_assignments (lang_assigning_phase_enum);
+
+             /* 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.  */
+             relax_again = FALSE;
+             lang_size_sections (&relax_again, FALSE);
+           }
+         while (relax_again);
+
+         link_info.relax_pass++;
+       }
+      need_layout = TRUE;
+    }
 
-      /* We must do this after lang_do_assignments, because it uses
-        size.  */
+  if (need_layout)
+    {
+      /* Final extra sizing to report errors.  */
+      lang_do_assignments (lang_assigning_phase_enum);
       lang_reset_memory_regions ();
+      lang_size_sections (NULL, TRUE);
+    }
+}
+
+#ifdef ENABLE_PLUGINS
+/* Find the insert point for the plugin's replacement files.  We
+   place them after the first claimed real object file, or if the
+   first claimed object is an archive member, after the last real
+   object file immediately preceding the archive.  In the event
+   no objects have been claimed at all, we return the first dummy
+   object file on the list as the insert point; that works, but
+   the callee must be careful when relinking the file_chain as it
+   is not actually on that chain, only the statement_list and the
+   input_file list; in that case, the replacement files must be
+   inserted at the head of the file_chain.  */
+
+static lang_input_statement_type *
+find_replacements_insert_point (void)
+{
+  lang_input_statement_type *claim1, *lastobject;
+  lastobject = &input_file_chain.head->input_statement;
+  for (claim1 = &file_chain.head->input_statement;
+       claim1 != NULL;
+       claim1 = &claim1->next->input_statement)
+    {
+      if (claim1->flags.claimed)
+       return claim1->flags.claim_archive ? lastobject : claim1;
+      /* Update lastobject if this is a real object file.  */
+      if (claim1->the_bfd != NULL && claim1->the_bfd->my_archive == NULL)
+       lastobject = claim1;
+    }
+  /* No files were claimed by the plugin.  Choose the last object
+     file found on the list (maybe the first, dummy entry) as the
+     insert point.  */
+  return lastobject;
+}
+
+/* Insert SRCLIST into DESTLIST after given element by chaining
+   on FIELD as the next-pointer.  (Counterintuitively does not need
+   a pointer to the actual after-node itself, just its chain field.)  */
+
+static void
+lang_list_insert_after (lang_statement_list_type *destlist,
+                       lang_statement_list_type *srclist,
+                       lang_statement_union_type **field)
+{
+  *(srclist->tail) = *field;
+  *field = srclist->head;
+  if (destlist->tail == field)
+    destlist->tail = srclist->tail;
+}
+
+/* Detach new nodes added to DESTLIST since the time ORIGLIST
+   was taken as a copy of it and leave them in ORIGLIST.  */
+
+static void
+lang_list_remove_tail (lang_statement_list_type *destlist,
+                      lang_statement_list_type *origlist)
+{
+  union lang_statement_union **savetail;
+  /* Check that ORIGLIST really is an earlier state of DESTLIST.  */
+  ASSERT (origlist->head == destlist->head);
+  savetail = origlist->tail;
+  origlist->head = *(savetail);
+  origlist->tail = destlist->tail;
+  destlist->tail = savetail;
+  *savetail = NULL;
+}
+#endif /* ENABLE_PLUGINS */
+
+/* Add NAME to the list of garbage collection entry points.  */
+
+void
+lang_add_gc_name (const char *name)
+{
+  struct bfd_sym_chain *sym;
+
+  if (name == NULL)
+    return;
+
+  sym = (struct bfd_sym_chain *) stat_alloc (sizeof (*sym));
+
+  sym->next = link_info.gc_sym_list;
+  sym->name = name;
+  link_info.gc_sym_list = sym;
+}
 
-      /* Perform another relax pass - this time we know where the
-        globals are, so can make a better guess.  */
-      lang_size_sections (&relax_again, FALSE);
+/* Check relocations.  */
+
+static void
+lang_check_relocs (void)
+{
+  if (link_info.check_relocs_after_open_input)
+    {
+      bfd *abfd;
+
+      for (abfd = link_info.input_bfds;
+          abfd != (bfd *) NULL; abfd = abfd->link.next)
+       if (!bfd_link_check_relocs (abfd, &link_info))
+         {
+           /* No object output, fail return.  */
+           config.make_executable = FALSE;
+           /* Note: we do not abort the loop, but rather
+              continue the scan in case there are other
+              bad relocations to report.  */
+         }
     }
-  while (relax_again);
 }
 
 void
@@ -6077,13 +6829,92 @@ lang_process (void)
 
   /* Create a bfd for each input file.  */
   current_target = default_target;
-  open_input_bfds (statement_list.head, FALSE);
+  open_input_bfds (statement_list.head, OPEN_BFD_NORMAL);
+
+#ifdef ENABLE_PLUGINS
+  if (link_info.lto_plugin_active)
+    {
+      lang_statement_list_type added;
+      lang_statement_list_type files, inputfiles;
+
+      /* Now all files are read, let the plugin(s) decide if there
+        are any more to be added to the link before we call the
+        emulation's after_open hook.  We create a private list of
+        input statements for this purpose, which we will eventually
+        insert into the global statment list after the first claimed
+        file.  */
+      added = *stat_ptr;
+      /* We need to manipulate all three chains in synchrony.  */
+      files = file_chain;
+      inputfiles = input_file_chain;
+      if (plugin_call_all_symbols_read ())
+       einfo (_("%P%F: %s: plugin reported error after all symbols read\n"),
+              plugin_error_plugin ());
+      /* Open any newly added files, updating the file chains.  */
+      open_input_bfds (*added.tail, OPEN_BFD_NORMAL);
+      /* Restore the global list pointer now they have all been added.  */
+      lang_list_remove_tail (stat_ptr, &added);
+      /* And detach the fresh ends of the file lists.  */
+      lang_list_remove_tail (&file_chain, &files);
+      lang_list_remove_tail (&input_file_chain, &inputfiles);
+      /* Were any new files added?  */
+      if (added.head != NULL)
+       {
+         /* If so, we will insert them into the statement list immediately
+            after the first input file that was claimed by the plugin.  */
+         plugin_insert = find_replacements_insert_point ();
+         /* If a plugin adds input files without having claimed any, we
+            don't really have a good idea where to place them.  Just putting
+            them at the start or end of the list is liable to leave them
+            outside the crtbegin...crtend range.  */
+         ASSERT (plugin_insert != NULL);
+         /* Splice the new statement list into the old one.  */
+         lang_list_insert_after (stat_ptr, &added,
+                                 &plugin_insert->header.next);
+         /* Likewise for the file chains.  */
+         lang_list_insert_after (&input_file_chain, &inputfiles,
+                                 &plugin_insert->next_real_file);
+         /* We must be careful when relinking file_chain; we may need to
+            insert the new files at the head of the list if the insert
+            point chosen is the dummy first input file.  */
+         if (plugin_insert->filename)
+           lang_list_insert_after (&file_chain, &files, &plugin_insert->next);
+         else
+           lang_list_insert_after (&file_chain, &files, &file_chain.head);
+
+         /* Rescan archives in case new undefined symbols have appeared.  */
+         open_input_bfds (statement_list.head, OPEN_BFD_RESCAN);
+       }
+    }
+#endif /* ENABLE_PLUGINS */
+
+  /* Make sure that nobody has tried to add a symbol to this list
+     before now.  */
+  ASSERT (link_info.gc_sym_list == NULL);
 
   link_info.gc_sym_list = &entry_symbol;
+
   if (entry_symbol.name == NULL)
-    link_info.gc_sym_list = ldlang_undef_chain_list_head;
+    {
+      link_info.gc_sym_list = ldlang_undef_chain_list_head;
+
+      /* entry_symbol is normally initialied by a ENTRY definition in the
+        linker script or the -e command line option.  But if neither of
+        these have been used, the target specific backend may still have
+        provided an entry symbol via a call to lang_default_entry().
+        Unfortunately this value will not be processed until lang_end()
+        is called, long after this function has finished.  So detect this
+        case here and add the target's entry symbol to the list of starting
+        points for garbage collection resolution.  */
+      lang_add_gc_name (entry_symbol_default);
+    }
+
+  lang_add_gc_name (link_info.init_function);
+  lang_add_gc_name (link_info.fini_function);
 
   ldemul_after_open ();
+  if (config.map_file != NULL)
+    lang_print_asneeded ();
 
   bfd_section_already_linked_table_free ();
 
@@ -6102,17 +6933,28 @@ lang_process (void)
      files.  */
   ldctor_build_sets ();
 
-  /* Remove unreferenced sections if asked to.  */
-  lang_gc_sections ();
+  /* PR 13683: We must rerun the assignments prior to running garbage
+     collection in order to make sure that all symbol aliases are resolved.  */
+  lang_do_assignments (lang_mark_phase_enum);
+
+  lang_do_memory_regions();
+  expld.phase = lang_first_phase_enum;
 
   /* Size up the common data.  */
   lang_common ();
 
+  /* Remove unreferenced sections if asked to.  */
+  lang_gc_sections ();
+
+  /* Check relocations.  */
+  lang_check_relocs ();
+
   /* Update wild statements.  */
   update_wild_statements (statement_list.head);
 
   /* Run through the contours of the script and attach input sections
      to the correct output sections.  */
+  lang_statement_iteration++;
   map_input_to_output_sections (statement_list.head, NULL, NULL);
 
   process_insert_statements ();
@@ -6120,7 +6962,7 @@ lang_process (void)
   /* Find any sections not attached explicitly and handle them.  */
   lang_place_orphans ();
 
-  if (! link_info.relocatable)
+  if (!bfd_link_relocatable (&link_info))
     {
       asection *found;
 
@@ -6151,35 +6993,14 @@ lang_process (void)
   lang_record_phdrs ();
 
   /* Check relro sections.  */
-  if (link_info.relro && ! link_info.relocatable)
+  if (link_info.relro && !bfd_link_relocatable (&link_info))
     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)
-    {
-      /* We may need more than one relaxation pass.  */
-      int i = link_info.relax_pass;
-
-      /* The backend can use it to determine the current pass.  */
-      link_info.relax_pass = 0;
-
-      while (i--)
-       {
-         relax_sections ();
-         link_info.relax_pass++;
-       }
-
-      /* Final extra sizing to report errors.  */
-      lang_do_assignments ();
-      lang_reset_memory_regions ();
-      lang_size_sections (NULL, TRUE);
-    }
+  lang_size_sections (NULL, !RELAXATION_ENABLED);
 
   /* See if anything special should be done now we know how big
-     everything is.  */
+     everything is.  This is where relaxation is done.  */
   ldemul_after_allocation ();
 
   /* Fix any .startof. or .sizeof. symbols.  */
@@ -6187,16 +7008,20 @@ lang_process (void)
 
   /* Do all the assignments, now that we know the final resting places
      of all the symbols.  */
-
-  lang_do_assignments ();
+  lang_do_assignments (lang_final_phase_enum);
 
   ldemul_finish ();
 
+  /* Convert absolute symbols to section relative.  */
+  ldexp_finalize_syms ();
+
   /* Make sure that the section addresses make sense.  */
-  if (! link_info.relocatable
-      && command_line.check_section_addresses)
+  if (command_line.check_section_addresses)
     lang_check_section_addresses ();
 
+  /* Check any required symbols are known.  */
+  ldlang_check_require_defined_symbols ();
+
   lang_end ();
 }
 
@@ -6208,7 +7033,7 @@ lang_add_wild (struct wildcard_spec *filespec,
               bfd_boolean keep_sections)
 {
   struct wildcard_list *curr, *next;
-  lang_wild_statement_type *new;
+  lang_wild_statement_type *new_stmt;
 
   /* Reverse the list as the parser puts it back to front.  */
   for (curr = section_list, section_list = NULL;
@@ -6226,22 +7051,24 @@ lang_add_wild (struct wildcard_spec *filespec,
     {
       if (strcmp (filespec->name, "*") == 0)
        filespec->name = NULL;
-      else if (! wildcardp (filespec->name))
+      else if (!wildcardp (filespec->name))
        lang_has_input_file = TRUE;
     }
 
-  new = new_stat (lang_wild_statement, stat_ptr);
-  new->filename = NULL;
-  new->filenames_sorted = FALSE;
+  new_stmt = new_stat (lang_wild_statement, stat_ptr);
+  new_stmt->filename = NULL;
+  new_stmt->filenames_sorted = FALSE;
+  new_stmt->section_flag_list = NULL;
   if (filespec != NULL)
     {
-      new->filename = filespec->name;
-      new->filenames_sorted = filespec->sorted == by_name;
+      new_stmt->filename = filespec->name;
+      new_stmt->filenames_sorted = filespec->sorted == by_name;
+      new_stmt->section_flag_list = filespec->section_flag_list;
     }
-  new->section_list = section_list;
-  new->keep_sections = keep_sections;
-  lang_list_init (&new->children);
-  analyze_walk_wild_section_handler (new);
+  new_stmt->section_list = section_list;
+  new_stmt->keep_sections = keep_sections;
+  lang_list_init (&new_stmt->children);
+  analyze_walk_wild_section_handler (new_stmt);
 }
 
 void
@@ -6266,7 +7093,7 @@ lang_add_entry (const char *name, bfd_boolean cmdline)
 {
   if (entry_symbol.name == NULL
       || cmdline
-      || ! entry_from_cmdline)
+      || !entry_from_cmdline)
     {
       entry_symbol.name = name;
       entry_from_cmdline = cmdline;
@@ -6286,10 +7113,10 @@ lang_default_entry (const char *name)
 void
 lang_add_target (const char *name)
 {
-  lang_target_statement_type *new;
+  lang_target_statement_type *new_stmt;
 
-  new = new_stat (lang_target_statement, stat_ptr);
-  new->target = name;
+  new_stmt = new_stat (lang_target_statement, stat_ptr);
+  new_stmt->target = name;
 }
 
 void
@@ -6310,20 +7137,20 @@ lang_add_map (const char *name)
 void
 lang_add_fill (fill_type *fill)
 {
-  lang_fill_statement_type *new;
+  lang_fill_statement_type *new_stmt;
 
-  new = new_stat (lang_fill_statement, stat_ptr);
-  new->fill = fill;
+  new_stmt = new_stat (lang_fill_statement, stat_ptr);
+  new_stmt->fill = fill;
 }
 
 void
 lang_add_data (int type, union etree_union *exp)
 {
-  lang_data_statement_type *new;
+  lang_data_statement_type *new_stmt;
 
-  new = new_stat (lang_data_statement, stat_ptr);
-  new->exp = exp;
-  new->type = type;
+  new_stmt = new_stat (lang_data_statement, stat_ptr);
+  new_stmt->exp = exp;
+  new_stmt->type = type;
 }
 
 /* Create a new reloc statement.  RELOC is the BFD relocation type to
@@ -6356,11 +7183,11 @@ lang_add_reloc (bfd_reloc_code_real_type reloc,
 lang_assignment_statement_type *
 lang_add_assignment (etree_type *exp)
 {
-  lang_assignment_statement_type *new;
+  lang_assignment_statement_type *new_stmt;
 
-  new = new_stat (lang_assignment_statement, stat_ptr);
-  new->exp = exp;
-  return new;
+  new_stmt = new_stat (lang_assignment_statement, stat_ptr);
+  new_stmt->exp = exp;
+  return new_stmt;
 }
 
 void
@@ -6372,15 +7199,13 @@ lang_add_attribute (enum statement_enum attribute)
 void
 lang_startup (const char *name)
 {
-  if (startup_file != NULL)
+  if (first_file->filename != NULL)
     {
       einfo (_("%P%F: multiple STARTUP files\n"));
     }
   first_file->filename = name;
   first_file->local_sym_name = name;
-  first_file->real = TRUE;
-
-  startup_file = name;
+  first_file->flags.real = TRUE;
 }
 
 void
@@ -6415,14 +7240,15 @@ lang_get_regions (lang_memory_region_type **region,
      has been specified, then use the load region for the runtime region
      as well.  */
   if (lma_memspec != NULL
-      && ! have_vma
+      && !have_vma
       && strcmp (memspec, DEFAULT_MEMORY_REGION) == 0)
     *region = *lma_region;
   else
     *region = lang_memory_region_lookup (memspec, FALSE);
 
   if (have_lma && lma_memspec != 0)
-    einfo (_("%X%P:%S: section has both a load address and a load region\n"));
+    einfo (_("%X%P:%S: section has both a load address and a load region\n"),
+          NULL);
 }
 
 void
@@ -6435,72 +7261,20 @@ lang_leave_output_section_statement (fill_type *fill, const char *memspec,
                    memspec, lma_memspec,
                    current_section->load_base != NULL,
                    current_section->addr_tree != NULL);
-  current_section->fill = fill;
-  current_section->phdrs = phdrs;
-  stat_ptr = &statement_list;
-}
-
-/* Create an absolute symbol with the given name with the value of the
-   address of first byte of the section named.
-
-   If the symbol already exists, then do nothing.  */
-
-void
-lang_abs_symbol_at_beginning_of (const char *secname, const char *name)
-{
-  struct bfd_link_hash_entry *h;
-
-  h = bfd_link_hash_lookup (link_info.hash, name, TRUE, TRUE, TRUE);
-  if (h == NULL)
-    einfo (_("%P%F: bfd_link_hash_lookup failed: %E\n"));
-
-  if (h->type == bfd_link_hash_new
-      || h->type == bfd_link_hash_undefined)
-    {
-      asection *sec;
 
-      h->type = bfd_link_hash_defined;
+  /* If this section has no load region or base, but uses the same
+     region as the previous section, then propagate the previous
+     section's load region.  */
 
-      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 (link_info.output_bfd, sec);
-
-      h->u.def.section = bfd_abs_section_ptr;
-    }
-}
-
-/* Create an absolute symbol with the given name with the value of the
-   address of the first byte after the end of the section named.
-
-   If the symbol already exists, then do nothing.  */
-
-void
-lang_abs_symbol_at_end_of (const char *secname, const char *name)
-{
-  struct bfd_link_hash_entry *h;
-
-  h = bfd_link_hash_lookup (link_info.hash, name, TRUE, TRUE, TRUE);
-  if (h == NULL)
-    einfo (_("%P%F: bfd_link_hash_lookup failed: %E\n"));
-
-  if (h->type == bfd_link_hash_new
-      || h->type == bfd_link_hash_undefined)
-    {
-      asection *sec;
-
-      h->type = bfd_link_hash_defined;
-
-      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 (link_info.output_bfd, sec)
-                         + TO_ADDR (sec->size));
+  if (current_section->lma_region == NULL
+      && current_section->load_base == NULL
+      && current_section->addr_tree == NULL
+      && current_section->region == current_section->prev->region)
+    current_section->lma_region = current_section->prev->lma_region;
 
-      h->u.def.section = bfd_abs_section_ptr;
-    }
+  current_section->fill = fill;
+  current_section->phdrs = phdrs;
+  pop_stat_ptr ();
 }
 
 void
@@ -6536,11 +7310,11 @@ lang_add_output_format (const char *format,
 void
 lang_add_insert (const char *where, int is_before)
 {
-  lang_insert_statement_type *new;
+  lang_insert_statement_type *new_stmt;
 
-  new = new_stat (lang_insert_statement, stat_ptr);
-  new->where = where;
-  new->is_before = is_before;
+  new_stmt = new_stat (lang_insert_statement, stat_ptr);
+  new_stmt->where = where;
+  new_stmt->is_before = is_before;
   saved_script_handle = previous_script_handle;
 }
 
@@ -6554,7 +7328,7 @@ lang_enter_group (void)
 
   g = new_stat (lang_group_statement, stat_ptr);
   lang_list_init (&g->children);
-  stat_ptr = &g->children;
+  push_stat_ptr (&g->children);
 }
 
 /* Leave a group.  This just resets stat_ptr to start writing to the
@@ -6565,7 +7339,7 @@ lang_enter_group (void)
 void
 lang_leave_group (void)
 {
-  stat_ptr = &statement_list;
+  pop_stat_ptr ();
 }
 
 /* Add a new program header.  This is called for each entry in a PHDRS
@@ -6580,8 +7354,9 @@ lang_new_phdr (const char *name,
               etree_type *flags)
 {
   struct lang_phdr *n, **pp;
+  bfd_boolean hdrs;
 
-  n = stat_alloc (sizeof (struct lang_phdr));
+  n = (struct lang_phdr *) stat_alloc (sizeof (struct lang_phdr));
   n->next = NULL;
   n->name = name;
   n->type = exp_get_value_int (type, 0, "program header type");
@@ -6590,8 +7365,18 @@ lang_new_phdr (const char *name,
   n->at = at;
   n->flags = flags;
 
+  hdrs = n->type == 1 && (phdrs || filehdr);
+
   for (pp = &lang_phdr_list; *pp != NULL; pp = &(*pp)->next)
-    ;
+    if (hdrs
+       && (*pp)->type == 1
+       && !((*pp)->filehdr || (*pp)->phdrs))
+      {
+       einfo (_("%X%P:%S: PHDRS and FILEHDR are not supported"
+                " when prior PT_LOAD headers lack them\n"), NULL);
+       hdrs = FALSE;
+      }
+
   *pp = n;
 }
 
@@ -6608,7 +7393,7 @@ lang_record_phdrs (void)
   lang_output_section_statement_type *os;
 
   alc = 10;
-  secs = xmalloc (alc * sizeof (asection *));
+  secs = (asection **) xmalloc (alc * sizeof (asection *));
   last = NULL;
 
   for (l = lang_phdr_list; l != NULL; l = l->next)
@@ -6624,7 +7409,7 @@ lang_record_phdrs (void)
        {
          lang_output_section_phdr_list *pl;
 
-         if (os->constraint == -1)
+         if (os->constraint < 0)
            continue;
 
          pl = os->phdrs;
@@ -6637,9 +7422,13 @@ lang_record_phdrs (void)
                  || (os->bfd_section->flags & SEC_ALLOC) == 0)
                continue;
 
+             /* Don't add orphans to PT_INTERP header.  */
+             if (l->type == 3)
+               continue;
+
              if (last == NULL)
                {
-                 lang_output_section_statement_type * tmp_os;
+                 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
@@ -6671,7 +7460,8 @@ lang_record_phdrs (void)
                  if (c >= alc)
                    {
                      alc *= 2;
-                     secs = xrealloc (secs, alc * sizeof (asection *));
+                     secs = (asection **) xrealloc (secs,
+                                                    alc * sizeof (asection *));
                    }
                  secs[c] = os->bfd_section;
                  ++c;
@@ -6690,9 +7480,9 @@ lang_record_phdrs (void)
       else
        at = exp_get_vma (l->at, 0, "phdr load address");
 
-      if (! bfd_record_phdr (link_info.output_bfd, l->type,
-                            l->flags != NULL, flags, l->at != NULL,
-                            at, l->filehdr, l->phdrs, c, secs))
+      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"));
     }
 
@@ -6705,14 +7495,14 @@ lang_record_phdrs (void)
     {
       lang_output_section_phdr_list *pl;
 
-      if (os->constraint == -1
+      if (os->constraint < 0
          || os->bfd_section == NULL)
        continue;
 
       for (pl = os->phdrs;
           pl != NULL;
           pl = pl->next)
-       if (! pl->used && strcmp (pl->name, "NONE") != 0)
+       if (!pl->used && strcmp (pl->name, "NONE") != 0)
          einfo (_("%X%P: section `%s' assigned to non-existent phdr `%s'\n"),
                 os->name, pl->name);
     }
@@ -6725,14 +7515,24 @@ lang_add_nocrossref (lang_nocrossref_type *l)
 {
   struct lang_nocrossrefs *n;
 
-  n = xmalloc (sizeof *n);
+  n = (struct lang_nocrossrefs *) xmalloc (sizeof *n);
   n->next = nocrossref_list;
   n->list = l;
+  n->onlyfirst = FALSE;
   nocrossref_list = n;
 
   /* Set notice_all so that we get informed about all symbols.  */
   link_info.notice_all = TRUE;
 }
+
+/* Record a section that cannot be referenced from a list of sections.  */
+
+void
+lang_add_nocrossref_to (lang_nocrossref_type *l)
+{
+  lang_add_nocrossref (l);
+  nocrossref_list->onlyfirst = TRUE;
+}
 \f
 /* Overlay handling.  We handle overlays with some static variables.  */
 
@@ -6778,7 +7578,7 @@ lang_enter_overlay_section (const char *name)
   etree_type *size;
 
   lang_enter_output_section_statement (name, overlay_vma, overlay_section,
-                                      0, overlay_subalign, 0, 0);
+                                      0, overlay_subalign, 0, 0, 0);
 
   /* If this is the first section, then base the VMA of future
      sections on this one.  This will work correctly even if `.' is
@@ -6787,7 +7587,7 @@ lang_enter_overlay_section (const char *name)
     overlay_vma = exp_nameop (ADDR, name);
 
   /* Remember the section.  */
-  n = xmalloc (sizeof *n);
+  n = (struct overlay_list *) xmalloc (sizeof *n);
   n->os = current_section;
   n->next = overlay_list;
   overlay_list = n;
@@ -6823,20 +7623,20 @@ lang_leave_overlay_section (fill_type *fill,
 
   /* Define the magic symbols.  */
 
-  clean = xmalloc (strlen (name) + 1);
+  clean = (char *) xmalloc (strlen (name) + 1);
   s2 = clean;
   for (s1 = name; *s1 != '\0'; s1++)
     if (ISALNUM (*s1) || *s1 == '_')
       *s2++ = *s1;
   *s2 = '\0';
 
-  buf = xmalloc (strlen (clean) + sizeof "__load_start_");
+  buf = (char *) xmalloc (strlen (clean) + sizeof "__load_start_");
   sprintf (buf, "__load_start_%s", clean);
   lang_add_assignment (exp_provide (buf,
                                    exp_nameop (LOADADDR, name),
                                    FALSE));
 
-  buf = xmalloc (strlen (clean) + sizeof "__load_stop_");
+  buf = (char *) xmalloc (strlen (clean) + sizeof "__load_stop_");
   sprintf (buf, "__load_stop_%s", clean);
   lang_add_assignment (exp_provide (buf,
                                    exp_binop ('+',
@@ -6872,8 +7672,11 @@ lang_leave_overlay (etree_type *lma_expr,
   /* After setting the size of the last section, set '.' to end of the
      overlay region.  */
   if (overlay_list != NULL)
-    overlay_list->os->update_dot_tree
-      = exp_assop ('=', ".", exp_binop ('+', overlay_vma, overlay_max));
+    {
+      overlay_list->os->update_dot = 1;
+      overlay_list->os->update_dot_tree
+       = exp_assign (".", exp_binop ('+', overlay_vma, overlay_max), FALSE);
+    }
 
   l = overlay_list;
   while (l != NULL)
@@ -6902,7 +7705,7 @@ lang_leave_overlay (etree_type *lma_expr,
        {
          lang_nocrossref_type *nc;
 
-         nc = xmalloc (sizeof *nc);
+         nc = (lang_nocrossref_type *) xmalloc (sizeof *nc);
          nc->name = l->os->name;
          nc->next = nocrossref;
          nocrossref = nc;
@@ -6923,10 +7726,6 @@ lang_leave_overlay (etree_type *lma_expr,
 \f
 /* Version handling.  This is only useful for ELF.  */
 
-/* This global variable holds the version tree that we build.  */
-
-struct bfd_elf_version_tree *lang_elf_version_info;
-
 /* If PREV is NULL, return first version pattern matching particular symbol.
    If PREV is non-NULL, return first version pattern matching particular
    symbol after PREV (previously returned by lang_vers_match).  */
@@ -6936,24 +7735,34 @@ lang_vers_match (struct bfd_elf_version_expr_head *head,
                 struct bfd_elf_version_expr *prev,
                 const char *sym)
 {
+  const char *c_sym;
   const char *cxx_sym = sym;
   const char *java_sym = sym;
   struct bfd_elf_version_expr *expr = NULL;
+  enum demangling_styles curr_style;
+
+  curr_style = CURRENT_DEMANGLING_STYLE;
+  cplus_demangle_set_style (no_demangling);
+  c_sym = bfd_demangle (link_info.output_bfd, sym, DMGL_NO_OPTS);
+  if (!c_sym)
+    c_sym = sym;
+  cplus_demangle_set_style (curr_style);
 
   if (head->mask & BFD_ELF_VERSION_CXX_TYPE)
     {
-      cxx_sym = cplus_demangle (sym, DMGL_PARAMS | DMGL_ANSI);
+      cxx_sym = bfd_demangle (link_info.output_bfd, sym,
+                             DMGL_PARAMS | DMGL_ANSI);
       if (!cxx_sym)
        cxx_sym = sym;
     }
   if (head->mask & BFD_ELF_VERSION_JAVA_TYPE)
     {
-      java_sym = cplus_demangle (sym, DMGL_JAVA);
+      java_sym = bfd_demangle (link_info.output_bfd, sym, DMGL_JAVA);
       if (!java_sym)
        java_sym = sym;
     }
 
-  if (head->htab && (prev == NULL || prev->symbol))
+  if (head->htab && (prev == NULL || prev->literal))
     {
       struct bfd_elf_version_expr e;
 
@@ -6962,9 +7771,10 @@ lang_vers_match (struct bfd_elf_version_expr_head *head,
        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)
+             e.pattern = c_sym;
+             expr = (struct bfd_elf_version_expr *)
+                 htab_find ((htab_t) head->htab, &e);
+             while (expr && strcmp (expr->pattern, c_sym) == 0)
                if (expr->mask == BFD_ELF_VERSION_C_TYPE)
                  goto out_ret;
                else
@@ -6974,9 +7784,10 @@ lang_vers_match (struct bfd_elf_version_expr_head *head,
        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)
+             e.pattern = cxx_sym;
+             expr = (struct bfd_elf_version_expr *)
+                 htab_find ((htab_t) head->htab, &e);
+             while (expr && strcmp (expr->pattern, cxx_sym) == 0)
                if (expr->mask == BFD_ELF_VERSION_CXX_TYPE)
                  goto out_ret;
                else
@@ -6986,9 +7797,10 @@ lang_vers_match (struct bfd_elf_version_expr_head *head,
        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)
+             e.pattern = java_sym;
+             expr = (struct bfd_elf_version_expr *)
+                 htab_find ((htab_t) head->htab, &e);
+             while (expr && strcmp (expr->pattern, java_sym) == 0)
                if (expr->mask == BFD_ELF_VERSION_JAVA_TYPE)
                  goto out_ret;
                else
@@ -7001,7 +7813,7 @@ lang_vers_match (struct bfd_elf_version_expr_head *head,
     }
 
   /* Finally, try the wildcards.  */
-  if (prev == NULL || prev->symbol)
+  if (prev == NULL || prev->literal)
     expr = head->remaining;
   else
     expr = prev->next;
@@ -7020,12 +7832,14 @@ lang_vers_match (struct bfd_elf_version_expr_head *head,
       else if (expr->mask == BFD_ELF_VERSION_CXX_TYPE)
        s = cxx_sym;
       else
-       s = sym;
+       s = c_sym;
       if (fnmatch (expr->pattern, s, 0) == 0)
        break;
     }
 
  out_ret:
+  if (c_sym != sym)
+    free ((char *) c_sym);
   if (cxx_sym != sym)
     free ((char *) cxx_sym);
   if (java_sym != sym)
@@ -7034,35 +7848,37 @@ lang_vers_match (struct bfd_elf_version_expr_head *head,
 }
 
 /* Return NULL if the PATTERN argument is a glob pattern, otherwise,
-   return a string pointing to the symbol name.  */
+   return a pointer to the symbol name with any backslash quotes removed.  */
 
 static const char *
 realsymbol (const char *pattern)
 {
   const char *p;
   bfd_boolean changed = FALSE, backslash = FALSE;
-  char *s, *symbol = xmalloc (strlen (pattern) + 1);
+  char *s, *symbol = (char *) xmalloc (strlen (pattern) + 1);
 
   for (p = pattern, s = symbol; *p != '\0'; ++p)
     {
       /* It is a glob pattern only if there is no preceding
         backslash.  */
-      if (! backslash && (*p == '?' || *p == '*' || *p == '['))
-       {
-         free (symbol);
-         return NULL;
-       }
-
       if (backslash)
        {
          /* Remove the preceding backslash.  */
          *(s - 1) = *p;
+         backslash = FALSE;
          changed = TRUE;
        }
       else
-       *s++ = *p;
+       {
+         if (*p == '?' || *p == '*' || *p == '[')
+           {
+             free (symbol);
+             return NULL;
+           }
 
-      backslash = *p == '\\';
+         *s++ = *p;
+         backslash = *p == '\\';
+       }
     }
 
   if (changed)
@@ -7077,24 +7893,29 @@ realsymbol (const char *pattern)
     }
 }
 
-/* This is called for each variable name or match expression.  NEW is
+/* This is called for each variable name or match expression.  NEW_NAME is
    the name of the symbol to match, or, if LITERAL_P is FALSE, a glob
    pattern to be matched against symbol names.  */
 
 struct bfd_elf_version_expr *
 lang_new_vers_pattern (struct bfd_elf_version_expr *orig,
-                      const char *new,
+                      const char *new_name,
                       const char *lang,
                       bfd_boolean literal_p)
 {
   struct bfd_elf_version_expr *ret;
 
-  ret = xmalloc (sizeof *ret);
+  ret = (struct bfd_elf_version_expr *) xmalloc (sizeof *ret);
   ret->next = orig;
-  ret->pattern = literal_p ? NULL : new;
   ret->symver = 0;
   ret->script = 0;
-  ret->symbol = literal_p ? new : realsymbol (new);
+  ret->literal = TRUE;
+  ret->pattern = literal_p ? new_name : realsymbol (new_name);
+  if (ret->pattern == NULL)
+    {
+      ret->pattern = new_name;
+      ret->literal = FALSE;
+    }
 
   if (lang == NULL || strcasecmp (lang, "C") == 0)
     ret->mask = BFD_ELF_VERSION_C_TYPE;
@@ -7121,7 +7942,7 @@ lang_new_vers_node (struct bfd_elf_version_expr *globals,
 {
   struct bfd_elf_version_tree *ret;
 
-  ret = xcalloc (1, sizeof *ret);
+  ret = (struct bfd_elf_version_tree *) xcalloc (1, sizeof *ret);
   ret->globals.list = globals;
   ret->locals.list = locals;
   ret->match = lang_vers_match;
@@ -7136,18 +7957,21 @@ static int version_index;
 static hashval_t
 version_expr_head_hash (const void *p)
 {
-  const struct bfd_elf_version_expr *e = p;
+  const struct bfd_elf_version_expr *e =
+      (const struct bfd_elf_version_expr *) p;
 
-  return htab_hash_string (e->symbol);
+  return htab_hash_string (e->pattern);
 }
 
 static int
 version_expr_head_eq (const void *p1, const void *p2)
 {
-  const struct bfd_elf_version_expr *e1 = p1;
-  const struct bfd_elf_version_expr *e2 = p2;
+  const struct bfd_elf_version_expr *e1 =
+      (const struct bfd_elf_version_expr *) p1;
+  const struct bfd_elf_version_expr *e2 =
+      (const struct bfd_elf_version_expr *) p2;
 
-  return strcmp (e1->symbol, e2->symbol) == 0;
+  return strcmp (e1->pattern, e2->pattern) == 0;
 }
 
 static void
@@ -7159,7 +7983,7 @@ lang_finalize_version_expr_head (struct bfd_elf_version_expr_head *head)
 
   for (e = head->list; e; e = e->next)
     {
-      if (e->symbol)
+      if (e->literal)
        count++;
       head->mask |= e->mask;
     }
@@ -7173,20 +7997,20 @@ lang_finalize_version_expr_head (struct bfd_elf_version_expr_head *head)
       for (e = head->list; e; e = next)
        {
          next = e->next;
-         if (!e->symbol)
+         if (!e->literal)
            {
              *remaining_loc = e;
              remaining_loc = &e->next;
            }
          else
            {
-             void **loc = htab_find_slot (head->htab, e, INSERT);
+             void **loc = htab_find_slot ((htab_t) head->htab, e, INSERT);
 
              if (*loc)
                {
                  struct bfd_elf_version_expr *e1, *last;
 
-                 e1 = *loc;
+                 e1 = (struct bfd_elf_version_expr *) *loc;
                  last = NULL;
                  do
                    {
@@ -7198,14 +8022,14 @@ lang_finalize_version_expr_head (struct bfd_elf_version_expr_head *head)
                      last = e1;
                      e1 = e1->next;
                    }
-                 while (e1 && strcmp (e1->symbol, e->symbol) == 0);
+                 while (e1 && strcmp (e1->pattern, e->pattern) == 0);
 
                  if (last == NULL)
                    {
                      /* This is a duplicate.  */
                      /* FIXME: Memory leak.  Sometimes pattern is not
                         xmalloced alone, but in larger chunk of memory.  */
-                     /* free (e->symbol); */
+                     /* free (e->pattern); */
                      free (e);
                    }
                  else
@@ -7243,8 +8067,8 @@ lang_register_vers_node (const char *name,
   if (name == NULL)
     name = "";
 
-  if ((name[0] == '\0' && lang_elf_version_info != NULL)
-      || (lang_elf_version_info && lang_elf_version_info->name[0] == '\0'))
+  if (link_info.version_info != NULL
+      && (name[0] == '\0' || link_info.version_info->name[0] == '\0'))
     {
       einfo (_("%X%P: anonymous version tag cannot be combined"
               " with other version tags\n"));
@@ -7253,7 +8077,7 @@ lang_register_vers_node (const char *name,
     }
 
   /* Make sure this node has a unique name.  */
-  for (t = lang_elf_version_info; t != NULL; t = t->next)
+  for (t = link_info.version_info; t != NULL; t = t->next)
     if (strcmp (t->name, name) == 0)
       einfo (_("%X%P: duplicate version tag `%s'\n"), name);
 
@@ -7265,22 +8089,23 @@ lang_register_vers_node (const char *name,
 
   for (e1 = version->globals.list; e1 != NULL; e1 = e1->next)
     {
-      for (t = lang_elf_version_info; t != NULL; t = t->next)
+      for (t = link_info.version_info; t != NULL; t = t->next)
        {
          struct bfd_elf_version_expr *e2;
 
-         if (t->locals.htab && e1->symbol)
+         if (t->locals.htab && e1->literal)
            {
-             e2 = htab_find (t->locals.htab, e1);
-             while (e2 && strcmp (e1->symbol, e2->symbol) == 0)
+             e2 = (struct bfd_elf_version_expr *)
+                 htab_find ((htab_t) t->locals.htab, e1);
+             while (e2 && strcmp (e1->pattern, e2->pattern) == 0)
                {
                  if (e1->mask == e2->mask)
                    einfo (_("%X%P: duplicate expression `%s'"
-                            " in version information\n"), e1->symbol);
+                            " in version information\n"), e1->pattern);
                  e2 = e2->next;
                }
            }
-         else if (!e1->symbol)
+         else if (!e1->literal)
            for (e2 = t->locals.remaining; e2 != NULL; e2 = e2->next)
              if (strcmp (e1->pattern, e2->pattern) == 0
                  && e1->mask == e2->mask)
@@ -7291,23 +8116,24 @@ lang_register_vers_node (const char *name,
 
   for (e1 = version->locals.list; e1 != NULL; e1 = e1->next)
     {
-      for (t = lang_elf_version_info; t != NULL; t = t->next)
+      for (t = link_info.version_info; t != NULL; t = t->next)
        {
          struct bfd_elf_version_expr *e2;
 
-         if (t->globals.htab && e1->symbol)
+         if (t->globals.htab && e1->literal)
            {
-             e2 = htab_find (t->globals.htab, e1);
-             while (e2 && strcmp (e1->symbol, e2->symbol) == 0)
+             e2 = (struct bfd_elf_version_expr *)
+                 htab_find ((htab_t) t->globals.htab, e1);
+             while (e2 && strcmp (e1->pattern, e2->pattern) == 0)
                {
                  if (e1->mask == e2->mask)
                    einfo (_("%X%P: duplicate expression `%s'"
                             " in version information\n"),
-                          e1->symbol);
+                          e1->pattern);
                  e2 = e2->next;
                }
            }
-         else if (!e1->symbol)
+         else if (!e1->literal)
            for (e2 = t->globals.remaining; e2 != NULL; e2 = e2->next)
              if (strcmp (e1->pattern, e2->pattern) == 0
                  && e1->mask == e2->mask)
@@ -7326,7 +8152,7 @@ lang_register_vers_node (const char *name,
   else
     version->vernum = 0;
 
-  for (pp = &lang_elf_version_info; *pp != NULL; pp = &(*pp)->next)
+  for (pp = &link_info.version_info; *pp != NULL; pp = &(*pp)->next)
     ;
   *pp = version;
 }
@@ -7339,10 +8165,10 @@ lang_add_vers_depend (struct bfd_elf_version_deps *list, const char *name)
   struct bfd_elf_version_deps *ret;
   struct bfd_elf_version_tree *t;
 
-  ret = xmalloc (sizeof *ret);
+  ret = (struct bfd_elf_version_deps *) xmalloc (sizeof *ret);
   ret->next = list;
 
-  for (t = lang_elf_version_info; t != NULL; t = t->next)
+  for (t = link_info.version_info; t != NULL; t = t->next)
     {
       if (strcmp (t->name, name) == 0)
        {
@@ -7353,6 +8179,7 @@ lang_add_vers_depend (struct bfd_elf_version_deps *list, const char *name)
 
   einfo (_("%X%P: unable to find version dependency `%s'\n"), name);
 
+  ret->version_needed = NULL;
   return ret;
 }
 
@@ -7371,7 +8198,7 @@ lang_do_version_exports_section (void)
        continue;
 
       len = sec->size;
-      contents = xmalloc (len);
+      contents = (char *) xmalloc (len);
       if (!bfd_get_section_contents (is->the_bfd, sec, contents, 0, len))
        einfo (_("%X%P: unable to read .exports section contents\n"), sec);
 
@@ -7393,6 +8220,39 @@ lang_do_version_exports_section (void)
                           lang_new_vers_node (greg, lreg), NULL);
 }
 
+/* Evaluate LENGTH and ORIGIN parts of MEMORY spec */
+
+static void
+lang_do_memory_regions (void)
+{
+  lang_memory_region_type *r = lang_memory_region_list;
+
+  for (; r != NULL; r = r->next)
+    {
+      if (r->origin_exp)
+       {
+         exp_fold_tree_no_dot (r->origin_exp);
+         if (expld.result.valid_p)
+           {
+             r->origin = expld.result.value;
+             r->current = r->origin;
+           }
+         else
+           einfo (_("%F%P: invalid origin for memory region %s\n"),
+                  r->name_list.name);
+       }
+      if (r->length_exp)
+       {
+         exp_fold_tree_no_dot (r->length_exp);
+         if (expld.result.valid_p)
+           r->length = expld.result.value;
+         else
+           einfo (_("%F%P: invalid length for memory region %s\n"),
+                  r->name_list.name);
+       }
+    }
+}
+
 void
 lang_add_unique (const char *name)
 {
@@ -7402,7 +8262,7 @@ lang_add_unique (const char *name)
     if (strcmp (ent->name, name) == 0)
       return;
 
-  ent = xmalloc (sizeof *ent);
+  ent = (struct unique_sections *) xmalloc (sizeof *ent);
   ent->name = xstrdup (name);
   ent->next = unique_section_list;
   unique_section_list = ent;
@@ -7425,7 +8285,7 @@ lang_append_dynamic_list (struct bfd_elf_version_expr *dynamic)
     {
       struct bfd_elf_dynamic_list *d;
 
-      d = xcalloc (1, sizeof *d);
+      d = (struct bfd_elf_dynamic_list *) xcalloc (1, sizeof *d);
       d->head.list = dynamic;
       d->match = lang_vers_match;
       link_info.dynamic_list = d;
@@ -7438,7 +8298,7 @@ lang_append_dynamic_list (struct bfd_elf_version_expr *dynamic)
 void
 lang_append_dynamic_list_cpp_typeinfo (void)
 {
-  const char * symbols [] =
+  const char *symbols[] =
     {
       "typeinfo name for*",
       "typeinfo for*"
@@ -7459,7 +8319,7 @@ lang_append_dynamic_list_cpp_typeinfo (void)
 void
 lang_append_dynamic_list_cpp_new (void)
 {
-  const char * symbols [] =
+  const char *symbols[] =
     {
       "operator new*",
       "operator delete*"
@@ -7473,3 +8333,70 @@ lang_append_dynamic_list_cpp_new (void)
 
   lang_append_dynamic_list (dynamic);
 }
+
+/* Scan a space and/or comma separated string of features.  */
+
+void
+lang_ld_feature (char *str)
+{
+  char *p, *q;
+
+  p = str;
+  while (*p)
+    {
+      char sep;
+      while (*p == ',' || ISSPACE (*p))
+       ++p;
+      if (!*p)
+       break;
+      q = p + 1;
+      while (*q && *q != ',' && !ISSPACE (*q))
+       ++q;
+      sep = *q;
+      *q = 0;
+      if (strcasecmp (p, "SANE_EXPR") == 0)
+       config.sane_expr = TRUE;
+      else
+       einfo (_("%X%P: unknown feature `%s'\n"), p);
+      *q = sep;
+      p = q;
+    }
+}
+
+/* Pretty print memory amount.  */
+
+static void
+lang_print_memory_size (bfd_vma sz)
+{
+  if ((sz & 0x3fffffff) == 0)
+    printf ("%10" BFD_VMA_FMT "u GB", sz >> 30);
+  else if ((sz & 0xfffff) == 0)
+    printf ("%10" BFD_VMA_FMT "u MB", sz >> 20);
+  else if ((sz & 0x3ff) == 0)
+    printf ("%10" BFD_VMA_FMT "u KB", sz >> 10);
+  else
+    printf (" %10" BFD_VMA_FMT "u B", sz);
+}
+
+/* Implement --print-memory-usage: disply per region memory usage.  */
+
+void
+lang_print_memory_usage (void)
+{
+  lang_memory_region_type *r;
+
+  printf ("Memory region         Used Size  Region Size  %%age Used\n");
+  for (r = lang_memory_region_list; r->next != NULL; r = r->next)
+    {
+      bfd_vma used_length = r->current - r->origin;
+      double percent;
+
+      printf ("%16s: ",r->name_list.name);
+      lang_print_memory_size (used_length);
+      lang_print_memory_size ((bfd_vma) r->length);
+
+      percent = used_length * 100.0 / r->length;
+
+      printf ("    %6.2f%%\n", percent);
+    }
+}
This page took 0.101827 seconds and 4 git commands to generate.