]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - ld/ldlang.c
ld: Support customized output section type
[thirdparty/binutils-gdb.git] / ld / ldlang.c
index 3bcab7a876e3036cf69c14daeeed0a0c7ef06845..1733f8e65c4d20786a5b7ad3eb896f378c3781ed 100644 (file)
@@ -1,5 +1,5 @@
 /* Linker command language support.
-   Copyright (C) 1991-2019 Free Software Foundation, Inc.
+   Copyright (C) 1991-2022 Free Software Foundation, Inc.
 
    This file is part of the GNU Binutils.
 
@@ -42,9 +42,9 @@
 #include "demangle.h"
 #include "hashtab.h"
 #include "elf-bfd.h"
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_PLUGINS
 #include "plugin.h"
-#endif /* ENABLE_PLUGINS */
+#endif /* BFD_SUPPORTS_PLUGINS */
 
 #ifndef offsetof
 #define offsetof(TYPE, MEMBER) ((size_t) & (((TYPE*) 0)->MEMBER))
@@ -63,19 +63,12 @@ static struct obstack map_obstack;
 #define obstack_chunk_alloc xmalloc
 #define obstack_chunk_free free
 static const char *entry_symbol_default = "start";
-static bfd_boolean map_head_is_link_order = FALSE;
+static bool map_head_is_link_order = false;
 static lang_output_section_statement_type *default_common_section;
-static bfd_boolean map_option_f;
+static bool map_option_f;
 static bfd_vma print_dot;
 static lang_input_statement_type *first_file;
 static const char *current_target;
-/* Header for list of statements corresponding to any files involved in the
-   link, either specified from the command-line or added implicitely (eg.
-   archive member used to resolved undefined symbol, wildcard statement from
-   linker script, etc.).  Next pointer is in next field of a
-   lang_statement_header_type (reached via header field in a
-   lang_statement_union).  */
-static lang_statement_list_type statement_list;
 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;
@@ -86,23 +79,30 @@ static unsigned int opb_shift = 0;
 static void exp_init_os (etree_type *);
 static lang_input_statement_type *lookup_name (const char *);
 static void insert_undefined (const char *);
-static bfd_boolean sort_def_symbol (struct bfd_link_hash_entry *, void *);
+static bool sort_def_symbol (struct bfd_link_hash_entry *, void *);
 static void print_statement (lang_statement_union_type *,
                             lang_output_section_statement_type *);
 static void print_statement_list (lang_statement_union_type *,
                                  lang_output_section_statement_type *);
 static void print_statements (void);
-static void print_input_section (asection *, bfd_boolean);
-static bfd_boolean lang_one_common (struct bfd_link_hash_entry *, void *);
+static void print_input_section (asection *, bool);
+static bool 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);
+static void lang_do_memory_regions (bool);
 
 /* Exported variables.  */
 const char *output_target;
 lang_output_section_statement_type *abs_output_section;
+/* Header for list of statements corresponding to any files involved in the
+   link, either specified from the command-line or added implicitely (eg.
+   archive member used to resolved undefined symbol, wildcard statement from
+   linker script, etc.).  Next pointer is in next field of a
+   lang_statement_header_type (reached via header field in a
+   lang_statement_union).  */
+lang_statement_list_type statement_list;
 lang_statement_list_type lang_os_list;
 lang_statement_list_type *stat_ptr = &statement_list;
 /* Header for list of statements corresponding to files used in the final
@@ -117,24 +117,30 @@ lang_statement_list_type file_chain = { NULL, NULL };
    lang_input_statement_type statement (reached via input_statement field in a
    lang_statement_union).  */
 lang_statement_list_type input_file_chain;
+static const char *current_input_file;
+struct bfd_elf_dynamic_list **current_dynamic_list_p;
 struct bfd_sym_chain entry_symbol = { NULL, NULL };
 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;
+bool entry_from_cmdline;
+bool lang_has_input_file = false;
+bool had_output_filename = false;
+bool lang_float_flag = false;
+bool delete_output_file_on_failure = false;
 struct lang_phdr *lang_phdr_list;
 struct lang_nocrossrefs *nocrossref_list;
 struct asneeded_minfo **asneeded_list_tail;
-static ctf_file_t *ctf_output;
+#ifdef ENABLE_LIBCTF
+static ctf_dict_t *ctf_output;
+#endif
 
- /* Functions that traverse the linker script and might evaluate
-    DEFINED() need to increment this at the start of the traversal.  */
+/* Functions that traverse the linker script and might evaluate
+   DEFINED() need to increment this at the start of the traversal.  */
 int lang_statement_iteration = 0;
 
+/* Count times through one_lang_size_sections_pass after mark phase.  */
+static int lang_sizing_iteration = 0;
+
 /* 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
@@ -151,8 +157,6 @@ int lang_statement_iteration = 0;
 #define outside_symbol_address(q) \
   ((q)->value + outside_section_address (q->section))
 
-#define SECTION_NAME_MAP_LENGTH (16)
-
 /* CTF sections smaller than this are not compressed: compression of
    dictionaries this small doesn't gain much, and this lets consumers mmap the
    sections directly out of the ELF file and use them with no decompression
@@ -173,6 +177,21 @@ name_match (const char *pattern, const char *name)
   return strcmp (pattern, name);
 }
 
+static char *
+ldirname (const char *name)
+{
+  const char *base = lbasename (name);
+  char *dirname;
+
+  while (base > name && IS_DIR_SEPARATOR (base[-1]))
+    --base;
+  if (base == name)
+    return strdup (".");
+  dirname = strdup (name);
+  dirname[base - name] = '\0';
+  return dirname;
+}
+
 /* If PATTERN is of the form archive:file, return a pointer to the
    separator.  If not, return NULL.  */
 
@@ -200,22 +219,22 @@ archive_path (const char *pattern)
 /* Given that FILE_SPEC results in a non-NULL SEP result from archive_path,
    return whether F matches FILE_SPEC.  */
 
-static bfd_boolean
+static bool
 input_statement_is_archive_path (const char *file_spec, char *sep,
                                 lang_input_statement_type *f)
 {
-  bfd_boolean match = FALSE;
+  bool match = false;
 
   if ((*(sep + 1) == 0
        || name_match (sep + 1, f->filename) == 0)
       && ((sep != file_spec)
          == (f->the_bfd != NULL && f->the_bfd->my_archive != NULL)))
     {
-      match = TRUE;
+      match = true;
 
       if (sep != file_spec)
        {
-         const char *aname = f->the_bfd->my_archive->filename;
+         const char *aname = bfd_get_filename (f->the_bfd->my_archive);
          *sep = 0;
          match = name_match (file_spec, aname) == 0;
          *sep = link_info.path_separator;
@@ -224,7 +243,7 @@ input_statement_is_archive_path (const char *file_spec, char *sep,
   return match;
 }
 
-static bfd_boolean
+static bool
 unique_section_p (const asection *sec,
                  const lang_output_section_statement_type *os)
 {
@@ -240,9 +259,9 @@ unique_section_p (const asection *sec,
   secnam = sec->name;
   for (unam = unique_section_list; unam; unam = unam->next)
     if (name_match (unam->name, secnam) == 0)
-      return TRUE;
+      return true;
 
-  return FALSE;
+  return false;
 }
 
 /* Generic traversal routines for finding matching sections.  */
@@ -250,7 +269,7 @@ unique_section_p (const asection *sec,
 /* Return true if FILE matches a pattern in EXCLUDE_LIST, otherwise return
    false.  */
 
-static bfd_boolean
+static bool
 walk_wild_file_in_exclude_list (struct name_list *exclude_list,
                                lang_input_statement_type *file)
 {
@@ -265,11 +284,11 @@ walk_wild_file_in_exclude_list (struct name_list *exclude_list,
       if (p != NULL)
        {
          if (input_statement_is_archive_path (list_tmp->name, p, file))
-           return TRUE;
+           return true;
        }
 
       else if (name_match (list_tmp->name, file->filename) == 0)
-       return TRUE;
+       return true;
 
       /* FIXME: Perhaps remove the following at some stage?  Matching
         unadorned archives like this was never documented and has
@@ -277,11 +296,11 @@ walk_wild_file_in_exclude_list (struct name_list *exclude_list,
       else if (file->the_bfd != NULL
               && file->the_bfd->my_archive != NULL
               && name_match (list_tmp->name,
-                             file->the_bfd->my_archive->filename) == 0)
-       return TRUE;
+                             bfd_get_filename (file->the_bfd->my_archive)) == 0)
+       return true;
     }
 
-  return FALSE;
+  return false;
 }
 
 /* Try processing a section against a wildcard.  This just calls
@@ -301,7 +320,7 @@ walk_wild_consider_section (lang_wild_statement_type *ptr,
   if (walk_wild_file_in_exclude_list (sec->spec.exclude_name_list, file))
     return;
 
-  (*callback) (ptr, sec, s, ptr->section_flag_list, file, data);
+  (*callback) (ptr, sec, s, file, data);
 }
 
 /* Lowest common denominator routine that can handle everything correctly,
@@ -320,11 +339,11 @@ walk_wild_section_general (lang_wild_statement_type *ptr,
     {
       sec = ptr->section_list;
       if (sec == NULL)
-       (*callback) (ptr, sec, s, ptr->section_flag_list, file, data);
+       (*callback) (ptr, sec, s, file, data);
 
       while (sec != NULL)
        {
-         bfd_boolean skip = FALSE;
+         bool skip = false;
 
          if (sec->spec.name != NULL)
            {
@@ -347,30 +366,30 @@ walk_wild_section_general (lang_wild_statement_type *ptr,
 typedef struct
 {
   asection *found_section;
-  bfd_boolean multiple_sections_found;
+  bool multiple_sections_found;
 } section_iterator_callback_data;
 
-static bfd_boolean
+static bool
 section_iterator_callback (bfd *abfd ATTRIBUTE_UNUSED, asection *s, void *data)
 {
   section_iterator_callback_data *d = (section_iterator_callback_data *) data;
 
   if (d->found_section != NULL)
     {
-      d->multiple_sections_found = TRUE;
-      return TRUE;
+      d->multiple_sections_found = true;
+      return true;
     }
 
   d->found_section = s;
-  return FALSE;
+  return false;
 }
 
 static asection *
 find_section (lang_input_statement_type *file,
              struct wildcard_list *sec,
-             bfd_boolean *multiple_sections_found)
+             bool *multiple_sections_found)
 {
-  section_iterator_callback_data cb_data = { NULL, FALSE };
+  section_iterator_callback_data cb_data = { NULL, false };
 
   bfd_get_section_by_name_if (file->the_bfd, sec->spec.name,
                              section_iterator_callback, &cb_data);
@@ -384,29 +403,29 @@ find_section (lang_input_statement_type *file,
 /* A simple wild is a literal string followed by a single '*',
    where the literal part is at least 4 characters long.  */
 
-static bfd_boolean
+static bool
 is_simple_wild (const char *name)
 {
   size_t len = strcspn (name, "*?[");
   return len >= 4 && name[len] == '*' && name[len + 1] == '\0';
 }
 
-static bfd_boolean
+static bool
 match_simple_wild (const char *pattern, const char *name)
 {
   /* The first four characters of the pattern are guaranteed valid
      non-wildcard characters.  So we can go faster.  */
   if (pattern[0] != name[0] || pattern[1] != name[1]
       || pattern[2] != name[2] || pattern[3] != name[3])
-    return FALSE;
+    return false;
 
   pattern += 4;
   name += 4;
   while (*pattern != '*')
     if (*name++ != *pattern++)
-      return FALSE;
+      return false;
 
-  return TRUE;
+  return true;
 }
 
 /* Return the numerical value of the init_priority attribute from
@@ -489,7 +508,7 @@ compare_section (sort_type sort, asection *asec, asection *bsec)
       /* Fall through.  */
 
     case by_name:
-sort_by_name:
+    sort_by_name:
       ret = strcmp (bfd_section_name (asec), bfd_section_name (bsec));
       break;
 
@@ -547,7 +566,6 @@ 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)
 {
@@ -564,6 +582,7 @@ output_section_callback_fast (lang_wild_statement_type *ptr,
   node->left = 0;
   node->right = 0;
   node->section = section;
+  node->pattern = ptr->section_list;
 
   tree = wild_sort_fast (ptr, sec, file, section);
   if (tree != NULL)
@@ -580,7 +599,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, NULL,
+  lang_add_section (&ptr->children, tree->section, tree->pattern, NULL,
                    (lang_output_section_statement_type *) output);
 
   if (tree->right)
@@ -603,7 +622,7 @@ walk_wild_section_specs1_wild0 (lang_wild_statement_type *ptr,
      (should be rare), we fall back to the general algorithm because
      we would otherwise have to sort the sections to make sure they
      get processed in the bfd's order.  */
-  bfd_boolean multiple_sections_found;
+  bool multiple_sections_found;
   struct wildcard_list *sec0 = ptr->handler_data[0];
   asection *s0 = find_section (file, sec0, &multiple_sections_found);
 
@@ -625,7 +644,7 @@ walk_wild_section_specs1_wild1 (lang_wild_statement_type *ptr,
   for (s = file->the_bfd->sections; s != NULL; s = s->next)
     {
       const char *sname = bfd_section_name (s);
-      bfd_boolean skip = !match_simple_wild (wildsec0->spec.name, sname);
+      bool skip = !match_simple_wild (wildsec0->spec.name, sname);
 
       if (!skip)
        walk_wild_consider_section (ptr, file, s, wildsec0, callback, data);
@@ -641,7 +660,7 @@ walk_wild_section_specs2_wild1 (lang_wild_statement_type *ptr,
   asection *s;
   struct wildcard_list *sec0 = ptr->handler_data[0];
   struct wildcard_list *wildsec1 = ptr->handler_data[1];
-  bfd_boolean multiple_sections_found;
+  bool multiple_sections_found;
   asection *s0 = find_section (file, sec0, &multiple_sections_found);
 
   if (multiple_sections_found)
@@ -662,7 +681,7 @@ walk_wild_section_specs2_wild1 (lang_wild_statement_type *ptr,
       else
        {
          const char *sname = bfd_section_name (s);
-         bfd_boolean skip = !match_simple_wild (wildsec1->spec.name, sname);
+         bool skip = !match_simple_wild (wildsec1->spec.name, sname);
 
          if (!skip)
            walk_wild_consider_section (ptr, file, s, wildsec1, callback,
@@ -681,7 +700,7 @@ walk_wild_section_specs3_wild2 (lang_wild_statement_type *ptr,
   struct wildcard_list *sec0 = ptr->handler_data[0];
   struct wildcard_list *wildsec1 = ptr->handler_data[1];
   struct wildcard_list *wildsec2 = ptr->handler_data[2];
-  bfd_boolean multiple_sections_found;
+  bool multiple_sections_found;
   asection *s0 = find_section (file, sec0, &multiple_sections_found);
 
   if (multiple_sections_found)
@@ -697,7 +716,7 @@ walk_wild_section_specs3_wild2 (lang_wild_statement_type *ptr,
       else
        {
          const char *sname = bfd_section_name (s);
-         bfd_boolean skip = !match_simple_wild (wildsec1->spec.name, sname);
+         bool skip = !match_simple_wild (wildsec1->spec.name, sname);
 
          if (!skip)
            walk_wild_consider_section (ptr, file, s, wildsec1, callback, data);
@@ -723,7 +742,7 @@ walk_wild_section_specs4_wild2 (lang_wild_statement_type *ptr,
   struct wildcard_list *sec1 = ptr->handler_data[1];
   struct wildcard_list *wildsec2 = ptr->handler_data[2];
   struct wildcard_list *wildsec3 = ptr->handler_data[3];
-  bfd_boolean multiple_sections_found;
+  bool multiple_sections_found;
   asection *s0 = find_section (file, sec0, &multiple_sections_found), *s1;
 
   if (multiple_sections_found)
@@ -749,8 +768,7 @@ walk_wild_section_specs4_wild2 (lang_wild_statement_type *ptr,
        else
          {
            const char *sname = bfd_section_name (s);
-           bfd_boolean skip = !match_simple_wild (wildsec2->spec.name,
-                                                  sname);
+           bool skip = !match_simple_wild (wildsec2->spec.name, sname);
 
            if (!skip)
              walk_wild_consider_section (ptr, file, s, wildsec2, callback,
@@ -783,7 +801,7 @@ walk_wild_section (lang_wild_statement_type *ptr,
    only if the prefixes of name1 and name2 are different up to the
    first wildcard character.  */
 
-static bfd_boolean
+static bool
 wild_spec_can_overlap (const char *name1, const char *name2)
 {
   size_t prefix1_len = strcspn (name1, "?*[");
@@ -1090,15 +1108,17 @@ new_statement (enum statement_enum type,
 static lang_input_statement_type *
 new_afile (const char *name,
           lang_input_file_enum_type file_type,
-          const char *target)
+          const char *target,
+          const char *from_filename)
 {
   lang_input_statement_type *p;
 
-  lang_has_input_file = TRUE;
+  lang_has_input_file = true;
 
   p = new_stat (lang_input_statement, stat_ptr);
   memset (&p->the_bfd, 0,
          sizeof (*p) - offsetof (lang_input_statement_type, the_bfd));
+  p->extra_search_path = NULL;
   p->target = target;
   p->flags.dynamic = input_flags.dynamic;
   p->flags.add_DT_NEEDED_for_dynamic = input_flags.add_DT_NEEDED_for_dynamic;
@@ -1111,8 +1131,8 @@ new_afile (const char *name,
     case lang_input_file_is_symbols_only_enum:
       p->filename = name;
       p->local_sym_name = name;
-      p->flags.real = TRUE;
-      p->flags.just_syms = TRUE;
+      p->flags.real = true;
+      p->flags.just_syms = true;
       break;
     case lang_input_file_is_fake_enum:
       p->filename = name;
@@ -1122,30 +1142,34 @@ new_afile (const char *name,
       if (name[0] == ':' && name[1] != '\0')
        {
          p->filename = name + 1;
-         p->flags.full_name_provided = TRUE;
+         p->flags.full_name_provided = true;
        }
       else
        p->filename = name;
       p->local_sym_name = concat ("-l", name, (const char *) NULL);
-      p->flags.maybe_archive = TRUE;
-      p->flags.real = TRUE;
-      p->flags.search_dirs = 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->local_sym_name = name;
-      p->flags.search_dirs = TRUE;
+      p->flags.search_dirs = true;
       break;
     case lang_input_file_is_search_file_enum:
       p->filename = name;
       p->local_sym_name = name;
-      p->flags.real = TRUE;
-      p->flags.search_dirs = TRUE;
+      /* If name is a relative path, search the directory of the current linker
+         script first. */
+      if (from_filename && !IS_ABSOLUTE_PATH (name))
+        p->extra_search_path = ldirname (from_filename);
+      p->flags.real = true;
+      p->flags.search_dirs = true;
       break;
     case lang_input_file_is_file_enum:
       p->filename = name;
       p->local_sym_name = name;
-      p->flags.real = TRUE;
+      p->flags.real = true;
       break;
     default:
       FAIL ();
@@ -1161,7 +1185,7 @@ lang_add_input_file (const char *name,
                     const char *target)
 {
   if (name != NULL
-      && (*name == '=' || CONST_STRNEQ (name, "$SYSROOT")))
+      && (*name == '=' || startswith (name, "$SYSROOT")))
     {
       lang_input_statement_type *ret;
       char *sysrooted_name
@@ -1178,12 +1202,12 @@ lang_add_input_file (const char *name,
         within the sysroot subdirectory.)  */
       unsigned int outer_sysrooted = input_flags.sysrooted;
       input_flags.sysrooted = 0;
-      ret = new_afile (sysrooted_name, file_type, target);
+      ret = new_afile (sysrooted_name, file_type, target, NULL);
       input_flags.sysrooted = outer_sysrooted;
       return ret;
     }
 
-  return new_afile (name, file_type, target);
+  return new_afile (name, file_type, target, current_input_file);
 }
 
 struct out_section_hash_entry
@@ -1280,7 +1304,7 @@ lang_init (void)
   first_file = lang_add_input_file (NULL, lang_input_file_is_marker_enum,
                                    NULL);
   abs_output_section =
-    lang_output_section_statement_lookup (BFD_ABS_SECTION_NAME, 0, TRUE);
+    lang_output_section_statement_lookup (BFD_ABS_SECTION_NAME, 0, 1);
 
   abs_output_section->bfd_section = bfd_abs_section_ptr;
 
@@ -1321,7 +1345,7 @@ static lang_memory_region_type **lang_memory_region_list_tail
   = &lang_memory_region_list;
 
 lang_memory_region_type *
-lang_memory_region_lookup (const char *const name, bfd_boolean create)
+lang_memory_region_lookup (const char *const name, bool create)
 {
   lang_memory_region_name *n;
   lang_memory_region_type *r;
@@ -1358,7 +1382,7 @@ lang_memory_region_lookup (const char *const name, bfd_boolean create)
   new_region->last_os = NULL;
   new_region->flags = 0;
   new_region->not_flags = 0;
-  new_region->had_full_message = FALSE;
+  new_region->had_full_message = false;
 
   *lang_memory_region_list_tail = new_region;
   lang_memory_region_list_tail = &new_region->next;
@@ -1426,7 +1450,7 @@ lang_memory_default (asection *section)
          return p;
        }
     }
-  return lang_memory_region_lookup (DEFAULT_MEMORY_REGION, FALSE);
+  return lang_memory_region_lookup (DEFAULT_MEMORY_REGION, false);
 }
 
 /* Get the output section statement directly from the userdata.  */
@@ -1439,19 +1463,21 @@ lang_output_section_get (const asection *output_section)
 
 /* 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.  */
+   match any non-negative constraint.  If CREATE is 0 return NULL when
+   no match exists.  If CREATE is 1, create an output_section_statement
+   when no match exists or if CONSTRAINT is SPECIAL.  If CREATE is 2,
+   always make a new output_section_statement.  */
 
 lang_output_section_statement_type *
 lang_output_section_statement_lookup (const char *name,
                                      int constraint,
-                                     bfd_boolean create)
+                                     int create)
 {
   struct out_section_hash_entry *entry;
 
   entry = ((struct out_section_hash_entry *)
           bfd_hash_lookup (&output_section_statement_table, name,
-                           create, FALSE));
+                           create != 0, false));
   if (entry == NULL)
     {
       if (create)
@@ -1466,23 +1492,19 @@ lang_output_section_statement_lookup (const char *name,
       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);
+      do
+       {
+         if (create != 2
+             && !(create && constraint == SPECIAL)
+             && (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;
@@ -1503,6 +1525,8 @@ lang_output_section_statement_lookup (const char *name,
 
   entry->s.output_section_statement.name = name;
   entry->s.output_section_statement.constraint = constraint;
+  entry->s.output_section_statement.dup_output = (create == 2
+                                                 || constraint == SPECIAL);
   return &entry->s.output_section_statement;
 }
 
@@ -1552,7 +1576,7 @@ lang_output_section_find_by_flags (const asection *sec,
 
   /* We know the first statement on this list is *ABS*.  May as well
      skip it.  */
-  first = &lang_os_list.head->output_section_statement;
+  first = (void *) lang_os_list.head;
   first = first->next;
 
   /* First try for an exact match.  */
@@ -1630,7 +1654,7 @@ lang_output_section_find_by_flags (const asection *sec,
     {
       /* .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;
+      bool seen_thread_local = false;
 
       match_type = NULL;
       for (look = first; look; look = look->next)
@@ -1650,7 +1674,7 @@ lang_output_section_find_by_flags (const asection *sec,
                   previous section.  */
                break;
              found = look;
-             seen_thread_local = TRUE;
+             seen_thread_local = true;
            }
          else if (seen_thread_local)
            break;
@@ -1778,9 +1802,9 @@ insert_os_after (lang_output_section_statement_type *after)
 {
   lang_statement_union_type **where;
   lang_statement_union_type **assign = NULL;
-  bfd_boolean ignore_first;
+  bool ignore_first;
 
-  ignore_first = after == &lang_os_list.head->output_section_statement;
+  ignore_first = after == (void *) lang_os_list.head;
 
   for (where = &after->header.next;
        *where != NULL;
@@ -1800,7 +1824,7 @@ insert_os_after (lang_output_section_statement_type *after)
                {
                  if (!ignore_first)
                    assign = where;
-                 ignore_first = FALSE;
+                 ignore_first = false;
                }
            }
          continue;
@@ -1813,7 +1837,7 @@ insert_os_after (lang_output_section_statement_type *after)
        case lang_padding_statement_enum:
        case lang_constructors_statement_enum:
          assign = NULL;
-         ignore_first = FALSE;
+         ignore_first = false;
          continue;
        case lang_output_section_statement_enum:
          if (assign != NULL)
@@ -1867,12 +1891,12 @@ lang_insert_orphan (asection *s,
     address = exp_intop (0);
 
   os_tail = (lang_output_section_statement_type **) lang_os_list.tail;
-  os = lang_enter_output_section_statement (secname, address, normal_section,
-                                           NULL, NULL, NULL, constraint, 0);
+  os = lang_enter_output_section_statement (
+      secname, address, normal_section, 0, NULL, NULL, NULL, constraint, 0);
 
   if (add_child == NULL)
     add_child = &os->children;
-  lang_add_section (add_child, s, NULL, os);
+  lang_add_section (add_child, s, NULL, NULL, os);
 
   if (after && (s->flags & (SEC_LOAD | SEC_ALLOC)) != 0)
     {
@@ -1896,15 +1920,15 @@ lang_insert_orphan (asection *s,
   if (after != NULL && os->bfd_section != NULL)
     {
       asection *snew, *as;
-      bfd_boolean place_after = place->stmt == NULL;
-      bfd_boolean insert_after = TRUE;
+      bool place_after = place->stmt == NULL;
+      bool insert_after = true;
 
       snew = os->bfd_section;
 
       /* Shuffle the bfd section list to make the output file look
         neater.  This is really only cosmetic.  */
       if (place->section == NULL
-         && after != &lang_os_list.head->output_section_statement)
+         && after != (void *) lang_os_list.head)
        {
          asection *bfd_section = after->bfd_section;
 
@@ -1952,7 +1976,7 @@ lang_insert_orphan (asection *s,
          asection *after_sec;
          /* True if we need to insert the orphan section after a
             specific section to maintain output note section order.  */
-         bfd_boolean after_sec_note = FALSE;
+         bool after_sec_note = false;
 
          static asection *first_orphan_note = NULL;
 
@@ -1996,14 +2020,14 @@ lang_insert_orphan (asection *s,
                     alignments, place the section before all
                     output orphan note sections.  */
                  after_sec = first_orphan_note;
-                 insert_after = FALSE;
+                 insert_after = false;
                }
            }
          else if (first_orphan_note)
            {
              /* Don't place non-note sections in the middle of orphan
                 note sections.  */
-             after_sec_note = TRUE;
+             after_sec_note = true;
              after_sec = as;
              for (sec = as->next;
                   (sec != NULL
@@ -2021,7 +2045,7 @@ lang_insert_orphan (asection *s,
                  /* Search forward to insert OS after AFTER_SEC output
                     statement.  */
                  lang_output_section_statement_type *stmt, *next;
-                 bfd_boolean found = FALSE;
+                 bool found = false;
                  for (stmt = after; stmt != NULL; stmt = next)
                    {
                      next = stmt->next;
@@ -2029,8 +2053,8 @@ lang_insert_orphan (asection *s,
                        {
                          if (stmt->bfd_section == after_sec)
                            {
-                             place_after = TRUE;
-                             found = TRUE;
+                             place_after = true;
+                             found = true;
                              after = stmt;
                              break;
                            }
@@ -2041,8 +2065,8 @@ lang_insert_orphan (asection *s,
                             AFTER_SEC output statement.  */
                          if (next && next->bfd_section == after_sec)
                            {
-                             place_after = TRUE;
-                             found = TRUE;
+                             place_after = true;
+                             found = true;
                              after = stmt;
                              break;
                            }
@@ -2058,7 +2082,7 @@ lang_insert_orphan (asection *s,
                          {
                            if (stmt->bfd_section == after_sec)
                              {
-                               place_after = TRUE;
+                               place_after = true;
                                after = stmt;
                                break;
                              }
@@ -2069,7 +2093,7 @@ lang_insert_orphan (asection *s,
                               AFTER_SEC output statement.  */
                            if (stmt->next->bfd_section == after_sec)
                              {
-                               place_after = TRUE;
+                               place_after = true;
                                after = stmt;
                                break;
                              }
@@ -2241,7 +2265,7 @@ void
 lang_map (void)
 {
   lang_memory_region_type *m;
-  bfd_boolean dis_header_printed = FALSE;
+  bool dis_header_printed = false;
 
   LANG_FOR_EACH_INPUT_STATEMENT (file)
     {
@@ -2260,10 +2284,10 @@ lang_map (void)
              if (! dis_header_printed)
                {
                  fprintf (config.map_file, _("\nDiscarded input sections\n\n"));
-                 dis_header_printed = TRUE;
+                 dis_header_printed = true;
                }
 
-             print_input_section (s, TRUE);
+             print_input_section (s, true);
            }
     }
 
@@ -2324,7 +2348,7 @@ lang_map (void)
                              config.map_file);
 }
 
-static bfd_boolean
+static bool
 sort_def_symbol (struct bfd_link_hash_entry *hash_entry,
                 void *info ATTRIBUTE_UNUSED)
 {
@@ -2353,7 +2377,7 @@ sort_def_symbol (struct bfd_link_hash_entry *hash_entry,
       ud->map_symbol_def_tail = &def->next;
       ud->map_symbol_def_count++;
     }
-  return TRUE;
+  return true;
 }
 
 /* Initialize an output section.  */
@@ -2364,7 +2388,7 @@ init_os (lang_output_section_statement_type *s, flagword flags)
   if (strcmp (s->name, DISCARD_SECTION_NAME) == 0)
     einfo (_("%F%P: illegal use of `%s' section\n"), DISCARD_SECTION_NAME);
 
-  if (s->constraint != SPECIAL)
+  if (!s->dup_output)
     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_anyway_with_flags (link_info.output_bfd,
@@ -2477,10 +2501,10 @@ section_already_linked (bfd *abfd, asection *sec, void *data)
 /* Returns true if SECTION is one we know will be discarded based on its
    section flags, otherwise returns false.  */
 
-static bfd_boolean
+static bool
 lang_discard_section_p (asection *section)
 {
-  bfd_boolean discard;
+  bool discard;
   flagword flags = section->flags;
 
   /* Discard sections marked with SEC_EXCLUDE.  */
@@ -2490,13 +2514,13 @@ lang_discard_section_p (asection *section)
      sections from within the group.  */
   if ((flags & SEC_GROUP) != 0
       && link_info.resolve_section_groups)
-    discard = TRUE;
+    discard = true;
 
   /* Discard debugging sections if we are stripping debugging
      information.  */
   if ((link_info.strip == strip_debugger || link_info.strip == strip_all)
       && (flags & SEC_DEBUGGING) != 0)
-    discard = TRUE;
+    discard = true;
 
   return discard;
 }
@@ -2513,12 +2537,13 @@ lang_discard_section_p (asection *section)
 void
 lang_add_section (lang_statement_list_type *ptr,
                  asection *section,
+                 struct wildcard_list *pattern,
                  struct flag_info *sflag_info,
                  lang_output_section_statement_type *output)
 {
   flagword flags = section->flags;
 
-  bfd_boolean discard;
+  bool discard;
   lang_input_section_type *new_section;
   bfd *abfd = link_info.output_bfd;
 
@@ -2528,7 +2553,7 @@ lang_add_section (lang_statement_list_type *ptr,
   /* Discard input sections which are assigned to a section named
      DISCARD_SECTION_NAME.  */
   if (strcmp (output->name, DISCARD_SECTION_NAME) == 0)
-    discard = TRUE;
+    discard = true;
 
   if (discard)
     {
@@ -2537,12 +2562,17 @@ lang_add_section (lang_statement_list_type *ptr,
          /* This prevents future calls from assigning this section.  */
          section->output_section = bfd_abs_section_ptr;
        }
+      else if (link_info.non_contiguous_regions_warnings)
+       einfo (_("%P:%pS: warning: --enable-non-contiguous-regions makes "
+                "section `%pA' from '%pB' match /DISCARD/ clause.\n"),
+              NULL, section, section->owner);
+
       return;
     }
 
   if (sflag_info)
     {
-      bfd_boolean keep;
+      bool keep;
 
       keep = bfd_lookup_section_flags (&link_info, sflag_info, section);
       if (!keep)
@@ -2550,7 +2580,33 @@ lang_add_section (lang_statement_list_type *ptr,
     }
 
   if (section->output_section != NULL)
-    return;
+    {
+      if (!link_info.non_contiguous_regions)
+       return;
+
+      /* SECTION has already been handled in a special way
+        (eg. LINK_ONCE): skip it.  */
+      if (bfd_is_abs_section (section->output_section))
+       return;
+
+      /* Already assigned to the same output section, do not process
+        it again, to avoid creating loops between duplicate sections
+        later.  */
+      if (section->output_section == output->bfd_section)
+       return;
+
+      if (link_info.non_contiguous_regions_warnings && output->bfd_section)
+       einfo (_("%P:%pS: warning: --enable-non-contiguous-regions may "
+                "change behaviour for section `%pA' from '%pB' (assigned to "
+                "%pA, but additional match: %pA)\n"),
+              NULL, section, section->owner, section->output_section,
+              output->bfd_section);
+
+      /* SECTION has already been assigned to an output section, but
+        the user allows it to be mapped to another one in case it
+        overflows. We'll later update the actual output section in
+        size_input_section as appropriate.  */
+    }
 
   /* 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
@@ -2579,10 +2635,15 @@ lang_add_section (lang_statement_list_type *ptr,
     case normal_section:
     case overlay_section:
     case first_overlay_section:
+    case type_section:
       break;
     case noalloc_section:
       flags &= ~SEC_ALLOC;
       break;
+    case typed_readonly_section:
+    case readonly_section:
+      flags |= SEC_READONLY;
+      break;
     case noload_section:
       flags &= ~SEC_LOAD;
       flags |= SEC_NEVER_LOAD;
@@ -2642,6 +2703,16 @@ lang_add_section (lang_statement_list_type *ptr,
       output->block_value = 128;
     }
 
+  /* When a .ctors section is placed in .init_array it must be copied
+     in reverse order.  Similarly for .dtors.  Set that up.  */
+  if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour
+      && ((startswith (section->name, ".ctors")
+          && strcmp (output->bfd_section->name, ".init_array") == 0)
+         || (startswith (section->name, ".dtors")
+             && strcmp (output->bfd_section->name, ".fini_array") == 0))
+      && (section->name[6] == 0 || section->name[6] == '.'))
+    section->flags |= SEC_ELF_REVERSE_COPY;
+
   if (section->alignment_power > output->bfd_section->alignment_power)
     output->bfd_section->alignment_power = section->alignment_power;
 
@@ -2662,6 +2733,7 @@ lang_add_section (lang_statement_list_type *ptr,
   /* Add a section reference to the list.  */
   new_section = new_stat (lang_input_section, ptr);
   new_section->section = section;
+  new_section->pattern = pattern;
 }
 
 /* Handle wildcard sorting.  This returns the lang_input_section which
@@ -2695,7 +2767,7 @@ wild_sort (lang_wild_statement_type *wild,
       if (wild->filenames_sorted)
        {
          const char *fn, *ln;
-         bfd_boolean fa, la;
+         bool fa, la;
          int i;
 
          /* The PE support for the .idata section as generated by
@@ -2707,23 +2779,23 @@ wild_sort (lang_wild_statement_type *wild,
              && file->the_bfd->my_archive != NULL)
            {
              fn = bfd_get_filename (file->the_bfd->my_archive);
-             fa = TRUE;
+             fa = true;
            }
          else
            {
              fn = file->filename;
-             fa = FALSE;
+             fa = false;
            }
 
          if (ls->section->owner->my_archive != NULL)
            {
              ln = bfd_get_filename (ls->section->owner->my_archive);
-             la = TRUE;
+             la = true;
            }
          else
            {
-             ln = ls->section->owner->filename;
-             la = FALSE;
+             ln = bfd_get_filename (ls->section->owner);
+             la = false;
            }
 
          i = filename_cmp (fn, ln);
@@ -2737,7 +2809,7 @@ wild_sort (lang_wild_statement_type *wild,
              if (fa)
                fn = file->filename;
              if (la)
-               ln = ls->section->owner->filename;
+               ln = bfd_get_filename (ls->section->owner);
 
              i = filename_cmp (fn, ln);
              if (i > 0)
@@ -2767,7 +2839,6 @@ 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)
 {
@@ -2788,14 +2859,16 @@ output_section_callback (lang_wild_statement_type *ptr,
      of the current list.  */
 
   if (before == NULL)
-    lang_add_section (&ptr->children, section, sflag_info, os);
+    lang_add_section (&ptr->children, section, ptr->section_list,
+                     ptr->section_flag_list, os);
   else
     {
       lang_statement_list_type list;
       lang_statement_union_type **pp;
 
       lang_list_init (&list);
-      lang_add_section (&list, section, sflag_info, os);
+      lang_add_section (&list, section, ptr->section_list,
+                       ptr->section_flag_list, os);
 
       /* If we are discarding the section, LIST.HEAD will
         be NULL.  */
@@ -2821,7 +2894,6 @@ 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 *output)
 {
@@ -2834,7 +2906,7 @@ check_section_callback (lang_wild_statement_type *ptr ATTRIBUTE_UNUSED,
     return;
 
   if (section->output_section == NULL && (section->flags & SEC_READONLY) == 0)
-    os->all_input_readonly = FALSE;
+    os->all_input_readonly = false;
 }
 
 /* This is passed a file name which must have been seen already and
@@ -2846,7 +2918,7 @@ lookup_name (const char *name)
 {
   lang_input_statement_type *search;
 
-  for (search = &input_file_chain.head->input_statement;
+  for (search = (void *) input_file_chain.head;
        search != NULL;
        search = search->next_real_file)
     {
@@ -2875,7 +2947,7 @@ lookup_name (const char *name)
       lang_statement_union_type *rest = *after;
       stat_ptr->tail = after;
       search = new_afile (name, lang_input_file_is_search_file_enum,
-                         default_target);
+                         default_target, NULL);
       *stat_ptr->tail = rest;
       if (*tail == NULL)
        stat_ptr->tail = tail;
@@ -2932,11 +3004,11 @@ check_excluded_libs (bfd *abfd)
   while (lib)
     {
       int len = strlen (lib->name);
-      const char *filename = lbasename (abfd->filename);
+      const char *filename = lbasename (bfd_get_filename (abfd));
 
       if (strcmp (lib->name, "ALL") == 0)
        {
-         abfd->no_export = TRUE;
+         abfd->no_export = true;
          return;
        }
 
@@ -2945,7 +3017,7 @@ check_excluded_libs (bfd *abfd)
              || (filename[len] == '.' && filename[len + 1] == 'a'
                  && filename[len + 2] == '\0')))
        {
-         abfd->no_export = TRUE;
+         abfd->no_export = true;
          return;
        }
 
@@ -2955,20 +3027,20 @@ check_excluded_libs (bfd *abfd)
 
 /* Get the symbols for an input file.  */
 
-bfd_boolean
+bool
 load_symbols (lang_input_statement_type *entry,
              lang_statement_list_type *place)
 {
   char **matching;
 
   if (entry->flags.loaded)
-    return TRUE;
+    return true;
 
   ldfile_open_file (entry);
 
   /* Do not process further if the file was missing.  */
   if (entry->flags.missing_file)
-    return TRUE;
+    return true;
 
   if (trace_files || verbose)
     info_msg ("%pI\n", entry);
@@ -2984,7 +3056,7 @@ load_symbols (lang_input_statement_type *entry,
 
       /* See if the emulation has some special knowledge.  */
       if (ldemul_unrecognized_file (entry))
-       return TRUE;
+       return true;
 
       if (err == bfd_error_file_ambiguously_recognized)
        {
@@ -3015,10 +3087,12 @@ load_symbols (lang_input_statement_type *entry,
       input_flags.whole_archive = entry->flags.whole_archive;
       input_flags.dynamic = entry->flags.dynamic;
 
-      ldfile_assumed_script = TRUE;
+      ldfile_assumed_script = true;
       parser_input = input_script;
+      current_input_file = entry->filename;
       yyparse ();
-      ldfile_assumed_script = FALSE;
+      current_input_file = NULL;
+      ldfile_assumed_script = false;
 
       /* missing_file is sticky.  sysrooted will already have been
         restored when seeing EOF in yyparse, but no harm to restore
@@ -3028,13 +3102,13 @@ load_symbols (lang_input_statement_type *entry,
       pop_stat_ptr ();
       fclose (yyin);
       yyin = NULL;
-      entry->flags.loaded = TRUE;
+      entry->flags.loaded = true;
 
-      return TRUE;
+      return true;
     }
 
   if (ldemul_recognized_file (entry))
-    return TRUE;
+    return true;
 
   /* We don't call ldlang_add_file for an archive.  Instead, the
      add_symbols entry point will call ldlang_add_file, via the
@@ -3057,7 +3131,7 @@ load_symbols (lang_input_statement_type *entry,
       if (entry->flags.whole_archive)
        {
          bfd *member = NULL;
-         bfd_boolean loaded = TRUE;
+         bool loaded = true;
 
          for (;;)
            {
@@ -3071,7 +3145,7 @@ load_symbols (lang_input_statement_type *entry,
                {
                  einfo (_("%F%P: %pB: member %pB in archive is not an object\n"),
                         entry->the_bfd, member);
-                 loaded = FALSE;
+                 loaded = false;
                }
 
              subsbfd = member;
@@ -3085,7 +3159,7 @@ load_symbols (lang_input_statement_type *entry,
              if (!bfd_link_add_symbols (subsbfd, &link_info))
                {
                  einfo (_("%F%P: %pB: error adding symbols: %E\n"), member);
-                 loaded = FALSE;
+                 loaded = false;
                }
            }
 
@@ -3096,7 +3170,7 @@ load_symbols (lang_input_statement_type *entry,
     }
 
   if (bfd_link_add_symbols (entry->the_bfd, &link_info))
-    entry->flags.loaded = TRUE;
+    entry->flags.loaded = true;
   else
     einfo (_("%F%P: %pB: error adding symbols: %E\n"), entry->the_bfd);
 
@@ -3330,6 +3404,22 @@ lang_get_output_target (void)
 static void
 open_output (const char *name)
 {
+  lang_input_statement_type *f;
+  char *out = lrealpath (name);
+
+  for (f = (void *) input_file_chain.head;
+       f != NULL;
+       f = f->next_real_file)
+    if (f->flags.real)
+      {
+       char *in = lrealpath (f->local_sym_name);
+       if (filename_cmp (in, out) == 0)
+         einfo (_("%F%P: input file '%s' is the same as output file\n"),
+                f->filename);
+       free (in);
+      }
+  free (out);
+
   output_target = lang_get_output_target ();
 
   /* Has the user requested a particular endianness on the command
@@ -3391,7 +3481,7 @@ open_output (const char *name)
       einfo (_("%F%P: cannot open output file %s: %E\n"), name);
     }
 
-  delete_output_file_on_failure = TRUE;
+  delete_output_file_on_failure = true;
 
   if (!bfd_set_format (link_info.output_bfd, bfd_object))
     einfo (_("%F%P: %s: can not make object file: %E\n"), name);
@@ -3442,16 +3532,16 @@ ldlang_open_output (lang_statement_union_type *statement)
 static void
 init_opb (asection *s)
 {
-  unsigned x = bfd_arch_mach_octets_per_byte (ldfile_output_architecture,
-                                             ldfile_output_machine);
-  if (s != NULL)
-    {
-      if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour
-         && (s->flags & SEC_ELF_OCTETS))
-       x = 1;
-    }
+  unsigned int x;
 
   opb_shift = 0;
+  if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour
+      && s != NULL
+      && (s->flags & SEC_ELF_OCTETS) != 0)
+    return;
+
+  x = bfd_arch_mach_octets_per_byte (ldfile_output_architecture,
+                                    ldfile_output_machine);
   if (x > 1)
     while ((x & 1) == 0)
       {
@@ -3469,7 +3559,7 @@ enum open_bfd_mode
     OPEN_BFD_FORCE = 1,
     OPEN_BFD_RESCAN = 2
   };
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_PLUGINS
 static lang_input_statement_type *plugin_insert = NULL;
 static struct bfd_link_hash_entry *plugin_undefs = NULL;
 #endif
@@ -3499,7 +3589,7 @@ open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
        case lang_group_statement_enum:
          {
            struct bfd_link_hash_entry *undefs;
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_PLUGINS
            lang_input_statement_type *plugin_insert_save;
 #endif
 
@@ -3509,7 +3599,7 @@ open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
 
            do
              {
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_PLUGINS
                plugin_insert_save = plugin_insert;
 #endif
                undefs = link_info.hash->undefs_tail;
@@ -3517,7 +3607,7 @@ open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
                                 mode | OPEN_BFD_FORCE);
              }
            while (undefs != link_info.hash->undefs_tail
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_PLUGINS
                   /* Objects inserted by a plugin, which are loaded
                      before we hit this loop, may have added new
                      undefs.  */
@@ -3544,7 +3634,7 @@ open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
                 has been loaded already.  Do the same for a rescan.
                 Likewise reload --as-needed shared libs.  */
              if (mode != OPEN_BFD_NORMAL
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_PLUGINS
                  && ((mode & OPEN_BFD_RESCAN) == 0
                      || plugin_insert == NULL)
 #endif
@@ -3558,15 +3648,15 @@ open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
                          && 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;
+                 s->input_statement.flags.loaded = false;
+                 s->input_statement.flags.reload = true;
                }
 
              os_tail = lang_os_list.tail;
              lang_list_init (&add);
 
              if (!load_symbols (&s->input_statement, &add))
-               config.make_executable = FALSE;
+               config.make_executable = false;
 
              if (add.head != NULL)
                {
@@ -3591,7 +3681,7 @@ open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
                    }
                }
            }
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_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)
@@ -3612,6 +3702,35 @@ open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
     einfo ("%F");
 }
 
+#ifdef ENABLE_LIBCTF
+/* Emit CTF errors and warnings.  fp can be NULL to report errors/warnings
+   that happened specifically at CTF open time.  */
+static void
+lang_ctf_errs_warnings (ctf_dict_t *fp)
+{
+  ctf_next_t *i = NULL;
+  char *text;
+  int is_warning;
+  int err;
+
+  while ((text = ctf_errwarning_next (fp, &i, &is_warning, &err)) != NULL)
+    {
+      einfo (_("%s: %s\n"), is_warning ? _("CTF warning"): _("CTF error"),
+            text);
+      free (text);
+    }
+  if (err != ECTF_NEXT_END)
+    {
+      einfo (_("CTF error: cannot get CTF errors: `%s'\n"),
+            ctf_errmsg (err));
+    }
+
+  /* `err' returns errors from the error/warning iterator in particular.
+     These never assert.  But if we have an fp, that could have recorded
+     an assertion failure: assert if it has done so.  */
+  ASSERT (!fp || ctf_errno (fp) != ECTF_INTERNAL);
+}
+
 /* Open the CTF sections in the input files with libctf: if any were opened,
    create a fake input file that we'll write the merged CTF data to later
    on.  */
@@ -3626,7 +3745,7 @@ ldlang_open_ctf (void)
     {
       asection *sect;
 
-      /* Incoming files from the compiler have a single ctf_file_t in them
+      /* Incoming files from the compiler have a single ctf_dict_t in them
         (which is presented to us by the libctf API in a ctf_archive_t
         wrapper): files derived from a previous relocatable link have a CTF
         archive containing possibly many CTF files.  */
@@ -3634,19 +3753,25 @@ ldlang_open_ctf (void)
       if ((file->the_ctf = ctf_bfdopen (file->the_bfd, &err)) == NULL)
        {
          if (err != ECTF_NOCTFDATA)
-           einfo (_("%P: warning: CTF section in `%pI' not loaded: "
-                    "its types will be discarded: `%s'\n"), file,
+           {
+             lang_ctf_errs_warnings (NULL);
+             einfo (_("%P: warning: CTF section in %pB not loaded; "
+                      "its types will be discarded: %s\n"), file->the_bfd,
                     ctf_errmsg (err));
+           }
          continue;
        }
 
       /* Prevent the contents of this section from being written, while
-        requiring the section itself to be duplicated in the output.  */
+        requiring the section itself to be duplicated in the output, but only
+        once.  */
       /* This section must exist if ctf_bfdopen() succeeded.  */
       sect = bfd_get_section_by_name (file->the_bfd, ".ctf");
       sect->size = 0;
       sect->flags |= SEC_NEVER_LOAD | SEC_HAS_CONTENTS | SEC_LINKER_CREATED;
 
+      if (any_ctf)
+       sect->flags |= SEC_EXCLUDE;
       any_ctf = 1;
     }
 
@@ -3659,7 +3784,7 @@ ldlang_open_ctf (void)
   if ((ctf_output = ctf_create (&err)) != NULL)
     return;
 
-  einfo (_("%P: warning: CTF output not created: `s'\n"),
+  einfo (_("%P: warning: CTF output not created: `%s'\n"),
         ctf_errmsg (err));
 
   LANG_FOR_EACH_INPUT_STATEMENT (errfile)
@@ -3673,6 +3798,7 @@ static void
 lang_merge_ctf (void)
 {
   asection *output_sect;
+  int flags = 0;
 
   if (!ctf_output)
     return;
@@ -3682,7 +3808,7 @@ lang_merge_ctf (void)
   /* If the section was discarded, don't waste time merging.  */
   if (output_sect == NULL)
     {
-      ctf_file_close (ctf_output);
+      ctf_dict_close (ctf_output);
       ctf_output = NULL;
 
       LANG_FOR_EACH_INPUT_STATEMENT (file)
@@ -3698,20 +3824,31 @@ lang_merge_ctf (void)
       if (!file->the_ctf)
        continue;
 
-      /* Takes ownership of file->u.the_ctfa.  */
+      /* Takes ownership of file->the_ctf.  */
       if (ctf_link_add_ctf (ctf_output, file->the_ctf, file->filename) < 0)
        {
-         einfo (_("%F%P: cannot link with CTF in %pB: %s\n"), file->the_bfd,
-                ctf_errmsg (ctf_errno (ctf_output)));
+         einfo (_("%P: warning: CTF section in %pB cannot be linked: `%s'\n"),
+                file->the_bfd, ctf_errmsg (ctf_errno (ctf_output)));
          ctf_close (file->the_ctf);
          file->the_ctf = NULL;
          continue;
        }
     }
 
-  if (ctf_link (ctf_output, CTF_LINK_SHARE_UNCONFLICTED) < 0)
+  if (!config.ctf_share_duplicated)
+    flags = CTF_LINK_SHARE_UNCONFLICTED;
+  else
+    flags = CTF_LINK_SHARE_DUPLICATED;
+  if (!config.ctf_variables)
+    flags |= CTF_LINK_OMIT_VARIABLES_SECTION;
+  if (bfd_link_relocatable (&link_info))
+    flags |= CTF_LINK_NO_FILTER_REPORTED_SYMS;
+
+  if (ctf_link (ctf_output, flags) < 0)
     {
-      einfo (_("%F%P: CTF linking failed; output will have no CTF section: %s\n"),
+      lang_ctf_errs_warnings (ctf_output);
+      einfo (_("%P: warning: CTF linking failed; "
+              "output will have no CTF section: %s\n"),
             ctf_errmsg (ctf_errno (ctf_output)));
       if (output_sect)
        {
@@ -3719,16 +3856,24 @@ lang_merge_ctf (void)
          output_sect->flags |= SEC_EXCLUDE;
        }
     }
+  /* Output any lingering errors that didn't come from ctf_link.  */
+  lang_ctf_errs_warnings (ctf_output);
 }
 
-/* Let the emulation examine the symbol table and strtab to help it optimize the
-   CTF, if supported.  */
+/* Let the emulation acquire strings from the dynamic strtab to help it optimize
+   the CTF, if supported.  */
 
 void
-ldlang_ctf_apply_strsym (struct elf_sym_strtab *syms, bfd_size_type symcount,
-                        struct elf_strtab_hash *symstrtab)
+ldlang_ctf_acquire_strings (struct elf_strtab_hash *dynstrtab)
 {
-  ldemul_examine_strtab_for_ctf (ctf_output, syms, symcount, symstrtab);
+  ldemul_acquire_strings_for_ctf (ctf_output, dynstrtab);
+}
+
+/* Inform the emulation about the addition of a new dynamic symbol, in BFD
+   internal format.  */
+void ldlang_ctf_new_dynsym (int symidx, struct elf_internal_sym *sym)
+{
+  ldemul_new_dynsym_for_ctf (ctf_output, symidx, sym);
 }
 
 /* Write out the CTF section.  Called early, if the emulation isn't going to
@@ -3755,6 +3900,11 @@ lang_write_ctf (int late)
        return;
     }
 
+  /* Inform the emulation that all the symbols that will be received have
+     been.  */
+
+  ldemul_new_dynsym_for_ctf (ctf_output, 0, NULL);
+
   /* Emit CTF.  */
 
   output_sect = bfd_get_section_by_name (link_info.output_bfd, ".ctf");
@@ -3765,17 +3915,19 @@ lang_write_ctf (int late)
       output_sect->size = output_size;
       output_sect->flags |= SEC_IN_MEMORY | SEC_KEEP;
 
+      lang_ctf_errs_warnings (ctf_output);
       if (!output_sect->contents)
        {
-         einfo (_("%F%P: CTF section emission failed; output will have no "
-                  "CTF section: %s\n"), ctf_errmsg (ctf_errno (ctf_output)));
+         einfo (_("%P: warning: CTF section emission failed; "
+                  "output will have no CTF section: %s\n"),
+                ctf_errmsg (ctf_errno (ctf_output)));
          output_sect->size = 0;
          output_sect->flags |= SEC_EXCLUDE;
        }
     }
 
   /* This also closes every CTF input file used in the link.  */
-  ctf_file_close (ctf_output);
+  ctf_dict_close (ctf_output);
   ctf_output = NULL;
 
   LANG_FOR_EACH_INPUT_STATEMENT (file)
@@ -3791,6 +3943,38 @@ ldlang_write_ctf_late (void)
 
   lang_write_ctf (1);
 }
+#else
+static void
+ldlang_open_ctf (void)
+{
+  LANG_FOR_EACH_INPUT_STATEMENT (file)
+    {
+      asection *sect;
+
+      /* If built without CTF, warn and delete all CTF sections from the output.
+        (The alternative would be to simply concatenate them, which does not
+        yield a valid CTF section.)  */
+
+      if ((sect = bfd_get_section_by_name (file->the_bfd, ".ctf")) != NULL)
+       {
+           einfo (_("%P: warning: CTF section in %pB not linkable: "
+                    "%P was built without support for CTF\n"), file->the_bfd);
+           sect->size = 0;
+           sect->flags |= SEC_EXCLUDE;
+       }
+    }
+}
+
+static void lang_merge_ctf (void) {}
+void
+ldlang_ctf_acquire_strings (struct elf_strtab_hash *dynstrtab
+                           ATTRIBUTE_UNUSED) {}
+void
+ldlang_ctf_new_dynsym (int symidx ATTRIBUTE_UNUSED,
+                      struct elf_internal_sym *sym ATTRIBUTE_UNUSED) {}
+static void lang_write_ctf (int late ATTRIBUTE_UNUSED) {}
+void ldlang_write_ctf_late (void) {}
+#endif
 
 /* Add the supplied name to the symbol table as an undefined reference.
    This is a two step process as the symbol table doesn't even exist at
@@ -3803,11 +3987,10 @@ 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, bfd_boolean cmdline)
+ldlang_add_undef (const char *const name, bool cmdline ATTRIBUTE_UNUSED)
 {
   ldlang_undef_chain_list_type *new_undef;
 
-  undef_from_cmdline = undef_from_cmdline || cmdline;
   new_undef = stat_alloc (sizeof (*new_undef));
   new_undef->next = ldlang_undef_chain_list_head;
   ldlang_undef_chain_list_head = new_undef;
@@ -3825,16 +4008,14 @@ insert_undefined (const char *name)
 {
   struct bfd_link_hash_entry *h;
 
-  h = bfd_link_hash_lookup (link_info.hash, name, TRUE, FALSE, TRUE);
+  h = bfd_link_hash_lookup (link_info.hash, name, true, false, true);
   if (h == NULL)
     einfo (_("%F%P: bfd_link_hash_lookup failed: %E\n"));
   if (h->type == bfd_link_hash_new)
     {
       h->type = bfd_link_hash_undefined;
       h->u.undef.abfd = NULL;
-      h->non_ir_ref_regular = TRUE;
-      if (is_elf_hash_table (link_info.hash))
-       ((struct elf_link_hash_entry *) h)->mark = 1;
+      h->non_ir_ref_regular = true;
       bfd_link_add_undef (link_info.hash, h);
     }
 }
@@ -3852,6 +4033,23 @@ lang_place_undefineds (void)
     insert_undefined (ptr->name);
 }
 
+/* Mark -u symbols against garbage collection.  */
+
+static void
+lang_mark_undefineds (void)
+{
+  ldlang_undef_chain_list_type *ptr;
+
+  if (is_elf_hash_table (link_info.hash))
+    for (ptr = ldlang_undef_chain_list_head; ptr != NULL; ptr = ptr->next)
+      {
+       struct elf_link_hash_entry *h = (struct elf_link_hash_entry *)
+         bfd_link_hash_lookup (link_info.hash, ptr->name, false, false, true);
+       if (h != NULL)
+         h->mark = 1;
+      }
+}
+
 /* Structure used to build the list of symbols that the user has required
    be defined.  */
 
@@ -3873,7 +4071,7 @@ ldlang_add_require_defined (const char *const name)
 {
   struct require_defined_symbol *ptr;
 
-  ldlang_add_undef (name, TRUE);
+  ldlang_add_undef (name, true);
   ptr = stat_alloc (sizeof (*ptr));
   ptr->next = require_defined_symbol_list;
   ptr->name = strdup (name);
@@ -3893,7 +4091,7 @@ ldlang_check_require_defined_symbols (void)
       struct bfd_link_hash_entry *h;
 
       h = bfd_link_hash_lookup (link_info.hash, ptr->name,
-                               FALSE, FALSE, TRUE);
+                               false, false, true);
       if (h == NULL
          || (h->type != bfd_link_hash_defined
              && h->type != bfd_link_hash_defweak))
@@ -4013,6 +4211,7 @@ map_input_to_output_sections
     {
       lang_output_section_statement_type *tos;
       flagword flags;
+      unsigned int type = 0;
 
       switch (s->header.type)
        {
@@ -4026,22 +4225,18 @@ map_input_to_output_sections
          break;
        case lang_output_section_statement_enum:
          tos = &s->output_section_statement;
-         if (tos->constraint != 0)
+         if (tos->constraint == ONLY_IF_RW
+             || tos->constraint == ONLY_IF_RO)
            {
-             if (tos->constraint != ONLY_IF_RW
-                 && tos->constraint != ONLY_IF_RO)
-               break;
-             tos->all_input_readonly = TRUE;
+             tos->all_input_readonly = true;
              check_input_sections (tos->children.head, tos);
              if (tos->all_input_readonly != (tos->constraint == ONLY_IF_RO))
-               {
-                 tos->constraint = -1;
-                 break;
-               }
+               tos->constraint = -1;
            }
-         map_input_to_output_sections (tos->children.head,
-                                       target,
-                                       tos);
+         if (tos->constraint >= 0)
+           map_input_to_output_sections (tos->children.head,
+                                         target,
+                                         tos);
          break;
        case lang_output_statement_enum:
          break;
@@ -4069,6 +4264,45 @@ map_input_to_output_sections
            case noalloc_section:
              flags = SEC_HAS_CONTENTS;
              break;
+           case readonly_section:
+             flags |= SEC_READONLY;
+             break;
+           case typed_readonly_section:
+             flags |= SEC_READONLY;
+             /* Fall through.  */
+           case type_section:
+             if (os->sectype_value->type.node_class == etree_name
+                 && os->sectype_value->type.node_code == NAME)
+               {
+                 const char *name = os->sectype_value->name.name;
+                 if (strcmp (name, "SHT_PROGBITS") == 0)
+                   type = SHT_PROGBITS;
+                 else if (strcmp (name, "SHT_STRTAB") == 0)
+                   type = SHT_STRTAB;
+                 else if (strcmp (name, "SHT_NOTE") == 0)
+                   type = SHT_NOTE;
+                 else if (strcmp (name, "SHT_NOBITS") == 0)
+                   type = SHT_NOBITS;
+                 else if (strcmp (name, "SHT_INIT_ARRAY") == 0)
+                   type = SHT_INIT_ARRAY;
+                 else if (strcmp (name, "SHT_FINI_ARRAY") == 0)
+                   type = SHT_FINI_ARRAY;
+                 else if (strcmp (name, "SHT_PREINIT_ARRAY") == 0)
+                   type = SHT_PREINIT_ARRAY;
+                 else
+                   einfo (_ ("%F%P: invalid type for output section `%s'\n"),
+                          os->name);
+               }
+            else
+              {
+                exp_fold_tree_no_dot (os->sectype_value);
+                if (expld.result.valid_p)
+                  type = expld.result.value;
+                else
+                  einfo (_ ("%F%P: invalid type for output section `%s'\n"),
+                         os->name);
+              }
+             break;
            case noload_section:
              if (bfd_get_flavour (link_info.output_bfd)
                  == bfd_target_elf_flavour)
@@ -4078,9 +4312,10 @@ map_input_to_output_sections
              break;
            }
          if (os->bfd_section == NULL)
-           init_os (os, flags);
+           init_os (os, flags | SEC_READONLY);
          else
            os->bfd_section->flags |= flags;
+         os->bfd_section->type = type;
          break;
        case lang_input_section_enum:
          break;
@@ -4121,7 +4356,7 @@ map_input_to_output_sections
                 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 = lang_output_section_statement_lookup (name, 0, 1);
              tos->addr_tree = s->address_statement.address;
              if (tos->bfd_section == NULL)
                init_os (tos, 0);
@@ -4194,6 +4429,12 @@ process_insert_statements (lang_statement_union_type **start)
          lang_statement_union_type **ptr;
          lang_statement_union_type *first;
 
+         if (link_info.non_contiguous_regions)
+           {
+             einfo (_("warning: INSERT statement in linker script is "
+                      "incompatible with --enable-non-contiguous-regions.\n"));
+           }
+
          where = lang_output_section_find (i->where);
          if (where != NULL && i->is_before)
            {
@@ -4325,16 +4566,16 @@ strip_excluded_output_sections (void)
     {
       expld.phase = lang_mark_phase_enum;
       expld.dataseg.phase = exp_seg_none;
-      one_lang_size_sections_pass (NULL, FALSE);
+      one_lang_size_sections_pass (NULL, false);
       lang_reset_memory_regions ();
     }
 
-  for (os = &lang_os_list.head->output_section_statement;
+  for (os = (void *) lang_os_list.head;
        os != NULL;
        os = os->next)
     {
       asection *output_section;
-      bfd_boolean exclude;
+      bool exclude;
 
       if (os->constraint < 0)
        continue;
@@ -4361,7 +4602,7 @@ strip_excluded_output_sections (void)
                && ((s->flags & SEC_LINKER_CREATED) != 0
                    || link_info.emitrelocations))
              {
-               exclude = FALSE;
+               exclude = false;
                break;
              }
        }
@@ -4371,7 +4612,7 @@ strip_excluded_output_sections (void)
          /* We don't set bfd_section to NULL since bfd_section of the
             removed output section statement may still be used.  */
          if (!os->update_dot)
-           os->ignored = TRUE;
+           os->ignored = true;
          output_section->flags |= SEC_EXCLUDE;
          bfd_section_list_remove (link_info.output_bfd, output_section);
          link_info.output_bfd->section_count--;
@@ -4390,7 +4631,7 @@ lang_clear_os_map (void)
   if (map_head_is_link_order)
     return;
 
-  for (os = &lang_os_list.head->output_section_statement;
+  for (os = (void *) lang_os_list.head;
        os != NULL;
        os = os->next)
     {
@@ -4410,7 +4651,7 @@ lang_clear_os_map (void)
 
   /* Stop future calls to lang_add_section from messing with map_head
      and map_tail link_order fields.  */
-  map_head_is_link_order = TRUE;
+  map_head_is_link_order = true;
 }
 
 static void
@@ -4462,7 +4703,7 @@ print_assignment (lang_assignment_statement_type *assignment,
                  lang_output_section_statement_type *output_section)
 {
   unsigned int i;
-  bfd_boolean is_dot;
+  bool is_dot;
   etree_type *tree;
   asection *osec;
 
@@ -4471,7 +4712,7 @@ print_assignment (lang_assignment_statement_type *assignment,
 
   if (assignment->exp->type.node_class == etree_assert)
     {
-      is_dot = FALSE;
+      is_dot = false;
       tree = assignment->exp->assert_s.child;
     }
   else
@@ -4489,7 +4730,7 @@ print_assignment (lang_assignment_statement_type *assignment,
   if (assignment->exp->type.node_class != etree_provide)
     exp_fold_tree (tree, osec, &print_dot);
   else
-    expld.result.valid_p = FALSE;
+    expld.result.valid_p = false;
 
   if (expld.result.valid_p)
     {
@@ -4513,7 +4754,7 @@ print_assignment (lang_assignment_statement_type *assignment,
          struct bfd_link_hash_entry *h;
 
          h = bfd_link_hash_lookup (link_info.hash, assignment->exp->assign.dst,
-                                   FALSE, FALSE, TRUE);
+                                   false, false, true);
          if (h != NULL
              && (h->type == bfd_link_hash_defined
                  || h->type == bfd_link_hash_defweak))
@@ -4555,7 +4796,7 @@ print_input_statement (lang_input_statement_type *statm)
 /* Print all symbols defined in a particular section.  This is called
    via bfd_link_hash_traverse, or by print_all_symbols.  */
 
-static bfd_boolean
+bool
 print_one_symbol (struct bfd_link_hash_entry *hash_entry, void *ptr)
 {
   asection *sec = (asection *) ptr;
@@ -4576,7 +4817,7 @@ print_one_symbol (struct bfd_link_hash_entry *hash_entry, void *ptr)
       minfo ("             %pT\n", hash_entry->root.string);
     }
 
-  return TRUE;
+  return true;
 }
 
 static int
@@ -4619,7 +4860,7 @@ print_all_symbols (asection *sec)
 
   /* Print the symbols.  */
   for (i = 0; i < ud->map_symbol_def_count; i++)
-    print_one_symbol (entries[i], sec);
+    ldemul_print_symbol (entries[i], sec);
 
   obstack_free (&map_obstack, entries);
 }
@@ -4627,7 +4868,7 @@ print_all_symbols (asection *sec)
 /* Print information about an input section to the map file.  */
 
 static void
-print_input_section (asection *i, bfd_boolean is_discarded)
+print_input_section (asection *i, bool is_discarded)
 {
   bfd_size_type size = i->size;
   int len;
@@ -4683,7 +4924,7 @@ print_input_section (asection *i, bfd_boolean is_discarded)
       && i->output_section->owner == link_info.output_bfd)
     {
       if (link_info.reduce_memory_overheads)
-       bfd_link_hash_traverse (link_info.hash, print_one_symbol, i);
+       bfd_link_hash_traverse (link_info.hash, ldemul_print_symbol, i);
       else
        print_all_symbols (i);
 
@@ -4999,7 +5240,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, FALSE);
+      print_input_section (s->input_section.section, false);
       break;
     case lang_padding_statement_enum:
       print_padding_statement (&s->padding_statement);
@@ -5116,11 +5357,27 @@ size_input_section
   (lang_statement_union_type **this_ptr,
    lang_output_section_statement_type *output_section_statement,
    fill_type *fill,
+   bool *removed,
    bfd_vma dot)
 {
   lang_input_section_type *is = &((*this_ptr)->input_section);
   asection *i = is->section;
   asection *o = output_section_statement->bfd_section;
+  *removed = 0;
+
+  if (link_info.non_contiguous_regions)
+    {
+      /* If the input section I has already been successfully assigned
+        to an output section other than O, don't bother with it and
+        let the caller remove it from the list.  Keep processing in
+        case we have already handled O, because the repeated passes
+        have reinitialized its size.  */
+      if (i->already_assigned && i->already_assigned != o)
+       {
+         *removed = 1;
+         return dot;
+       }
+    }
 
   if (i->sec_info_type == SEC_INFO_TYPE_JUST_SYMS)
     i->output_offset = i->vma - o->vma;
@@ -5152,6 +5409,36 @@ size_input_section
          dot += alignment_needed;
        }
 
+      if (link_info.non_contiguous_regions)
+       {
+         /* If I would overflow O, let the caller remove I from the
+            list.  */
+         if (output_section_statement->region)
+           {
+             bfd_vma end = output_section_statement->region->origin
+               + output_section_statement->region->length;
+
+             if (dot + TO_ADDR (i->size) > end)
+               {
+                 if (i->flags & SEC_LINKER_CREATED)
+                   einfo (_("%F%P: Output section '%s' not large enough for the "
+                            "linker-created stubs section '%s'.\n"),
+                          i->output_section->name, i->name);
+
+                 if (i->rawsize && i->rawsize != i->size)
+                   einfo (_("%F%P: Relaxation not supported with "
+                            "--enable-non-contiguous-regions (section '%s' "
+                            "would overflow '%s' after it changed size).\n"),
+                          i->name, i->output_section->name);
+
+                 *removed = 1;
+                 dot = end;
+                 i->output_section = NULL;
+                 return dot;
+               }
+           }
+       }
+
       /* Remember where in the output section this input section goes.  */
       i->output_offset = dot - o->vma;
 
@@ -5159,6 +5446,14 @@ size_input_section
       dot += TO_ADDR (i->size);
       if (!(o->flags & SEC_FIXED_SIZE))
        o->size = TO_SIZE (dot - o->vma);
+
+      if (link_info.non_contiguous_regions)
+       {
+         /* Record that I was successfully assigned to O, and update
+            its actual output section too.  */
+         i->already_assigned = o;
+         i->output_section = o;
+       }
     }
 
   return dot;
@@ -5167,7 +5462,7 @@ size_input_section
 struct check_sec
 {
   asection *sec;
-  bfd_boolean warned;
+  bool warned;
 };
 
 static int
@@ -5229,7 +5524,7 @@ lang_check_section_addresses (void)
   bfd_vma p_start = 0;
   bfd_vma p_end = 0;
   lang_memory_region_type *m;
-  bfd_boolean overlays;
+  bool overlays;
 
   /* Detect address space overflow on allocated sections.  */
   addr_mask = ((bfd_vma) 1 <<
@@ -5266,7 +5561,7 @@ lang_check_section_addresses (void)
        continue;
 
       sections[count].sec = s;
-      sections[count].warned = FALSE;
+      sections[count].warned = false;
       count++;
     }
 
@@ -5301,7 +5596,7 @@ lang_check_section_addresses (void)
              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;
+             sections[i].warned = true;
            }
          p = s;
          p_start = s_start;
@@ -5315,14 +5610,14 @@ lang_check_section_addresses (void)
      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;
+  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;
+         overlays = true;
          break;
        }
       p_start = s_start;
@@ -5399,7 +5694,7 @@ os_region_check (lang_output_section_statement_type *os,
        }
       else if (!region->had_full_message)
        {
-         region->had_full_message = TRUE;
+         region->had_full_message = true;
 
          einfo (_("%X%P: %pB section `%s' will not fit in region `%s'\n"),
                 os->bfd_section->owner,
@@ -5410,9 +5705,10 @@ os_region_check (lang_output_section_statement_type *os,
 }
 
 static void
-ldlang_check_relro_region (lang_statement_union_type *s,
-                          seg_align_type *seg)
+ldlang_check_relro_region (lang_statement_union_type *s)
 {
+  seg_align_type *seg = &expld.dataseg;
+
   if (seg->relro == exp_seg_relro_start)
     {
       if (!seg->relro_start_stat)
@@ -5441,14 +5737,18 @@ lang_size_sections_1
    lang_output_section_statement_type *output_section_statement,
    fill_type *fill,
    bfd_vma dot,
-   bfd_boolean *relax,
-   bfd_boolean check_regions)
+   bool *relax,
+   bool check_regions)
 {
   lang_statement_union_type *s;
+  lang_statement_union_type *prev_s = NULL;
+  bool removed_prev_s = false;
 
   /* Size up the sections from their constituent parts.  */
-  for (s = *prev; s != NULL; s = s->header.next)
+  for (s = *prev; s != NULL; prev_s = s, s = s->header.next)
     {
+      bool removed = false;
+
       switch (s->header.type)
        {
        case lang_output_section_statement_enum:
@@ -5473,7 +5773,7 @@ lang_size_sections_1
              os->addr_tree = exp_intop (0);
            if (os->addr_tree != NULL)
              {
-               os->processed_vma = FALSE;
+               os->processed_vma = false;
                exp_fold_tree (os->addr_tree, bfd_abs_section_ptr, &dot);
 
                if (expld.result.valid_p)
@@ -5554,7 +5854,7 @@ lang_size_sections_1
                        && (strcmp (lang_memory_region_list->name_list.name,
                                    DEFAULT_MEMORY_REGION) != 0
                            || lang_memory_region_list->next != NULL)
-                       && expld.phase != lang_mark_phase_enum)
+                       && lang_sizing_iteration == 1)
                      {
                        /* By default this is an error rather than just a
                           warning because if we allocate the section to the
@@ -5586,19 +5886,27 @@ lang_size_sections_1
                if (section_alignment > 0)
                  {
                    bfd_vma savedot = newdot;
-                   newdot = align_power (newdot, section_alignment);
+                   bfd_vma diff = 0;
 
+                   newdot = align_power (newdot, section_alignment);
                    dotdelta = newdot - savedot;
-                   if (dotdelta != 0
+
+                   if (lang_sizing_iteration == 1)
+                     diff = dotdelta;
+                   else if (lang_sizing_iteration > 1)
+                     {
+                       /* Only report adjustments that would change
+                          alignment from what we have already reported.  */
+                       diff = newdot - os->bfd_section->vma;
+                       if (!(diff & (((bfd_vma) 1 << section_alignment) - 1)))
+                         diff = 0;
+                     }
+                   if (diff != 0
                        && (config.warn_section_align
-                           || os->addr_tree != NULL)
-                       && expld.phase != lang_mark_phase_enum)
-                     einfo (ngettext ("%P: warning: changing start of "
-                                      "section %s by %lu byte\n",
-                                      "%P: warning: changing start of "
-                                      "section %s by %lu bytes\n",
-                                      (unsigned long) dotdelta),
-                            os->name, (unsigned long) dotdelta);
+                           || os->addr_tree != NULL))
+                     einfo (_("%P: warning: "
+                              "start of section %s changed by %ld\n"),
+                            os->name, (long) diff);
                  }
 
                bfd_set_section_vma (os->bfd_section, newdot);
@@ -5609,7 +5917,7 @@ lang_size_sections_1
            lang_size_sections_1 (&os->children.head, os,
                                  os->fill, newdot, relax, check_regions);
 
-           os->processed_vma = TRUE;
+           os->processed_vma = true;
 
            if (bfd_is_abs_section (os->bfd_section) || os->ignored)
              /* Except for some special linker created sections,
@@ -5637,7 +5945,7 @@ lang_size_sections_1
            /* Set section lma.  */
            r = os->region;
            if (r == NULL)
-             r = lang_memory_region_lookup (DEFAULT_MEMORY_REGION, FALSE);
+             r = lang_memory_region_lookup (DEFAULT_MEMORY_REGION, false);
 
            if (os->load_base)
              {
@@ -5708,7 +6016,7 @@ lang_size_sections_1
                    os->bfd_section->lma = lma;
                  }
              }
-           os->processed_lma = TRUE;
+           os->processed_lma = true;
 
            /* Keep track of normal sections using the default
               lma region.  We use this to set the lma for
@@ -5866,15 +6174,15 @@ lang_size_sections_1
            i = s->input_section.section;
            if (relax)
              {
-               bfd_boolean again;
+               bool again;
 
                if (!bfd_relax_section (i->owner, i, &link_info, &again))
                  einfo (_("%F%P: can't relax section: %E\n"));
                if (again)
-                 *relax = TRUE;
+                 *relax = true;
              }
            dot = size_input_section (prev, output_section_statement,
-                                     fill, dot);
+                                     fill, &removed, dot);
          }
          break;
 
@@ -5899,7 +6207,7 @@ lang_size_sections_1
                           output_section_statement->bfd_section,
                           &newdot);
 
-           ldlang_check_relro_region (s, &expld.dataseg);
+           ldlang_check_relro_region (s);
 
            expld.dataseg.relro = exp_seg_relro_none;
 
@@ -5917,7 +6225,7 @@ lang_size_sections_1
                    /* If we don't have an output section, then just adjust
                       the default memory address.  */
                    lang_memory_region_lookup (DEFAULT_MEMORY_REGION,
-                                              FALSE)->current = newdot;
+                                              false)->current = newdot;
                  }
                else if (newdot != dot)
                  {
@@ -5979,7 +6287,43 @@ lang_size_sections_1
          FAIL ();
          break;
        }
-      prev = &s->header.next;
+
+      /* If an input section doesn't fit in the current output
+        section, remove it from the list.  Handle the case where we
+        have to remove an input_section statement here: there is a
+        special case to remove the first element of the list.  */
+      if (link_info.non_contiguous_regions && removed)
+       {
+         /* If we removed the first element during the previous
+            iteration, override the loop assignment of prev_s.  */
+         if (removed_prev_s)
+             prev_s = NULL;
+
+         if (prev_s)
+           {
+             /* If there was a real previous input section, just skip
+                the current one.  */
+             prev_s->header.next=s->header.next;
+             s = prev_s;
+             removed_prev_s = false;
+           }
+         else
+           {
+             /* Remove the first input section of the list.  */
+             *prev = s->header.next;
+             removed_prev_s = true;
+           }
+
+         /* Move to next element, unless we removed the head of the
+            list.  */
+         if (!removed_prev_s)
+           prev = &s->header.next;
+       }
+      else
+       {
+         prev = &s->header.next;
+         removed_prev_s = false;
+       }
     }
   return dot;
 }
@@ -5989,12 +6333,12 @@ lang_size_sections_1
    CURRENT_SECTION and PREVIOUS_SECTION ought to be placed into different
    segments.  We are allowed an opportunity to override this decision.  */
 
-bfd_boolean
+bool
 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)
+                                   bool new_segment)
 {
   lang_output_section_statement_type *cur;
   lang_output_section_statement_type *prev;
@@ -6002,7 +6346,7 @@ ldlang_override_segment_assignment (struct bfd_link_info *info ATTRIBUTE_UNUSED,
   /* The checks below are only necessary when the BFD library has decided
      that the two sections ought to be placed into the same segment.  */
   if (new_segment)
-    return TRUE;
+    return true;
 
   /* Paranoia checks.  */
   if (current_section == NULL || previous_section == NULL)
@@ -6012,7 +6356,7 @@ ldlang_override_segment_assignment (struct bfd_link_info *info ATTRIBUTE_UNUSED,
      sections comingled in the same segment.  */
   if (config.separate_code
       && ((current_section->flags ^ previous_section->flags) & SEC_CODE))
-    return TRUE;
+    return true;
 
   /* Find the memory regions associated with the two sections.
      We call lang_output_section_find() here rather than scanning the list
@@ -6033,44 +6377,47 @@ ldlang_override_segment_assignment (struct bfd_link_info *info ATTRIBUTE_UNUSED,
 }
 
 void
-one_lang_size_sections_pass (bfd_boolean *relax, bfd_boolean check_regions)
+one_lang_size_sections_pass (bool *relax, bool check_regions)
 {
   lang_statement_iteration++;
+  if (expld.phase != lang_mark_phase_enum)
+    lang_sizing_iteration++;
   lang_size_sections_1 (&statement_list.head, abs_output_section,
                        0, 0, relax, check_regions);
 }
 
-static bfd_boolean
-lang_size_segment (seg_align_type *seg)
+static bool
+lang_size_segment (void)
 {
   /* If XXX_SEGMENT_ALIGN XXX_SEGMENT_END pair was seen, check whether
      a page could be saved in the data segment.  */
+  seg_align_type *seg = &expld.dataseg;
   bfd_vma first, last;
 
-  first = -seg->base & (seg->pagesize - 1);
-  last = seg->end & (seg->pagesize - 1);
+  first = -seg->base & (seg->commonpagesize - 1);
+  last = seg->end & (seg->commonpagesize - 1);
   if (first && last
-      && ((seg->base & ~(seg->pagesize - 1))
-         != (seg->end & ~(seg->pagesize - 1)))
-      && first + last <= seg->pagesize)
+      && ((seg->base & ~(seg->commonpagesize - 1))
+         != (seg->end & ~(seg->commonpagesize - 1)))
+      && first + last <= seg->commonpagesize)
     {
       seg->phase = exp_seg_adjust;
-      return TRUE;
+      return true;
     }
 
   seg->phase = exp_seg_done;
-  return FALSE;
+  return false;
 }
 
 static bfd_vma
-lang_size_relro_segment_1 (seg_align_type *seg)
+lang_size_relro_segment_1 (void)
 {
+  seg_align_type *seg = &expld.dataseg;
   bfd_vma relro_end, desired_end;
   asection *sec;
 
   /* Compute the expected PT_GNU_RELRO/PT_LOAD segment end.  */
-  relro_end = ((seg->relro_end + seg->pagesize - 1)
-              & ~(seg->pagesize - 1));
+  relro_end = (seg->relro_end + seg->relropagesize - 1) & -seg->relropagesize;
 
   /* Adjust by the offset arg of XXX_SEGMENT_RELRO_END.  */
   desired_end = relro_end - seg->relro_offset;
@@ -6103,47 +6450,35 @@ lang_size_relro_segment_1 (seg_align_type *seg)
   return relro_end;
 }
 
-static bfd_boolean
-lang_size_relro_segment (bfd_boolean *relax, bfd_boolean check_regions)
+static bool
+lang_size_relro_segment (bool *relax, bool check_regions)
 {
-  bfd_boolean do_reset = FALSE;
-  bfd_boolean do_data_relro;
-  bfd_vma data_initial_base, data_relro_end;
+  bool do_reset = false;
 
   if (link_info.relro && expld.dataseg.relro_end)
     {
-      do_data_relro = TRUE;
-      data_initial_base = expld.dataseg.base;
-      data_relro_end = lang_size_relro_segment_1 (&expld.dataseg);
-    }
-  else
-    {
-      do_data_relro = FALSE;
-      data_initial_base = data_relro_end = 0;
-    }
+      bfd_vma data_initial_base = expld.dataseg.base;
+      bfd_vma data_relro_end = lang_size_relro_segment_1 ();
 
-  if (do_data_relro)
-    {
       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.  */
-      if (do_data_relro && expld.dataseg.relro_end > data_relro_end)
+      if (expld.dataseg.relro_end > data_relro_end)
        {
-         expld.dataseg.base = data_initial_base;;
-         do_reset = TRUE;
+         expld.dataseg.base = data_initial_base;
+         do_reset = true;
        }
     }
-
-  if (!do_data_relro && lang_size_segment (&expld.dataseg))
-    do_reset = TRUE;
+  else if (lang_size_segment ())
+    do_reset = true;
 
   return do_reset;
 }
 
 void
-lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions)
+lang_size_sections (bool *relax, bool check_regions)
 {
   expld.phase = lang_allocating_phase_enum;
   expld.dataseg.phase = exp_seg_none;
@@ -6155,7 +6490,7 @@ lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions)
 
   if (expld.dataseg.phase == exp_seg_end_seen)
     {
-      bfd_boolean do_reset
+      bool do_reset
        = lang_size_relro_segment (relax, check_regions);
 
       if (do_reset)
@@ -6174,7 +6509,7 @@ lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions)
 
 static lang_output_section_statement_type *current_section;
 static lang_assignment_statement_type *current_assign;
-static bfd_boolean prefer_next_section;
+static bool prefer_next_section;
 
 /* Worker function for lang_do_assignments.  Recursiveness goes here.  */
 
@@ -6183,7 +6518,7 @@ lang_do_assignments_1 (lang_statement_union_type *s,
                       lang_output_section_statement_type *current_os,
                       fill_type *fill,
                       bfd_vma dot,
-                      bfd_boolean *found_end)
+                      bool *found_end)
 {
   for (; s != NULL; s = s->header.next)
     {
@@ -6202,32 +6537,34 @@ lang_do_assignments_1 (lang_statement_union_type *s,
            os = &(s->output_section_statement);
            os->after_end = *found_end;
            init_opb (os->bfd_section);
-           if (os->bfd_section != NULL && !os->ignored)
+           newdot = dot;
+           if (os->bfd_section != NULL)
              {
-               if ((os->bfd_section->flags & SEC_ALLOC) != 0)
+               if (!os->ignored && (os->bfd_section->flags & SEC_ALLOC) != 0)
                  {
                    current_section = os;
-                   prefer_next_section = FALSE;
+                   prefer_next_section = false;
                  }
-               dot = os->bfd_section->vma;
+               newdot = os->bfd_section->vma;
              }
            newdot = lang_do_assignments_1 (os->children.head,
-                                           os, os->fill, dot, found_end);
+                                           os, os->fill, newdot, found_end);
            if (!os->ignored)
              {
                if (os->bfd_section != NULL)
                  {
+                   newdot = os->bfd_section->vma;
+
                    /* .tbss sections effectively have zero size.  */
                    if (!IS_TBSS (os->bfd_section)
                        || bfd_link_relocatable (&link_info))
-                     dot += TO_ADDR (os->bfd_section->size);
+                     newdot += TO_ADDR (os->bfd_section->size);
 
                    if (os->update_dot_tree != NULL)
                      exp_fold_tree (os->update_dot_tree,
-                                    bfd_abs_section_ptr, &dot);
+                                    bfd_abs_section_ptr, &newdot);
                  }
-               else
-                 dot = newdot;
+               dot = newdot;
              }
          }
          break;
@@ -6312,12 +6649,12 @@ lang_do_assignments_1 (lang_statement_union_type *s,
              const char *p = current_assign->exp->assign.dst;
 
              if (current_os == abs_output_section && p[0] == '.' && p[1] == 0)
-               prefer_next_section = TRUE;
+               prefer_next_section = true;
 
              while (*p == '_')
                ++p;
              if (strcmp (p, "end") == 0)
-               *found_end = TRUE;
+               *found_end = true;
            }
          exp_fold_tree (s->assignment_statement.exp,
                         (current_os->bfd_section != NULL
@@ -6351,10 +6688,10 @@ lang_do_assignments_1 (lang_statement_union_type *s,
 void
 lang_do_assignments (lang_phase_type phase)
 {
-  bfd_boolean found_end = FALSE;
+  bool found_end = false;
 
   current_section = NULL;
-  prefer_next_section = FALSE;
+  prefer_next_section = false;
   expld.phase = phase;
   lang_statement_iteration++;
   lang_do_assignments_1 (statement_list.head,
@@ -6549,6 +6886,19 @@ undef_start_stop (struct bfd_link_hash_entry *h)
        }
       h->type = bfd_link_hash_undefined;
       h->u.undef.abfd = NULL;
+      if (is_elf_hash_table (link_info.hash))
+       {
+         const struct elf_backend_data *bed;
+         struct elf_link_hash_entry *eh = (struct elf_link_hash_entry *) h;
+         unsigned int was_forced = eh->forced_local;
+
+         bed = get_elf_backend_data (link_info.output_bfd);
+         (*bed->elf_backend_hide_symbol) (&link_info, eh, true);
+         if (!eh->ref_regular_nonweak)
+           h->type = bfd_link_hash_undefweak;
+         eh->def_regular = 0;
+         eh->forced_local = was_forced;
+       }
     }
 }
 
@@ -6621,37 +6971,89 @@ lang_finalize_start_stop (void)
   foreach_start_stop (set_start_stop);
 }
 
+static void
+lang_symbol_tweaks (void)
+{
+  /* Give initial values for __start and __stop symbols, so that  ELF
+     gc_sections will keep sections referenced by these symbols.  Must
+     be done before lang_do_assignments.  */
+  if (config.build_constructors)
+    lang_init_start_stop ();
+
+  /* Make __ehdr_start hidden, and set def_regular even though it is
+     likely undefined at this stage.  For lang_check_relocs.  */
+  if (is_elf_hash_table (link_info.hash)
+      && !bfd_link_relocatable (&link_info))
+    {
+      struct elf_link_hash_entry *h = (struct elf_link_hash_entry *)
+       bfd_link_hash_lookup (link_info.hash, "__ehdr_start",
+                             false, false, true);
+
+      /* Only adjust the export class if the symbol was referenced
+        and not defined, otherwise leave it alone.  */
+      if (h != NULL
+         && (h->root.type == bfd_link_hash_new
+             || h->root.type == bfd_link_hash_undefined
+             || h->root.type == bfd_link_hash_undefweak
+             || h->root.type == bfd_link_hash_common))
+       {
+         const struct elf_backend_data *bed;
+         bed = get_elf_backend_data (link_info.output_bfd);
+         (*bed->elf_backend_hide_symbol) (&link_info, h, true);
+         if (ELF_ST_VISIBILITY (h->other) != STV_INTERNAL)
+           h->other = (h->other & ~ELF_ST_VISIBILITY (-1)) | STV_HIDDEN;
+         h->def_regular = 1;
+         h->root.linker_def = 1;
+         h->root.rel_from_abs = 1;
+       }
+    }
+}
+
 static void
 lang_end (void)
 {
   struct bfd_link_hash_entry *h;
-  bfd_boolean warn;
+  bool warn;
 
   if ((bfd_link_relocatable (&link_info) && !link_info.gc_sections)
       || bfd_link_dll (&link_info))
     warn = entry_from_cmdline;
   else
-    warn = TRUE;
+    warn = true;
 
   /* Force the user to specify a root when generating a relocatable with
      --gc-sections, unless --gc-keep-exported was also given.  */
   if (bfd_link_relocatable (&link_info)
       && link_info.gc_sections
-      && !link_info.gc_keep_exported
-      && !(entry_from_cmdline || undef_from_cmdline))
-    einfo (_("%F%P: gc-sections requires either an entry or "
-            "an undefined symbol\n"));
+      && !link_info.gc_keep_exported)
+    {
+      struct bfd_sym_chain *sym;
+
+      for (sym = link_info.gc_sym_list; sym != NULL; sym = sym->next)
+       {
+         h = bfd_link_hash_lookup (link_info.hash, sym->name,
+                                   false, false, false);
+         if (h != NULL
+             && (h->type == bfd_link_hash_defined
+                 || h->type == bfd_link_hash_defweak)
+             && !bfd_is_const_section (h->u.def.section))
+           break;
+       }
+      if (!sym)
+       einfo (_("%F%P: --gc-sections requires a defined symbol root "
+                "specified by -e or -u\n"));
+    }
 
   if (entry_symbol.name == NULL)
     {
       /* No entry has been specified.  Look for the default entry, but
         don't warn if we don't find it.  */
       entry_symbol.name = entry_symbol_default;
-      warn = FALSE;
+      warn = false;
     }
 
   h = bfd_link_hash_lookup (link_info.hash, entry_symbol.name,
-                           FALSE, FALSE, TRUE);
+                           false, false, true);
   if (h != NULL
       && (h->type == bfd_link_hash_defined
          || h->type == bfd_link_hash_defweak)
@@ -6678,7 +7080,8 @@ lang_end (void)
          if (!bfd_set_start_address (link_info.output_bfd, val))
            einfo (_("%F%P: can't set start address\n"));
        }
-      else
+      /* BZ 2004952: Only use the start of the entry section for executables.  */
+      else if bfd_link_executable (&link_info)
        {
          asection *ts;
 
@@ -6704,6 +7107,13 @@ lang_end (void)
                       entry_symbol.name);
            }
        }
+      else
+       {
+         if (warn)
+           einfo (_("%P: warning: cannot find entry symbol %s;"
+                    " not setting start address\n"),
+                  entry_symbol.name);
+       }
     }
 }
 
@@ -6728,15 +7138,15 @@ lang_check (void)
   bfd *input_bfd;
   const bfd_arch_info_type *compatible;
 
-  for (file = &file_chain.head->input_statement;
+  for (file = (void *) file_chain.head;
        file != NULL;
        file = file->next)
     {
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_PLUGINS
       /* Don't check format of files claimed by plugin.  */
       if (file->flags.claimed)
        continue;
-#endif /* ENABLE_PLUGINS */
+#endif /* BFD_SUPPORTS_PLUGINS */
       input_bfd = file->the_bfd;
       compatible
        = bfd_arch_get_compatible (input_bfd, link_info.output_bfd,
@@ -6748,8 +7158,9 @@ 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 ((bfd_link_relocatable (&link_info)
-          || link_info.emitrelocations)
+      if (!file->flags.just_syms
+         && (bfd_link_relocatable (&link_info)
+             || link_info.emitrelocations)
          && (compatible == NULL
              || (bfd_get_flavour (input_bfd)
                  != bfd_get_flavour (link_info.output_bfd)))
@@ -6770,11 +7181,13 @@ lang_check (void)
                   bfd_printable_name (input_bfd), input_bfd,
                   bfd_printable_name (link_info.output_bfd));
        }
-      else if (bfd_count_sections (input_bfd))
-       {
-         /* If the input bfd has no contents, it shouldn't set the
-            private data of the output bfd.  */
 
+      /* If the input bfd has no contents, it shouldn't set the
+        private data of the output bfd.  */
+      else if (!file->flags.just_syms
+              && ((input_bfd->flags & DYNAMIC) != 0
+                  || bfd_count_sections (input_bfd) != 0))
+       {
          bfd_error_handler_type pfn = NULL;
 
          /* If we aren't supposed to warn about mismatched input
@@ -6836,7 +7249,7 @@ lang_common (void)
 
 /* Place one common symbol in the correct section.  */
 
-static bfd_boolean
+static bool
 lang_one_common (struct bfd_link_hash_entry *h, void *info)
 {
   unsigned int power_of_two;
@@ -6844,17 +7257,17 @@ lang_one_common (struct bfd_link_hash_entry *h, void *info)
   asection *section;
 
   if (h->type != bfd_link_hash_common)
-    return TRUE;
+    return true;
 
   size = h->u.c.size;
   power_of_two = h->u.c.p->alignment_power;
 
   if (config.sort_common == sort_descending
       && power_of_two < *(unsigned int *) info)
-    return TRUE;
+    return true;
   else if (config.sort_common == sort_ascending
           && power_of_two > *(unsigned int *) info)
-    return TRUE;
+    return true;
 
   section = h->u.c.p->section;
   if (!bfd_define_common_symbol (link_info.output_bfd, &link_info, h))
@@ -6863,7 +7276,7 @@ lang_one_common (struct bfd_link_hash_entry *h, void *info)
 
   if (config.map_file != NULL)
     {
-      static bfd_boolean header_printed;
+      static bool header_printed;
       int len;
       char *name;
       char buf[50];
@@ -6872,7 +7285,7 @@ lang_one_common (struct bfd_link_hash_entry *h, void *info)
        {
          minfo (_("\nAllocating common symbols\n"));
          minfo (_("Common symbol       size              file\n\n"));
-         header_printed = TRUE;
+         header_printed = true;
        }
 
       name = bfd_demangle (link_info.output_bfd, h->root.string,
@@ -6917,7 +7330,7 @@ lang_one_common (struct bfd_link_hash_entry *h, void *info)
       minfo ("%pB\n", section->owner);
     }
 
-  return TRUE;
+  return true;
 }
 
 /* Handle a single orphan section S, placing the orphan into an appropriate
@@ -6930,13 +7343,12 @@ 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);
+      os = lang_output_section_statement_lookup (DISCARD_SECTION_NAME, 0, 1);
       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);
+      lang_add_section (&os->children, s, NULL, NULL, os);
     }
   else
     {
@@ -6954,12 +7366,12 @@ ldlang_place_orphan (asection *s)
       os = ldemul_place_orphan (s, name, constraint);
       if (os == NULL)
        {
-         os = lang_output_section_statement_lookup (name, constraint, TRUE);
+         os = lang_output_section_statement_lookup (name, constraint, 1);
          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);
+         lang_add_section (&os->children, s, NULL, NULL, os);
        }
 
       if (config.orphan_handling == orphan_handling_warn)
@@ -7001,10 +7413,9 @@ lang_place_orphans (void)
                    {
                      if (default_common_section == NULL)
                        default_common_section
-                         = lang_output_section_statement_lookup (".bss", 0,
-                                                                 TRUE);
+                         = lang_output_section_statement_lookup (".bss", 0, 1);
                      lang_add_section (&default_common_section->children, s,
-                                       NULL, default_common_section);
+                                       NULL, NULL, default_common_section);
                    }
                }
              else
@@ -7070,7 +7481,7 @@ lang_for_each_input_file (void (*func) (lang_input_statement_type *))
 {
   lang_input_statement_type *f;
 
-  for (f = &input_file_chain.head->input_statement;
+  for (f = (void *) input_file_chain.head;
        f != NULL;
        f = f->next_real_file)
     if (f->flags.real)
@@ -7098,7 +7509,8 @@ 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 (link_info.input_bfds_tail != &entry->the_bfd->link.next
+         && entry->the_bfd->link.next == NULL);
   ASSERT (entry->the_bfd != link_info.output_bfd);
 
   *link_info.input_bfds_tail = entry->the_bfd;
@@ -7126,7 +7538,7 @@ lang_add_output (const char *name, int from_script)
   if (!had_output_filename || !from_script)
     {
       output_filename = name;
-      had_output_filename = TRUE;
+      had_output_filename = true;
     }
 }
 
@@ -7134,6 +7546,7 @@ lang_output_section_statement_type *
 lang_enter_output_section_statement (const char *output_section_statement_name,
                                     etree_type *address_exp,
                                     enum section_type sectype,
+                                    etree_type *sectype_value,
                                     etree_type *align,
                                     etree_type *subalign,
                                     etree_type *ebase,
@@ -7143,7 +7556,7 @@ lang_enter_output_section_statement (const char *output_section_statement_name,
   lang_output_section_statement_type *os;
 
   os = lang_output_section_statement_lookup (output_section_statement_name,
-                                            constraint, TRUE);
+                                            constraint, 2);
   current_section = os;
 
   if (os->addr_tree == NULL)
@@ -7151,10 +7564,12 @@ lang_enter_output_section_statement (const char *output_section_statement_name,
       os->addr_tree = address_exp;
     }
   os->sectype = sectype;
-  if (sectype != noload_section)
-    os->flags = SEC_NO_FLAGS;
-  else
+  if (sectype == type_section || sectype == typed_readonly_section)
+    os->sectype_value = sectype_value;
+  else if (sectype == noload_section)
     os->flags = SEC_NEVER_LOAD;
+  else
+    os->flags = SEC_NO_FLAGS;
   os->block_value = 1;
 
   /* Make next things chain into subchain of this.  */
@@ -7196,12 +7611,12 @@ lang_reset_memory_regions (void)
       p->last_os = NULL;
     }
 
-  for (os = &lang_os_list.head->output_section_statement;
+  for (os = (void *) lang_os_list.head;
        os != NULL;
        os = os->next)
     {
-      os->processed_vma = FALSE;
-      os->processed_lma = FALSE;
+      os->processed_vma = false;
+      os->processed_lma = false;
     }
 
   for (o = link_info.output_bfd->sections; o != NULL; o = o->next)
@@ -7219,7 +7634,6 @@ 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)
 {
@@ -7263,19 +7677,20 @@ lang_gc_sections (void)
   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)
+     the special case of .stabstr debug info.  (See bfd/stabs.c)
      Twiddle the flag here, to simplify later linker code.  */
   if (bfd_link_relocatable (&link_info))
     {
       LANG_FOR_EACH_INPUT_STATEMENT (f)
        {
          asection *sec;
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_PLUGINS
          if (f->flags.claimed)
            continue;
 #endif
          for (sec = f->the_bfd->sections; sec != NULL; sec = sec->next)
-           if ((sec->flags & SEC_DEBUGGING) == 0)
+           if ((sec->flags & SEC_DEBUGGING) == 0
+               || strcmp (sec->name, ".stabstr") != 0)
              sec->flags &= ~SEC_EXCLUDE;
        }
     }
@@ -7290,7 +7705,6 @@ 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)
 {
@@ -7302,8 +7716,8 @@ find_relro_section_callback (lang_wild_statement_type *ptr ATTRIBUTE_UNUSED,
       && !IGNORE_SECTION (section)
       && section->size != 0)
     {
-      bfd_boolean *has_relro_section = (bfd_boolean *) data;
-      *has_relro_section = TRUE;
+      bool *has_relro_section = (bool *) data;
+      *has_relro_section = true;
     }
 }
 
@@ -7311,15 +7725,14 @@ find_relro_section_callback (lang_wild_statement_type *ptr ATTRIBUTE_UNUSED,
 
 static void
 lang_find_relro_sections_1 (lang_statement_union_type *s,
-                           seg_align_type *seg,
-                           bfd_boolean *has_relro_section)
+                           bool *has_relro_section)
 {
   if (*has_relro_section)
     return;
 
   for (; s != NULL; s = s->header.next)
     {
-      if (s == seg->relro_end_stat)
+      if (s == expld.dataseg.relro_end_stat)
        break;
 
       switch (s->header.type)
@@ -7331,15 +7744,15 @@ lang_find_relro_sections_1 (lang_statement_union_type *s,
          break;
        case lang_constructors_statement_enum:
          lang_find_relro_sections_1 (constructor_list.head,
-                                     seg, has_relro_section);
+                                     has_relro_section);
          break;
        case lang_output_section_statement_enum:
          lang_find_relro_sections_1 (s->output_section_statement.children.head,
-                                     seg, has_relro_section);
+                                     has_relro_section);
          break;
        case lang_group_statement_enum:
          lang_find_relro_sections_1 (s->group_statement.children.head,
-                                     seg, has_relro_section);
+                                     has_relro_section);
          break;
        default:
          break;
@@ -7350,23 +7763,24 @@ lang_find_relro_sections_1 (lang_statement_union_type *s,
 static void
 lang_find_relro_sections (void)
 {
-  bfd_boolean has_relro_section = FALSE;
+  bool has_relro_section = false;
 
   /* Check all sections in the link script.  */
 
   lang_find_relro_sections_1 (expld.dataseg.relro_start_stat,
-                             &expld.dataseg, &has_relro_section);
+                             &has_relro_section);
 
   if (!has_relro_section)
-    link_info.relro = FALSE;
+    link_info.relro = false;
 }
 
 /* Relax all sections until bfd_relax_section gives up.  */
 
 void
-lang_relax_sections (bfd_boolean need_layout)
+lang_relax_sections (bool need_layout)
 {
-  if (RELAXATION_ENABLED)
+  /* NB: Also enable relaxation to layout sections for DT_RELR.  */
+  if (RELAXATION_ENABLED || link_info.enable_dt_relr)
     {
       /* We may need more than one relaxation pass.  */
       int i = link_info.relax_pass;
@@ -7377,7 +7791,7 @@ lang_relax_sections (bfd_boolean need_layout)
       while (i--)
        {
          /* Keep relaxing until bfd_relax_section gives up.  */
-         bfd_boolean relax_again;
+         bool relax_again;
 
          link_info.relax_trip = -1;
          do
@@ -7398,14 +7812,14 @@ lang_relax_sections (bfd_boolean need_layout)
 
              /* 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);
+             relax_again = false;
+             lang_size_sections (&relax_again, false);
            }
          while (relax_again);
 
          link_info.relax_pass++;
        }
-      need_layout = TRUE;
+      need_layout = true;
     }
 
   if (need_layout)
@@ -7413,11 +7827,11 @@ lang_relax_sections (bfd_boolean need_layout)
       /* Final extra sizing to report errors.  */
       lang_do_assignments (lang_assigning_phase_enum);
       lang_reset_memory_regions ();
-      lang_size_sections (NULL, TRUE);
+      lang_size_sections (NULL, true);
     }
 }
 
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_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
@@ -7430,11 +7844,11 @@ lang_relax_sections (bfd_boolean need_layout)
    inserted at the head of the file_chain.  */
 
 static lang_input_statement_type *
-find_replacements_insert_point (bfd_boolean *before)
+find_replacements_insert_point (bool *before)
 {
   lang_input_statement_type *claim1, *lastobject;
-  lastobject = &input_file_chain.head->input_statement;
-  for (claim1 = &file_chain.head->input_statement;
+  lastobject = (void *) input_file_chain.head;
+  for (claim1 = (void *) file_chain.head;
        claim1 != NULL;
        claim1 = claim1->next)
     {
@@ -7450,7 +7864,7 @@ find_replacements_insert_point (bfd_boolean *before)
   /* 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.  */
-  *before = FALSE;
+  *before = false;
   return lastobject;
 }
 
@@ -7476,7 +7890,7 @@ find_rescan_insertion (lang_input_statement_type *add)
      file chain if it is full of archive elements.  Archives don't
      appear on the file chain, but if an element has been extracted
      then their input_statement->next points at it.  */
-  for (f = &input_file_chain.head->input_statement;
+  for (f = (void *) input_file_chain.head;
        f != NULL;
        f = f->next_real_file)
     {
@@ -7560,7 +7974,7 @@ find_next_input_statement (lang_statement_union_type **s)
     }
   return s;
 }
-#endif /* ENABLE_PLUGINS */
+#endif /* BFD_SUPPORTS_PLUGINS */
 
 /* Add NAME to the list of garbage collection entry points.  */
 
@@ -7593,7 +8007,7 @@ lang_check_relocs (void)
        if (!bfd_link_check_relocs (abfd, &link_info))
          {
            /* No object output, fail return.  */
-           config.make_executable = FALSE;
+           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.  */
@@ -7609,7 +8023,7 @@ lang_propagate_lma_regions (void)
 {
   lang_output_section_statement_type *os;
 
-  for (os = &lang_os_list.head->output_section_statement;
+  for (os = (void *) lang_os_list.head;
        os != NULL;
        os = os->next)
     {
@@ -7643,15 +8057,24 @@ lang_process (void)
   if (!bfd_section_already_linked_table_init ())
     einfo (_("%F%P: can not create hash table: %E\n"));
 
+  /* A first pass through the memory regions ensures that if any region
+     references a symbol for its origin or length then this symbol will be
+     added to the symbol table.  Having these symbols in the symbol table
+     means that when we call open_input_bfds PROVIDE statements will
+     trigger to provide any needed symbols.  The regions origins and
+     lengths are not assigned as a result of this call.  */
+  lang_do_memory_regions (false);
+
   /* Create a bfd for each input file.  */
   current_target = default_target;
   lang_statement_iteration++;
   open_input_bfds (statement_list.head, OPEN_BFD_NORMAL);
-  /* open_input_bfds also handles assignments, so we can give values
-     to symbolic origin/length now.  */
-  lang_do_memory_regions ();
 
-#ifdef ENABLE_PLUGINS
+  /* Now that open_input_bfds has processed assignments and provide
+     statements we can give values to symbolic origin/length now.  */
+  lang_do_memory_regions (true);
+
+#if BFD_SUPPORTS_PLUGINS
   if (link_info.lto_plugin_active)
     {
       lang_statement_list_type added;
@@ -7670,6 +8093,7 @@ lang_process (void)
       if (plugin_call_all_symbols_read ())
        einfo (_("%F%P: %s: plugin reported error after all symbols read\n"),
               plugin_error_plugin ());
+      link_info.lto_all_symbols_read = true;
       /* Open any newly added files, updating the file chains.  */
       plugin_undefs = link_info.hash->undefs_tail;
       open_input_bfds (*added.tail, OPEN_BFD_NORMAL);
@@ -7687,7 +8111,7 @@ lang_process (void)
             after the first input file that was claimed by the plugin,
             unless that file was an archive in which case it is inserted
             immediately before.  */
-         bfd_boolean before;
+         bool before;
          lang_statement_union_type **prev;
          plugin_insert = find_replacements_insert_point (&before);
          /* If a plugin adds input files without having claimed any, we
@@ -7754,7 +8178,7 @@ lang_process (void)
            }
        }
     }
-#endif /* ENABLE_PLUGINS */
+#endif /* BFD_SUPPORTS_PLUGINS */
 
   /* Make sure that nobody has tried to add a symbol to this list
      before now.  */
@@ -7803,11 +8227,7 @@ lang_process (void)
      files.  */
   ldctor_build_sets ();
 
-  /* Give initial values for __start and __stop symbols, so that  ELF
-     gc_sections will keep sections referenced by these symbols.  Must
-     be done before lang_do_assignments below.  */
-  if (config.build_constructors)
-    lang_init_start_stop ();
+  lang_symbol_tweaks ();
 
   /* PR 13683: We must rerun the assignments prior to running garbage
      collection in order to make sure that all symbol aliases are resolved.  */
@@ -7820,6 +8240,8 @@ lang_process (void)
   /* Remove unreferenced sections if asked to.  */
   lang_gc_sections ();
 
+  lang_mark_undefineds ();
+
   /* Check relocations.  */
   lang_check_relocs ();
 
@@ -7837,6 +8259,8 @@ lang_process (void)
      output statement, so that it isn't reordered.  */
   process_insert_statements (&lang_os_list.head->header.next);
 
+  ldemul_before_place_orphans ();
+
   /* Find any sections not attached explicitly and handle them.  */
   lang_place_orphans ();
 
@@ -7933,7 +8357,7 @@ lang_process (void)
 void
 lang_add_wild (struct wildcard_spec *filespec,
               struct wildcard_list *section_list,
-              bfd_boolean keep_sections)
+              bool keep_sections)
 {
   struct wildcard_list *curr, *next;
   lang_wild_statement_type *new_stmt;
@@ -7952,12 +8376,12 @@ lang_add_wild (struct wildcard_spec *filespec,
       if (strcmp (filespec->name, "*") == 0)
        filespec->name = NULL;
       else if (!wildcardp (filespec->name))
-       lang_has_input_file = TRUE;
+       lang_has_input_file = true;
     }
 
   new_stmt = new_stat (lang_wild_statement, stat_ptr);
   new_stmt->filename = NULL;
-  new_stmt->filenames_sorted = FALSE;
+  new_stmt->filenames_sorted = false;
   new_stmt->section_flag_list = NULL;
   new_stmt->exclude_name_list = NULL;
   if (filespec != NULL)
@@ -7991,7 +8415,7 @@ lang_section_start (const char *name, etree_type *address,
    precedence.  */
 
 void
-lang_add_entry (const char *name, bfd_boolean cmdline)
+lang_add_entry (const char *name, bool cmdline)
 {
   if (entry_symbol.name == NULL
       || cmdline
@@ -8029,7 +8453,7 @@ lang_add_map (const char *name)
       switch (*name)
        {
        case 'F':
-         map_option_f = TRUE;
+         map_option_f = true;
          break;
        }
       name++;
@@ -8107,11 +8531,11 @@ lang_startup (const char *name)
     }
   first_file->filename = name;
   first_file->local_sym_name = name;
-  first_file->flags.real = TRUE;
+  first_file->flags.real = true;
 }
 
 void
-lang_float (bfd_boolean maybe)
+lang_float (bool maybe)
 {
   lang_float_flag = maybe;
 }
@@ -8133,10 +8557,10 @@ lang_get_regions (lang_memory_region_type **region,
                  lang_memory_region_type **lma_region,
                  const char *memspec,
                  const char *lma_memspec,
-                 bfd_boolean have_lma,
-                 bfd_boolean have_vma)
+                 bool have_lma,
+                 bool have_vma)
 {
-  *lma_region = lang_memory_region_lookup (lma_memspec, FALSE);
+  *lma_region = lang_memory_region_lookup (lma_memspec, false);
 
   /* If no runtime region or VMA has been specified, but the load region
      has been specified, then use the load region for the runtime region
@@ -8146,7 +8570,7 @@ lang_get_regions (lang_memory_region_type **region,
       && strcmp (memspec, DEFAULT_MEMORY_REGION) == 0)
     *region = *lma_region;
   else
-    *region = lang_memory_region_lookup (memspec, FALSE);
+    *region = lang_memory_region_lookup (memspec, false);
 
   if (have_lma && lma_memspec != 0)
     einfo (_("%X%P:%pS: section has both a load address and a load region\n"),
@@ -8231,13 +8655,13 @@ lang_leave_group (void)
 void
 lang_new_phdr (const char *name,
               etree_type *type,
-              bfd_boolean filehdr,
-              bfd_boolean phdrs,
+              bool filehdr,
+              bool phdrs,
               etree_type *at,
               etree_type *flags)
 {
   struct lang_phdr *n, **pp;
-  bfd_boolean hdrs;
+  bool hdrs;
 
   n = stat_alloc (sizeof (struct lang_phdr));
   n->next = NULL;
@@ -8257,7 +8681,7 @@ lang_new_phdr (const char *name,
       {
        einfo (_("%X%P:%pS: PHDRS and FILEHDR are not supported"
                 " when prior PT_LOAD headers lack them\n"), NULL);
-       hdrs = FALSE;
+       hdrs = false;
       }
 
   *pp = n;
@@ -8286,7 +8710,7 @@ lang_record_phdrs (void)
       bfd_vma at;
 
       c = 0;
-      for (os = &lang_os_list.head->output_section_statement;
+      for (os = (void *) lang_os_list.head;
           os != NULL;
           os = os->next)
        {
@@ -8348,7 +8772,7 @@ lang_record_phdrs (void)
                    }
                  secs[c] = os->bfd_section;
                  ++c;
-                 pl->used = TRUE;
+                 pl->used = true;
                }
            }
        }
@@ -8372,7 +8796,7 @@ lang_record_phdrs (void)
   free (secs);
 
   /* Make sure all the phdr assignments succeeded.  */
-  for (os = &lang_os_list.head->output_section_statement;
+  for (os = (void *) lang_os_list.head;
        os != NULL;
        os = os->next)
     {
@@ -8401,11 +8825,11 @@ lang_add_nocrossref (lang_nocrossref_type *l)
   n = (struct lang_nocrossrefs *) xmalloc (sizeof *n);
   n->next = nocrossref_list;
   n->list = l;
-  n->onlyfirst = FALSE;
+  n->onlyfirst = false;
   nocrossref_list = n;
 
   /* Set notice_all so that we get informed about all symbols.  */
-  link_info.notice_all = TRUE;
+  link_info.notice_all = true;
 }
 
 /* Record a section that cannot be referenced from a list of sections.  */
@@ -8414,7 +8838,7 @@ void
 lang_add_nocrossref_to (lang_nocrossref_type *l)
 {
   lang_add_nocrossref (l);
-  nocrossref_list->onlyfirst = TRUE;
+  nocrossref_list->onlyfirst = true;
 }
 \f
 /* Overlay handling.  We handle overlays with some static variables.  */
@@ -8461,7 +8885,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);
+                                      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
@@ -8517,7 +8941,7 @@ lang_leave_overlay_section (fill_type *fill,
   sprintf (buf, "__load_start_%s", clean);
   lang_add_assignment (exp_provide (buf,
                                    exp_nameop (LOADADDR, name),
-                                   FALSE));
+                                   false));
 
   buf = (char *) xmalloc (strlen (clean) + sizeof "__load_stop_");
   sprintf (buf, "__load_stop_%s", clean);
@@ -8525,7 +8949,7 @@ lang_leave_overlay_section (fill_type *fill,
                                    exp_binop ('+',
                                               exp_nameop (LOADADDR, name),
                                               exp_nameop (SIZEOF, name)),
-                                   FALSE));
+                                   false));
 
   free (clean);
 }
@@ -8548,7 +8972,7 @@ lang_leave_overlay (etree_type *lma_expr,
 
   lang_get_regions (&region, &lma_region,
                    memspec, lma_memspec,
-                   lma_expr != NULL, FALSE);
+                   lma_expr != NULL, false);
 
   nocrossref = NULL;
 
@@ -8558,7 +8982,7 @@ lang_leave_overlay (etree_type *lma_expr,
     {
       overlay_list->os->update_dot = 1;
       overlay_list->os->update_dot_tree
-       = exp_assign (".", exp_binop ('+', overlay_vma, overlay_max), FALSE);
+       = exp_assign (".", exp_binop ('+', overlay_vma, overlay_max), false);
     }
 
   l = overlay_list;
@@ -8738,7 +9162,7 @@ static const char *
 realsymbol (const char *pattern)
 {
   const char *p;
-  bfd_boolean changed = FALSE, backslash = FALSE;
+  bool changed = false, backslash = false;
   char *s, *symbol = (char *) xmalloc (strlen (pattern) + 1);
 
   for (p = pattern, s = symbol; *p != '\0'; ++p)
@@ -8749,8 +9173,8 @@ realsymbol (const char *pattern)
        {
          /* Remove the preceding backslash.  */
          *(s - 1) = *p;
-         backslash = FALSE;
-         changed = TRUE;
+         backslash = false;
+         changed = true;
        }
       else
        {
@@ -8785,7 +9209,7 @@ struct bfd_elf_version_expr *
 lang_new_vers_pattern (struct bfd_elf_version_expr *orig,
                       const char *new_name,
                       const char *lang,
-                      bfd_boolean literal_p)
+                      bool literal_p)
 {
   struct bfd_elf_version_expr *ret;
 
@@ -8793,12 +9217,12 @@ lang_new_vers_pattern (struct bfd_elf_version_expr *orig,
   ret->next = orig;
   ret->symver = 0;
   ret->script = 0;
-  ret->literal = TRUE;
+  ret->literal = true;
   ret->pattern = literal_p ? new_name : realsymbol (new_name);
   if (ret->pattern == NULL)
     {
       ret->pattern = new_name;
-      ret->literal = FALSE;
+      ret->literal = false;
     }
 
   if (lang == NULL || strcasecmp (lang, "C") == 0)
@@ -9089,7 +9513,7 @@ lang_do_version_exports_section (void)
       p = contents;
       while (p < contents + len)
        {
-         greg = lang_new_vers_pattern (greg, p, NULL, FALSE);
+         greg = lang_new_vers_pattern (greg, p, NULL, false);
          p = strchr (p, '\0') + 1;
        }
 
@@ -9099,15 +9523,21 @@ lang_do_version_exports_section (void)
       sec->flags |= SEC_EXCLUDE | SEC_KEEP;
     }
 
-  lreg = lang_new_vers_pattern (NULL, "*", NULL, FALSE);
+  lreg = lang_new_vers_pattern (NULL, "*", NULL, false);
   lang_register_vers_node (command_line.version_exports_section,
                           lang_new_vers_node (greg, lreg), NULL);
 }
 
-/* Evaluate LENGTH and ORIGIN parts of MEMORY spec */
+/* Evaluate LENGTH and ORIGIN parts of MEMORY spec.  This is initially
+   called with UPDATE_REGIONS_P set to FALSE, in this case no errors are
+   thrown, however, references to symbols in the origin and length fields
+   will be pushed into the symbol table, this allows PROVIDE statements to
+   then provide these symbols.  This function is called a second time with
+   UPDATE_REGIONS_P set to TRUE, this time the we update the actual region
+   data structures, and throw errors if missing symbols are encountered.  */
 
 static void
-lang_do_memory_regions (void)
+lang_do_memory_regions (bool update_regions_p)
 {
   lang_memory_region_type *r = lang_memory_region_list;
 
@@ -9116,24 +9546,30 @@ lang_do_memory_regions (void)
       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 (update_regions_p)
+            {
+              if (expld.result.valid_p)
+                {
+                  r->origin = expld.result.value;
+                  r->current = r->origin;
+                }
+              else
+                einfo (_("%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);
-       }
+          if (update_regions_p)
+            {
+              if (expld.result.valid_p)
+                r->length = expld.result.value;
+              else
+                einfo (_("%P: invalid length for memory region %s\n"),
+                       r->name_list.name);
+            }
+        }
     }
 }
 
@@ -9155,15 +9591,16 @@ lang_add_unique (const char *name)
 /* Append the list of dynamic symbols to the existing one.  */
 
 void
-lang_append_dynamic_list (struct bfd_elf_version_expr *dynamic)
+lang_append_dynamic_list (struct bfd_elf_dynamic_list **list_p,
+                         struct bfd_elf_version_expr *dynamic)
 {
-  if (link_info.dynamic_list)
+  if (*list_p)
     {
       struct bfd_elf_version_expr *tail;
       for (tail = dynamic; tail->next != NULL; tail = tail->next)
        ;
-      tail->next = link_info.dynamic_list->head.list;
-      link_info.dynamic_list->head.list = dynamic;
+      tail->next = (*list_p)->head.list;
+      (*list_p)->head.list = dynamic;
     }
   else
     {
@@ -9172,7 +9609,7 @@ lang_append_dynamic_list (struct bfd_elf_version_expr *dynamic)
       d = (struct bfd_elf_dynamic_list *) xcalloc (1, sizeof *d);
       d->head.list = dynamic;
       d->match = lang_vers_match;
-      link_info.dynamic_list = d;
+      *list_p = d;
     }
 }
 
@@ -9192,9 +9629,9 @@ lang_append_dynamic_list_cpp_typeinfo (void)
 
   for (i = 0; i < ARRAY_SIZE (symbols); i++)
     dynamic = lang_new_vers_pattern (dynamic, symbols [i], "C++",
-                                    FALSE);
+                                    false);
 
-  lang_append_dynamic_list (dynamic);
+  lang_append_dynamic_list (&link_info.dynamic_list, dynamic);
 }
 
 /* Append the list of C++ operator new and delete dynamic symbols to the
@@ -9213,9 +9650,9 @@ lang_append_dynamic_list_cpp_new (void)
 
   for (i = 0; i < ARRAY_SIZE (symbols); i++)
     dynamic = lang_new_vers_pattern (dynamic, symbols [i], "C++",
-                                    FALSE);
+                                    false);
 
-  lang_append_dynamic_list (dynamic);
+  lang_append_dynamic_list (&link_info.dynamic_list, dynamic);
 }
 
 /* Scan a space and/or comma separated string of features.  */
@@ -9239,7 +9676,7 @@ lang_ld_feature (char *str)
       sep = *q;
       *q = 0;
       if (strcasecmp (p, "SANE_EXPR") == 0)
-       config.sane_expr = TRUE;
+       config.sane_expr = true;
       else
        einfo (_("%X%P: unknown feature `%s'\n"), p);
       *q = sep;
@@ -9273,14 +9710,16 @@ lang_print_memory_usage (void)
   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);
+      if (r->length != 0)
+       {
+         double percent = used_length * 100.0 / r->length;
+         printf ("    %6.2f%%", percent);
+       }
+      printf ("\n");
     }
 }