Add markers for 2.38 branch
[binutils-gdb.git] / ld / emultempl / xtensaelf.em
index ffd4add1b71bc9e3ed63f340d7f52a623de12216..9d527daea95b1edb63894664ebf5d7969b1f4583 100644 (file)
@@ -1,12 +1,11 @@
 # This shell script emits a C file. -*- C -*-
-#   Copyright 2003, 2004
-#   Free Software Foundation, Inc.
+#   Copyright (C) 2003-2022 Free Software Foundation, Inc.
 #
-# This file is part of GLD, the Gnu Linker.
+# This file is part of the GNU Binutils.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
+# the Free Software Foundation; either version 3 of the License, or
 # (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful,
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
 #
 
-# This file is sourced from elf32.em, and defines extra xtensa-elf
+# This file is sourced from elf.em, and defines extra xtensa-elf
 # specific routines.
 #
-cat >>e${EMULATION_NAME}.c <<EOF
+fragment <<EOF
 
 #include <xtensa-config.h>
+#include "../bfd/elf-bfd.h"
+#include "elf/xtensa.h"
+#include "bfd.h"
+
+/* Provide default values for new configuration settings.  */
+#ifndef XTHAL_ABI_UNDEFINED
+#define XTHAL_ABI_UNDEFINED -1
+#endif
+
+#ifndef XTHAL_ABI_WINDOWED
+#define XTHAL_ABI_WINDOWED 0
+#endif
+
+#ifndef XTHAL_ABI_CALL0
+#define XTHAL_ABI_CALL0 1
+#endif
 
 static void xtensa_wild_group_interleave (lang_statement_union_type *);
 static void xtensa_colocate_output_literals (lang_statement_union_type *);
+static void xtensa_strip_inconsistent_linkonce_sections
+  (lang_statement_list_type *);
 
 
-/* Flag for the emulation-specific "--no-relax" option.  */
-static bfd_boolean disable_relaxation = FALSE;
-
 /* This number is irrelevant until we turn on use_literal_pages */
 static bfd_vma xtensa_page_power = 12; /* 4K pages.  */
 
 /* To force a page break between literals and text, change
    xtensa_use_literal_pages to "true".  */
-static bfd_boolean xtensa_use_literal_pages = FALSE;
+static bool xtensa_use_literal_pages = false;
 
 #define EXTRA_VALIDATION 0
 
+/* Xtensa ABI.
+   This option is defined in BDF library.  */
+extern int elf32xtensa_abi;
+
 
 static char *
 elf_xtensa_choose_target (int argc ATTRIBUTE_UNUSED,
@@ -54,23 +73,347 @@ elf_xtensa_choose_target (int argc ATTRIBUTE_UNUSED,
 }
 
 
-static bfd_boolean
-elf_xtensa_place_orphan (lang_input_statement_type *file, asection *s)
+static void
+elf_xtensa_before_parse (void)
+{
+  /* Just call the default hook.... Tensilica's version of this function
+     does some other work that isn't relevant here.  */
+  gld${EMULATION_NAME}_before_parse ();
+}
+
+
+static void
+remove_section (bfd *abfd, asection *os)
+{
+  asection **spp;
+  for (spp = &abfd->sections; *spp; spp = &(*spp)->next)
+    if (*spp == os)
+      {
+       *spp = os->next;
+       os->owner->section_count--;
+       break;
+      }
+}
+
+
+static bool
+replace_insn_sec_with_prop_sec (bfd *abfd,
+                               const char *insn_sec_name,
+                               const char *prop_sec_name,
+                               char **error_message)
+{
+  asection *insn_sec;
+  asection *prop_sec;
+  bfd_byte *prop_contents = NULL;
+  bfd_byte *insn_contents = NULL;
+  unsigned entry_count;
+  unsigned entry;
+  Elf_Internal_Shdr *rel_hdr;
+  Elf_Internal_Rela *internal_relocs = NULL;
+  unsigned reloc_count;
+
+  *error_message = "";
+  insn_sec = bfd_get_section_by_name (abfd, insn_sec_name);
+  if (insn_sec == NULL)
+    return true;
+  entry_count = insn_sec->size / 8;
+
+  prop_sec = bfd_get_section_by_name (abfd, prop_sec_name);
+  if (prop_sec != NULL && insn_sec != NULL)
+    {
+      *error_message = _("file already has property tables");
+      return false;
+    }
+
+  if (insn_sec->size != 0)
+    {
+      insn_contents = (bfd_byte *) xmalloc (insn_sec->size);
+      if (! bfd_get_section_contents (abfd, insn_sec, insn_contents,
+                                     (file_ptr) 0, insn_sec->size))
+       {
+         *error_message = _("failed to read section contents");
+         goto cleanup;
+       }
+    }
+
+  /* Create a property table section for it.  */
+  prop_sec_name = strdup (prop_sec_name);
+  prop_sec = bfd_make_section_with_flags
+    (abfd, prop_sec_name, bfd_section_flags (insn_sec));
+  if (prop_sec == NULL
+      || !bfd_set_section_alignment (prop_sec, 2))
+    {
+      *error_message = _("could not create new section");
+      goto cleanup;
+    }
+
+  prop_sec->size = entry_count * 12;
+  prop_contents = (bfd_byte *) bfd_zalloc (abfd, prop_sec->size);
+  elf_section_data (prop_sec)->this_hdr.contents = prop_contents;
+
+  /* The entry size and size must be set to allow the linker to compute
+     the number of relocations since it does not use reloc_count.  */
+  rel_hdr = _bfd_elf_single_rel_hdr (prop_sec);
+  rel_hdr->sh_entsize = sizeof (Elf32_External_Rela);
+  rel_hdr->sh_size = _bfd_elf_single_rel_hdr (insn_sec)->sh_size;
+
+  if (prop_contents == NULL && prop_sec->size != 0)
+    {
+      *error_message = _("could not allocate section contents");
+      goto cleanup;
+    }
+
+  /* Read the relocations.  */
+  reloc_count = insn_sec->reloc_count;
+  if (reloc_count != 0)
+    {
+      /* If there is already an internal_reloc, then save it so that the
+        read_relocs function freshly allocates a copy.  */
+      Elf_Internal_Rela *saved_relocs = elf_section_data (insn_sec)->relocs;
+
+      elf_section_data (insn_sec)->relocs = NULL;
+      internal_relocs =
+       _bfd_elf_link_read_relocs (abfd, insn_sec, NULL, NULL, false);
+      elf_section_data (insn_sec)->relocs = saved_relocs;
+
+      if (internal_relocs == NULL)
+       {
+         *error_message = _("out of memory");
+         goto cleanup;
+       }
+    }
+
+  /* Create a relocation section for the property section.  */
+  if (internal_relocs != NULL)
+    {
+      elf_section_data (prop_sec)->relocs = internal_relocs;
+      prop_sec->reloc_count = reloc_count;
+    }
+
+  /* Now copy each insn table entry to the prop table entry with
+     appropriate flags.  */
+  for (entry = 0; entry < entry_count; ++entry)
+    {
+      unsigned value;
+      unsigned flags = (XTENSA_PROP_INSN | XTENSA_PROP_NO_TRANSFORM
+                       | XTENSA_PROP_INSN_NO_REORDER);
+      value = bfd_get_32 (abfd, insn_contents + entry * 8 + 0);
+      bfd_put_32 (abfd, value, prop_contents + entry * 12 + 0);
+      value = bfd_get_32 (abfd, insn_contents + entry * 8 + 4);
+      bfd_put_32 (abfd, value, prop_contents + entry * 12 + 4);
+      bfd_put_32 (abfd, flags, prop_contents + entry * 12 + 8);
+    }
+
+  /* Now copy all of the relocations.  Change offsets for the
+     instruction table section to offsets in the property table
+     section.  */
+  if (internal_relocs)
+    {
+      unsigned i;
+
+      for (i = 0; i < reloc_count; i++)
+       {
+         Elf_Internal_Rela *rela;
+         unsigned r_offset;
+
+         rela = &internal_relocs[i];
+
+         /* If this relocation is to the .xt.insn section,
+            change the section number and the offset.  */
+         r_offset = rela->r_offset;
+         r_offset += 4 * (r_offset / 8);
+         rela->r_offset = r_offset;
+       }
+    }
+
+  remove_section (abfd, insn_sec);
+
+  free (insn_contents);
+
+  return true;
+
+ cleanup:
+  if (prop_sec && prop_sec->owner)
+    remove_section (abfd, prop_sec);
+  free (insn_contents);
+  free (internal_relocs);
+
+  return false;
+}
+
+
+#define PROP_SEC_BASE_NAME ".xt.prop"
+#define INSN_SEC_BASE_NAME ".xt.insn"
+#define LINKONCE_SEC_OLD_TEXT_BASE_NAME ".gnu.linkonce.x."
+
+
+static void
+replace_instruction_table_sections (bfd *abfd, asection *sec)
+{
+  char *message = "";
+  const char *insn_sec_name = NULL;
+  char *prop_sec_name = NULL;
+  char *owned_prop_sec_name = NULL;
+  const char *sec_name;
+
+  sec_name = bfd_section_name (sec);
+  if (strcmp (sec_name, INSN_SEC_BASE_NAME) == 0)
+    {
+      insn_sec_name = INSN_SEC_BASE_NAME;
+      prop_sec_name = PROP_SEC_BASE_NAME;
+    }
+  else if (startswith (sec_name, LINKONCE_SEC_OLD_TEXT_BASE_NAME))
+    {
+      insn_sec_name = sec_name;
+      owned_prop_sec_name = (char *) xmalloc (strlen (sec_name) + 20);
+      prop_sec_name = owned_prop_sec_name;
+      strcpy (prop_sec_name, ".gnu.linkonce.prop.t.");
+      strcat (prop_sec_name,
+             sec_name + strlen (LINKONCE_SEC_OLD_TEXT_BASE_NAME));
+    }
+  if (insn_sec_name != NULL)
+    {
+      if (! replace_insn_sec_with_prop_sec (abfd, insn_sec_name, prop_sec_name,
+                                           &message))
+       {
+         einfo (_("%P: warning: failed to convert %s table in %pB (%s); subsequent disassembly may be incomplete\n"),
+                insn_sec_name, abfd, message);
+       }
+    }
+  free (owned_prop_sec_name);
+}
+
+
+/* This is called after all input sections have been opened to convert
+   instruction tables (.xt.insn, gnu.linkonce.x.*) tables into property
+   tables (.xt.prop) before any section placement.  */
+
+static void
+elf_xtensa_after_open (void)
+{
+  /* First call the ELF version.  */
+  gld${EMULATION_NAME}_after_open ();
+
+  /* Now search the input files looking for instruction table sections.  */
+  LANG_FOR_EACH_INPUT_STATEMENT (f)
+    {
+      asection *sec = f->the_bfd->sections;
+      asection *next_sec;
+
+      /* Do not use bfd_map_over_sections here since we are removing
+        sections as we iterate.  */
+      while (sec != NULL)
+       {
+         next_sec = sec->next;
+         replace_instruction_table_sections (f->the_bfd, sec);
+         sec = next_sec;
+       }
+    }
+}
+
+
+static bool
+xt_config_info_unpack_and_check (char *data,
+                                bool *pmismatch,
+                                char **pmsg)
 {
-  /* Early exit for relocatable links.  */
-  if (link_info.relocatable)
-    return FALSE;
+  char *d, *key;
+  int num;
 
-  return gld${EMULATION_NAME}_place_orphan (file, s);
+  *pmismatch = false;
+
+  d = data;
+  while (*d)
+    {
+      key = d;
+      d = strchr (d, '=');
+      if (! d)
+       goto error;
+
+      /* Overwrite the equal sign.  */
+      *d++ = 0;
+
+      /* Check if this is a quoted string or a number.  */
+      if (*d == '"')
+       {
+         /* No string values are currently checked by LD;
+            just skip over the quotes.  */
+         d++;
+         d = strchr (d, '"');
+         if (! d)
+           goto error;
+         /* Overwrite the trailing quote.  */
+         *d++ = 0;
+       }
+      else
+       {
+         if (*d == 0)
+           goto error;
+         num = strtoul (d, &d, 0);
+
+         if (! strcmp (key, "ABI"))
+           {
+             if (elf32xtensa_abi == XTHAL_ABI_UNDEFINED)
+               {
+                 elf32xtensa_abi = num;
+               }
+             else if (num != elf32xtensa_abi)
+               {
+                 *pmismatch = true;
+                 *pmsg = "ABI does not match";
+               }
+           }
+         else if (! strcmp (key, "USE_ABSOLUTE_LITERALS"))
+           {
+             if (num != XSHAL_USE_ABSOLUTE_LITERALS)
+               {
+                 *pmismatch = true;
+                 *pmsg = "incompatible use of the Extended L32R option";
+               }
+           }
+       }
+
+      if (*d++ != '\n')
+       goto error;
+    }
+
+  return true;
+
+ error:
+  return false;
 }
 
 
+#define XTINFO_NAME "Xtensa_Info"
+#define XTINFO_NAMESZ 12
+#define XTINFO_TYPE 1
+
 static void
-elf_xtensa_before_parse (void)
+check_xtensa_info (bfd *abfd, asection *info_sec)
 {
-  /* Just call the default hook.... Tensilica's version of this function
-     does some other work that isn't relevant here.  */
-  gld${EMULATION_NAME}_before_parse ();
+  char *data, *errmsg = "";
+  bool mismatch;
+
+  data = xmalloc (info_sec->size);
+  if (! bfd_get_section_contents (abfd, info_sec, data, 0, info_sec->size))
+    einfo (_("%F%P: %pB: cannot read contents of section %pA\n"), abfd, info_sec);
+
+  if (info_sec->size > 24
+      && info_sec->size >= 24 + bfd_get_32 (abfd, data + 4)
+      && bfd_get_32 (abfd, data + 0) == XTINFO_NAMESZ
+      && bfd_get_32 (abfd, data + 8) == XTINFO_TYPE
+      && strcmp (data + 12, XTINFO_NAME) == 0
+      && xt_config_info_unpack_and_check (data + 12 + XTINFO_NAMESZ,
+                                         &mismatch, &errmsg))
+    {
+      if (mismatch)
+       einfo (_("%P: %pB: warning: incompatible Xtensa configuration (%s)\n"),
+              abfd, errmsg);
+    }
+  else
+    einfo (_("%P: %pB: warning: cannot parse .xtensa.info section\n"), abfd);
+
+  free (data);
 }
 
 
@@ -80,8 +423,9 @@ elf_xtensa_before_parse (void)
 static void
 elf_xtensa_before_allocation (void)
 {
-  bfd *in_bfd;
-  bfd_boolean is_big_endian = XCHAL_HAVE_BE;
+  asection *info_sec, *first_info_sec;
+  bfd *first_bfd;
+  bool is_big_endian = XCHAL_HAVE_BE;
 
   /* Check that the output endianness matches the Xtensa
      configuration.  The BFD library always includes both big and
@@ -90,43 +434,109 @@ elf_xtensa_before_allocation (void)
      required to process relocations) for the selected Xtensa
      configuration.  */
 
-  if (is_big_endian && output_bfd->xvec->byteorder == BFD_ENDIAN_LITTLE)
+  if (is_big_endian
+      && link_info.output_bfd->xvec->byteorder == BFD_ENDIAN_LITTLE)
     {
       einfo (_("%F%P: little endian output does not match "
               "Xtensa configuration\n"));
     }
-  if (!is_big_endian && output_bfd->xvec->byteorder == BFD_ENDIAN_BIG)
+  if (!is_big_endian
+      && link_info.output_bfd->xvec->byteorder == BFD_ENDIAN_BIG)
     {
       einfo (_("%F%P: big endian output does not match "
               "Xtensa configuration\n"));
     }
 
-  /* Check that the endianness for each input file matches the output.
-     The merge_private_bfd_data hook has already reported any mismatches
-     as errors, but those errors are not fatal.  At this point, we
-     cannot go any further if there are any mismatches.  */
+  /* Keep track of the first input .xtensa.info section, and as a fallback,
+     the first input bfd where a .xtensa.info section could be created.
+     After the input .xtensa.info has been checked, the contents of the
+     first one will be replaced with the output .xtensa.info table.  */
+  first_info_sec = 0;
+  first_bfd = 0;
+
+  LANG_FOR_EACH_INPUT_STATEMENT (f)
+    {
+      /* Check that the endianness for each input file matches the output.
+        The merge_private_bfd_data hook has already reported any mismatches
+        as errors, but those errors are not fatal.  At this point, we
+        cannot go any further if there are any mismatches.  */
+      if ((is_big_endian && f->the_bfd->xvec->byteorder == BFD_ENDIAN_LITTLE)
+         || (!is_big_endian && f->the_bfd->xvec->byteorder == BFD_ENDIAN_BIG))
+       einfo (_("%F%P: cross-endian linking for %pB not supported\n"),
+              f->the_bfd);
+
+      if (! first_bfd)
+       first_bfd = f->the_bfd;
+
+      info_sec = bfd_get_section_by_name (f->the_bfd, ".xtensa.info");
+      if (! info_sec)
+       continue;
+
+      if (! first_info_sec)
+       first_info_sec = info_sec;
+
+      /* Unpack the .xtensa.info section and check it against the current
+        Xtensa configuration.  */
+      check_xtensa_info (f->the_bfd, info_sec);
+
+      /* Do not include this copy of .xtensa.info in the output.  */
+      info_sec->size = 0;
+      info_sec->flags |= SEC_EXCLUDE;
+    }
 
-  for (in_bfd = link_info.input_bfds;
-       in_bfd != NULL;
-       in_bfd = in_bfd->link_next)
+  /* Reuse the first .xtensa.info input section to hold the output
+     .xtensa.info; or, if none were found, create a new section in the
+     first input bfd (assuming there is one).  */
+  info_sec = first_info_sec;
+  if (! info_sec && first_bfd)
     {
-      if ((is_big_endian && in_bfd->xvec->byteorder == BFD_ENDIAN_LITTLE)
-         || (!is_big_endian && in_bfd->xvec->byteorder == BFD_ENDIAN_BIG))
-       einfo (_("%F%P: cross-endian linking not supported\n"));
+      info_sec = bfd_make_section_with_flags (first_bfd, ".xtensa.info",
+                                             SEC_HAS_CONTENTS | SEC_READONLY);
+      if (! info_sec)
+       einfo (_("%F%P: failed to create .xtensa.info section\n"));
+    }
+  if (info_sec)
+    {
+      int xtensa_info_size;
+      char *data;
+
+      info_sec->flags &= ~SEC_EXCLUDE;
+      info_sec->flags |= SEC_IN_MEMORY;
+
+      data = xmalloc (100);
+      sprintf (data, "USE_ABSOLUTE_LITERALS=%d\nABI=%d\n",
+              XSHAL_USE_ABSOLUTE_LITERALS, xtensa_abi_choice ());
+      xtensa_info_size = strlen (data) + 1;
+
+      /* Add enough null terminators to pad to a word boundary.  */
+      do
+       data[xtensa_info_size++] = 0;
+      while ((xtensa_info_size & 3) != 0);
+
+      info_sec->size = 12 + XTINFO_NAMESZ + xtensa_info_size;
+      info_sec->contents = xmalloc (info_sec->size);
+      bfd_put_32 (info_sec->owner, XTINFO_NAMESZ, info_sec->contents + 0);
+      bfd_put_32 (info_sec->owner, xtensa_info_size, info_sec->contents + 4);
+      bfd_put_32 (info_sec->owner, XTINFO_TYPE, info_sec->contents + 8);
+      memcpy (info_sec->contents + 12, XTINFO_NAME, XTINFO_NAMESZ);
+      memcpy (info_sec->contents + 12 + XTINFO_NAMESZ, data, xtensa_info_size);
+      free (data);
     }
 
   /* Enable relaxation by default if the "--no-relax" option was not
      specified.  This is done here instead of in the before_parse hook
      because there is a check in main() to prohibit use of --relax and
      -r together and that combination should be allowed for Xtensa.  */
+  if (RELAXATION_DISABLED_BY_DEFAULT)
+    ENABLE_RELAXATION;
 
-  if (!disable_relaxation)
-    command_line.relax = TRUE;
+  xtensa_strip_inconsistent_linkonce_sections (stat_ptr);
 
   gld${EMULATION_NAME}_before_allocation ();
 
   xtensa_wild_group_interleave (stat_ptr->head);
-  if (command_line.relax)
+
+  if (RELAXATION_ENABLED)
     xtensa_colocate_output_literals (stat_ptr->head);
 
   /* TBD: We need to force the page alignments to here and only do
@@ -155,7 +565,7 @@ struct reloc_deps_section_t
 {
   reloc_deps_e *preds;
   reloc_deps_e *succs;
-  bfd_boolean is_only_literal;
+  bool is_only_literal;
 };
 
 
@@ -175,77 +585,22 @@ typedef void (*deps_callback_t) (asection *, /* src_sec */
                                 bfd_vma,    /* target_offset */
                                 void *);    /* closure */
 
-extern bfd_boolean xtensa_callback_required_dependence
+extern bool xtensa_callback_required_dependence
   (bfd *, asection *, struct bfd_link_info *, deps_callback_t, void *);
-static void xtensa_ldlang_clear_addresses
-  (lang_statement_union_type *);
-static bfd_boolean ld_local_file_relocations_fit
+static void xtensa_ldlang_clear_addresses (lang_statement_union_type *);
+static bool ld_local_file_relocations_fit
   (lang_statement_union_type *, const reloc_deps_graph *);
 static bfd_vma ld_assign_relative_paged_dot
   (bfd_vma, lang_statement_union_type *, const reloc_deps_graph *,
-   bfd_boolean);
+   bool);
 static bfd_vma ld_xtensa_insert_page_offsets
-  (bfd_vma, lang_statement_union_type *, reloc_deps_graph *, bfd_boolean);
+  (bfd_vma, lang_statement_union_type *, reloc_deps_graph *, bool);
 #if EXTRA_VALIDATION
-static size_t ld_count_children
-  (lang_statement_union_type *);
+static size_t ld_count_children (lang_statement_union_type *);
 #endif
 
 extern lang_statement_list_type constructor_list;
 
-/*  Begin verbatim code from ldlang.c:
-    the following are copied from ldlang.c because they are defined
-    there statically.  */
-
-static void
-lang_for_each_statement_worker (void (*func) (lang_statement_union_type *),
-                               lang_statement_union_type *s)
-{
-  for (; s != (lang_statement_union_type *) NULL; s = s->header.next)
-    {
-      func (s);
-
-      switch (s->header.type)
-       {
-       case lang_constructors_statement_enum:
-         lang_for_each_statement_worker (func, constructor_list.head);
-         break;
-       case lang_output_section_statement_enum:
-         lang_for_each_statement_worker
-           (func,
-            s->output_section_statement.children.head);
-         break;
-       case lang_wild_statement_enum:
-         lang_for_each_statement_worker
-           (func,
-            s->wild_statement.children.head);
-         break;
-       case lang_group_statement_enum:
-         lang_for_each_statement_worker (func,
-                                         s->group_statement.children.head);
-         break;
-       case lang_data_statement_enum:
-       case lang_reloc_statement_enum:
-       case lang_object_symbols_statement_enum:
-       case lang_output_statement_enum:
-       case lang_target_statement_enum:
-       case lang_input_section_enum:
-       case lang_input_statement_enum:
-       case lang_assignment_statement_enum:
-       case lang_padding_statement_enum:
-       case lang_address_statement_enum:
-       case lang_fill_statement_enum:
-         break;
-       default:
-         FAIL ();
-         break;
-       }
-    }
-}
-
-/* End of verbatim code from ldlang.c.  */
-
-
 static reloc_deps_section *
 xtensa_get_section_deps (const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
                         asection *sec)
@@ -253,8 +608,12 @@ xtensa_get_section_deps (const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
   /* We have a separate function for this so that
      we could in the future keep a completely independent
      structure that maps a section to its dependence edges.
-     For now, we place these in the sec->userdata field.  */
-  reloc_deps_section *sec_deps = sec->userdata;
+     For now, we place these in the sec->userdata field.
+     This doesn't clash with ldlang.c use of userdata for output
+     sections, and during map output for input sections, since the
+     xtensa use is only for input sections and only extant in
+     before_allocation.  */
+  reloc_deps_section *sec_deps = bfd_section_userdata (sec);
   return sec_deps;
 }
 
@@ -263,7 +622,7 @@ xtensa_set_section_deps (const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
                         asection *sec,
                         reloc_deps_section *deps_section)
 {
-  sec->userdata = deps_section;
+  bfd_set_section_userdata (sec, deps_section);
 }
 
 
@@ -289,8 +648,7 @@ xtensa_append_section_deps (reloc_deps_graph *deps, asection *sec)
        {
          new_sections[i] = deps->sections[i];
        }
-      if (deps->sections != NULL)
-       free (deps->sections);
+      free (deps->sections);
       deps->sections = new_sections;
       deps->size = new_size;
     }
@@ -328,14 +686,12 @@ free_reloc_deps_graph (reloc_deps_graph *deps)
        }
       xtensa_set_section_deps (deps, sec, NULL);
     }
-  if (deps->sections)
-    free (deps->sections);
-
+  free (deps->sections);
   free (deps);
 }
 
 
-static bfd_boolean
+static bool
 section_is_source (const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
                   lang_statement_union_type *s)
 {
@@ -343,7 +699,7 @@ section_is_source (const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
   const reloc_deps_section *sec_deps;
 
   if (s->header.type != lang_input_section_enum)
-    return FALSE;
+    return false;
   sec = s->input_section.section;
 
   sec_deps = xtensa_get_section_deps (deps, sec);
@@ -351,7 +707,7 @@ section_is_source (const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
 }
 
 
-static bfd_boolean
+static bool
 section_is_target (const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
                   lang_statement_union_type *s)
 {
@@ -359,14 +715,15 @@ section_is_target (const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
   const reloc_deps_section *sec_deps;
 
   if (s->header.type != lang_input_section_enum)
-    return FALSE;
+    return false;
   sec = s->input_section.section;
 
   sec_deps = xtensa_get_section_deps (deps, sec);
   return sec_deps && sec_deps->preds != NULL;
 }
 
-static bfd_boolean
+
+static bool
 section_is_source_or_target (const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
                             lang_statement_union_type *s)
 {
@@ -443,16 +800,16 @@ ld_xtensa_move_section_after (xtensa_ld_iter *to, xtensa_ld_iter *current)
 
 
 /* Can only be called with lang_statements that have lists.  Returns
-   false if the list is empty.  */
+   FALSE if the list is empty.  */
 
-static bfd_boolean
+static bool
 iter_stack_empty (xtensa_ld_iter_stack **stack_p)
 {
   return *stack_p == NULL;
 }
 
 
-static bfd_boolean
+static bool
 iter_stack_push (xtensa_ld_iter_stack **stack_p,
                 lang_statement_union_type *parent)
 {
@@ -472,12 +829,12 @@ iter_stack_push (xtensa_ld_iter_stack **stack_p,
       break;
     default:
       ASSERT (0);
-      return FALSE;
+      return false;
     }
 
   /* Empty. do not push.  */
   if (l->tail == &l->head)
-    return FALSE;
+    return false;
 
   stack = xmalloc (sizeof (xtensa_ld_iter_stack));
   memset (stack, 0, sizeof (xtensa_ld_iter_stack));
@@ -490,7 +847,7 @@ iter_stack_push (xtensa_ld_iter_stack **stack_p,
   if (*stack_p != NULL)
     (*stack_p)->prev = stack;
   *stack_p = stack;
-  return TRUE;
+  return true;
 }
 
 
@@ -592,8 +949,7 @@ iter_stack_create (xtensa_ld_iter_stack **stack_p,
 
 
 static void
-iter_stack_copy_current (xtensa_ld_iter_stack **stack_p,
-                        xtensa_ld_iter *front)
+iter_stack_copy_current (xtensa_ld_iter_stack **stack_p, xtensa_ld_iter *front)
 {
   *front = (*stack_p)->iterloc;
 }
@@ -612,26 +968,16 @@ xtensa_colocate_literals (reloc_deps_graph *deps,
 
   xtensa_ld_iter current; /* Location we are checking.  */
   xtensa_ld_iter *current_p = NULL;
-  bfd_boolean in_literals = FALSE;
+  bool in_literals = false;
 
   if (deps->count == 0)
     return;
 
-#if 0
-  ld_assign_relative_paged_dot (0x100000, statement, deps,
-                               xtensa_use_literal_pages);
-
-  if (!ld_local_file_relocations_fit (statement, deps))
-    fprintf (stderr, "initial relocation placement does not fit\n");
-
-  lang_for_each_statement_worker (xtensa_ldlang_clear_addresses, statement);
-#endif
-
   iter_stack_create (stack_p, statement);
 
   while (!iter_stack_empty (stack_p))
     {
-      bfd_boolean skip_increment = FALSE;
+      bool skip_increment = false;
       lang_statement_union_type *l = iter_stack_current (stack_p);
 
       switch (l->header.type)
@@ -639,7 +985,7 @@ xtensa_colocate_literals (reloc_deps_graph *deps,
        case lang_assignment_statement_enum:
          /* Any assignment statement should block reordering across it.  */
          front_p = NULL;
-         in_literals = FALSE;
+         in_literals = false;
          break;
 
        case lang_input_section_enum:
@@ -655,7 +1001,7 @@ xtensa_colocate_literals (reloc_deps_graph *deps,
            }
          else
            {
-             bfd_boolean is_target;
+             bool is_target;
              current_p = &current;
              iter_stack_copy_current (stack_p, current_p);
              is_target = (section_is_target (deps, l)
@@ -665,7 +1011,7 @@ xtensa_colocate_literals (reloc_deps_graph *deps,
                {
                  iter_stack_copy_current (stack_p, front_p);
                  if (!is_target)
-                   in_literals = FALSE;
+                   in_literals = false;
                }
              else
                {
@@ -692,7 +1038,7 @@ xtensa_colocate_literals (reloc_deps_graph *deps,
                          front_p->loc = &(*front_p->loc)->header.next;
 
                          /* Do not increment the current pointer.  */
-                         skip_increment = TRUE;
+                         skip_increment = true;
                        }
                    }
                }
@@ -758,17 +1104,15 @@ xtensa_move_dependencies_to_front (reloc_deps_graph *deps,
 }
 
 
-static bfd_boolean
-deps_has_sec_edge (const reloc_deps_graph *deps,
-                  asection *src,
-                  asection *tgt)
+static bool
+deps_has_sec_edge (const reloc_deps_graph *deps, asection *src, asection *tgt)
 {
   const reloc_deps_section *sec_deps;
   const reloc_deps_e *sec_deps_e;
 
   sec_deps = xtensa_get_section_deps (deps, src);
   if (sec_deps == NULL)
-    return FALSE;
+    return false;
 
   for (sec_deps_e = sec_deps->succs;
        sec_deps_e != NULL;
@@ -776,26 +1120,26 @@ deps_has_sec_edge (const reloc_deps_graph *deps,
     {
       ASSERT (sec_deps_e->src == src);
       if (sec_deps_e->tgt == tgt)
-       return TRUE;
+       return true;
     }
-  return FALSE;
+  return false;
 }
 
 
-static bfd_boolean
+static bool
 deps_has_edge (const reloc_deps_graph *deps,
               lang_statement_union_type *src,
               lang_statement_union_type *tgt)
 {
   if (!section_is_source (deps, src))
-    return FALSE;
+    return false;
   if (!section_is_target (deps, tgt))
-    return FALSE;
+    return false;
 
   if (src->header.type != lang_input_section_enum)
-    return FALSE;
+    return false;
   if (tgt->header.type != lang_input_section_enum)
-    return FALSE;
+    return false;
 
   return deps_has_sec_edge (deps, src->input_section.section,
                            tgt->input_section.section);
@@ -803,9 +1147,7 @@ deps_has_edge (const reloc_deps_graph *deps,
 
 
 static void
-add_deps_edge (reloc_deps_graph *deps,
-              asection *src_sec,
-              asection *tgt_sec)
+add_deps_edge (reloc_deps_graph *deps, asection *src_sec, asection *tgt_sec)
 {
   reloc_deps_section *src_sec_deps;
   reloc_deps_section *tgt_sec_deps;
@@ -891,11 +1233,15 @@ ld_build_required_section_dependence (lang_statement_union_type *s)
     {
       lang_statement_union_type *l = iter_stack_current (&stack);
 
+      if (l == NULL && link_info.non_contiguous_regions)
+       einfo (_("%F%P: Relaxation not supported with "
+                "--enable-non-contiguous-regions.\n"));
+
       if (l->header.type == lang_input_section_enum)
        {
          lang_input_section_type *input;
          input = &l->input_section;
-         xtensa_callback_required_dependence (input->ifile->the_bfd,
+         xtensa_callback_required_dependence (input->section->owner,
                                               input->section,
                                               &link_info,
                                               /* Use the same closure.  */
@@ -926,6 +1272,151 @@ ld_count_children (lang_statement_union_type *s)
 #endif /* EXTRA_VALIDATION */
 
 
+/* Check if a particular section is included in the link.  This will only
+   be true for one instance of a particular linkonce section.  */
+
+static bool input_section_found = false;
+static asection *input_section_target = NULL;
+
+static void
+input_section_linked_worker (lang_statement_union_type *statement)
+{
+  if ((statement->header.type == lang_input_section_enum
+       && (statement->input_section.section == input_section_target)))
+    input_section_found = true;
+}
+
+static bool
+input_section_linked (asection *sec)
+{
+  input_section_found = false;
+  input_section_target = sec;
+  lang_for_each_statement_worker (input_section_linked_worker, stat_ptr->head);
+  return input_section_found;
+}
+
+
+/* Strip out any linkonce property tables or XCC exception tables where the
+   associated linkonce text is from a different object file.  Normally,
+   a matching set of linkonce sections is taken from the same object file,
+   but sometimes the files are compiled differently so that some of the
+   linkonce sections are not present in all files.  Stripping the
+   inconsistent sections like this is not completely robust -- a much
+   better solution is to use comdat groups.  */
+
+static int linkonce_len = sizeof (".gnu.linkonce.") - 1;
+
+static bool
+is_inconsistent_linkonce_section (asection *sec)
+{
+  bfd *abfd = sec->owner;
+  const char *sec_name = bfd_section_name (sec);
+  const char *name;
+
+  if ((bfd_section_flags (sec) & SEC_LINK_ONCE) == 0
+      || strncmp (sec_name, ".gnu.linkonce.", linkonce_len) != 0)
+    return false;
+
+  /* Check if this is an Xtensa property section or an exception table
+     for Tensilica's XCC compiler.  */
+  name = sec_name + linkonce_len;
+  if (startswith (name, "prop."))
+    name = strchr (name + 5, '.') ? strchr (name + 5, '.') + 1 : name + 5;
+  else if (name[1] == '.'
+          && (name[0] == 'p' || name[0] == 'e' || name[0] == 'h'))
+    name += 2;
+  else
+    name = 0;
+
+  if (name)
+    {
+      char *dep_sec_name = xmalloc (strlen (sec_name) + 1);
+      asection *dep_sec;
+
+      /* Get the associated linkonce text section and check if it is
+        included in the link.  If not, this section is inconsistent
+        and should be stripped.  */
+      strcpy (dep_sec_name, ".gnu.linkonce.t.");
+      strcat (dep_sec_name, name);
+      dep_sec = bfd_get_section_by_name (abfd, dep_sec_name);
+      if (dep_sec == NULL || ! input_section_linked (dep_sec))
+       {
+         free (dep_sec_name);
+         return true;
+       }
+      free (dep_sec_name);
+    }
+
+  return false;
+}
+
+
+static void
+xtensa_strip_inconsistent_linkonce_sections (lang_statement_list_type *slist)
+{
+  lang_statement_union_type **s_p = &slist->head;
+  while (*s_p)
+    {
+      lang_statement_union_type *s = *s_p;
+      lang_statement_union_type *s_next = (*s_p)->header.next;
+
+      switch (s->header.type)
+       {
+       case lang_input_section_enum:
+         if (is_inconsistent_linkonce_section (s->input_section.section))
+           {
+             s->input_section.section->output_section = bfd_abs_section_ptr;
+             *s_p = s_next;
+             continue;
+           }
+         break;
+
+       case lang_constructors_statement_enum:
+         xtensa_strip_inconsistent_linkonce_sections (&constructor_list);
+         break;
+
+       case lang_output_section_statement_enum:
+         if (s->output_section_statement.children.head)
+           xtensa_strip_inconsistent_linkonce_sections
+             (&s->output_section_statement.children);
+         break;
+
+       case lang_wild_statement_enum:
+         xtensa_strip_inconsistent_linkonce_sections
+           (&s->wild_statement.children);
+         break;
+
+       case lang_group_statement_enum:
+         xtensa_strip_inconsistent_linkonce_sections
+           (&s->group_statement.children);
+         break;
+
+       case lang_data_statement_enum:
+       case lang_reloc_statement_enum:
+       case lang_object_symbols_statement_enum:
+       case lang_output_statement_enum:
+       case lang_target_statement_enum:
+       case lang_input_statement_enum:
+       case lang_assignment_statement_enum:
+       case lang_padding_statement_enum:
+       case lang_address_statement_enum:
+       case lang_fill_statement_enum:
+         break;
+
+       default:
+         FAIL ();
+         break;
+       }
+
+      s_p = &(*s_p)->header.next;
+    }
+
+  /* Reset the tail of the list, in case the last entry was removed.  */
+  if (s_p != slist->tail)
+    slist->tail = s_p;
+}
+
+
 static void
 xtensa_wild_group_interleave_callback (lang_statement_union_type *statement)
 {
@@ -937,20 +1428,20 @@ xtensa_wild_group_interleave_callback (lang_statement_union_type *statement)
       size_t old_child_count;
       size_t new_child_count;
 #endif
-      bfd_boolean no_reorder;
+      bool no_reorder;
 
       w = &statement->wild_statement;
 
-      no_reorder = FALSE;
+      no_reorder = false;
 
       /* If it has 0 or 1 section bound, then do not reorder.  */
       if (w->children.head == NULL
          || (w->children.head->header.type == lang_input_section_enum
              && w->children.head->header.next == NULL))
-       no_reorder = TRUE;
+       no_reorder = true;
 
       if (w->filenames_sorted)
-       no_reorder = TRUE;
+       no_reorder = true;
 
       /* Check for sorting in a section list wildcard spec as well.  */
       if (!no_reorder)
@@ -958,9 +1449,9 @@ xtensa_wild_group_interleave_callback (lang_statement_union_type *statement)
          struct wildcard_list *l;
          for (l = w->section_list; l != NULL; l = l->next)
            {
-             if (l->spec.sorted == TRUE)
+             if (l->spec.sorted == by_name)
                {
-                 no_reorder = TRUE;
+                 no_reorder = true;
                  break;
                }
            }
@@ -979,7 +1470,7 @@ xtensa_wild_group_interleave_callback (lang_statement_union_type *statement)
                  && ((strcmp (".init", l->spec.name) == 0)
                      || (strcmp (".fini", l->spec.name) == 0)))
                {
-                 no_reorder = TRUE;
+                 no_reorder = true;
                  break;
                }
            }
@@ -1027,8 +1518,7 @@ xtensa_wild_group_interleave (lang_statement_union_type *s)
 
 
 static void
-xtensa_layout_wild (const reloc_deps_graph *deps,
-                   lang_wild_statement_type *w)
+xtensa_layout_wild (const reloc_deps_graph *deps, lang_wild_statement_type *w)
 {
   /* If it does not fit initially, we need to do this step.  Move all
      of the wild literal sections to a new list, then move each of
@@ -1043,9 +1533,9 @@ xtensa_layout_wild (const reloc_deps_graph *deps,
   literal_wild.header.next = NULL;
   literal_wild.header.type = lang_wild_statement_enum;
   literal_wild.filename = NULL;
-  literal_wild.filenames_sorted = FALSE;
+  literal_wild.filenames_sorted = false;
   literal_wild.section_list = NULL;
-  literal_wild.keep_sections = FALSE;
+  literal_wild.keep_sections = false;
   literal_wild.children.head = NULL;
   literal_wild.children.tail = &literal_wild.children.head;
 
@@ -1089,7 +1579,7 @@ xtensa_layout_wild (const reloc_deps_graph *deps,
   while (literal_wild.children.head != NULL)
     {
       lang_statement_union_type *lit = literal_wild.children.head;
-      bfd_boolean placed = FALSE;
+      bool placed = false;
 
 #if EXTRA_VALIDATION
       ASSERT (ct2 > 0);
@@ -1111,7 +1601,7 @@ xtensa_layout_wild (const reloc_deps_graph *deps,
              /* Place it here.  */
              lit->header.next = *s_p;
              *s_p = lit;
-             placed = TRUE;
+             placed = true;
              break;
            }
        }
@@ -1134,7 +1624,6 @@ xtensa_layout_wild (const reloc_deps_graph *deps,
 static void
 xtensa_colocate_output_literals_callback (lang_statement_union_type *statement)
 {
-  lang_output_section_statement_type *os;
   reloc_deps_graph *deps;
   if (statement->header.type == lang_output_section_statement_enum)
     {
@@ -1154,9 +1643,7 @@ xtensa_colocate_output_literals_callback (lang_statement_union_type *statement)
       size_t old_child_count;
       size_t new_child_count;
 #endif
-      bfd_boolean no_reorder = FALSE;
-
-      os = &statement->output_section_statement;
+      bool no_reorder = false;
 
 #if EXTRA_VALIDATION
       old_child_count = ld_count_children (statement);
@@ -1222,7 +1709,7 @@ static bfd_vma
 ld_assign_relative_paged_dot (bfd_vma dot,
                              lang_statement_union_type *s,
                              const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
-                             bfd_boolean lit_align)
+                             bool lit_align)
 {
   /* Walk through all of the input statements in this wild statement
      assign dot to all of them.  */
@@ -1230,8 +1717,8 @@ ld_assign_relative_paged_dot (bfd_vma dot,
   xtensa_ld_iter_stack *stack = NULL;
   xtensa_ld_iter_stack **stack_p = &stack;
 
-  bfd_boolean first_section = FALSE;
-  bfd_boolean in_literals = FALSE;
+  bool first_section = false;
+  bool in_literals = false;
 
   for (iter_stack_create (stack_p, s);
        !iter_stack_empty (stack_p);
@@ -1245,21 +1732,21 @@ ld_assign_relative_paged_dot (bfd_vma dot,
          {
            asection *section = l->input_section.section;
            size_t align_pow = section->alignment_power;
-           bfd_boolean do_xtensa_alignment = FALSE;
+           bool do_xtensa_alignment = false;
 
            if (lit_align)
              {
-               bfd_boolean sec_is_target = section_is_target (deps, l);
-               bfd_boolean sec_is_source = section_is_source (deps, l);
+               bool sec_is_target = section_is_target (deps, l);
+               bool sec_is_source = section_is_source (deps, l);
 
                if (section->size != 0
                    && (first_section
                        || (in_literals && !sec_is_target)
                        || (!in_literals && sec_is_target)))
                  {
-                   do_xtensa_alignment = TRUE;
+                   do_xtensa_alignment = true;
                  }
-               first_section = FALSE;
+               first_section = false;
                if (section->size != 0)
                  in_literals = (sec_is_target && !sec_is_source);
              }
@@ -1286,7 +1773,7 @@ ld_assign_relative_paged_dot (bfd_vma dot,
 }
 
 
-static bfd_boolean
+static bool
 ld_local_file_relocations_fit (lang_statement_union_type *statement,
                               const reloc_deps_graph *deps ATTRIBUTE_UNUSED)
 {
@@ -1340,19 +1827,21 @@ ld_local_file_relocations_fit (lang_statement_union_type *statement,
                  bfd_vma target_addr = e->tgt->output_offset & ~3;
                  if (l32r_addr < target_addr)
                    {
+                     fflush (stdout);
                      fprintf (stderr, "Warning: "
                               "l32r target section before l32r\n");
-                     return FALSE;
+                     fflush (stderr);
+                     return false;
                    }
 
                  if (l32r_addr - target_addr > 256 * 1024 - align_penalty)
-                   return FALSE;
+                   return false;
                }
            }
        }
     }
 
-  return TRUE;
+  return true;
 }
 
 
@@ -1360,16 +1849,16 @@ static bfd_vma
 ld_xtensa_insert_page_offsets (bfd_vma dot,
                               lang_statement_union_type *s,
                               reloc_deps_graph *deps,
-                              bfd_boolean lit_align)
+                              bool lit_align)
 {
   xtensa_ld_iter_stack *stack = NULL;
   xtensa_ld_iter_stack **stack_p = &stack;
 
-  bfd_boolean first_section = FALSE;
-  bfd_boolean in_literals = FALSE;
+  bool first_section = false;
+  bool in_literals = false;
 
   if (!lit_align)
-    return FALSE;
+    return false;
 
   for (iter_stack_create (stack_p, s);
        !iter_stack_empty (stack_p);
@@ -1382,7 +1871,7 @@ ld_xtensa_insert_page_offsets (bfd_vma dot,
        case lang_input_section_enum:
          {
            asection *section = l->input_section.section;
-           bfd_boolean do_xtensa_alignment = FALSE;
+           bool do_xtensa_alignment = false;
 
            if (lit_align)
              {
@@ -1391,9 +1880,9 @@ ld_xtensa_insert_page_offsets (bfd_vma dot,
                        || (in_literals && !section_is_target (deps, l))
                        || (!in_literals && section_is_target (deps, l))))
                  {
-                   do_xtensa_alignment = TRUE;
+                   do_xtensa_alignment = true;
                  }
-               first_section = FALSE;
+               first_section = false;
                if (section->size != 0)
                  {
                    in_literals = (section_is_target (deps, l)
@@ -1408,25 +1897,22 @@ ld_xtensa_insert_page_offsets (bfd_vma dot,
                etree_type *name_op = exp_nameop (NAME, ".");
                etree_type *addend_op = exp_intop (1 << xtensa_page_power);
                etree_type *add_op = exp_binop ('+', name_op, addend_op);
-               etree_type *assign_op = exp_assop ('=', ".", add_op);
+               etree_type *assign_op = exp_assign (".", add_op, false);
 
                lang_assignment_statement_type *assign_stmt;
                lang_statement_union_type *assign_union;
                lang_statement_list_type tmplist;
-               lang_statement_list_type *old_stat_ptr = stat_ptr;
 
                /* There is hidden state in "lang_add_assignment".  It
                   appends the new assignment statement to the stat_ptr
                   list.  Thus, we swap it before and after the call.  */
 
-               tmplist.head = NULL;
-               tmplist.tail = &tmplist.head;
-
-               stat_ptr = &tmplist;
+               lang_list_init (&tmplist);
+               push_stat_ptr (&tmplist);
                /* Warning: side effect; statement appended to stat_ptr.  */
                assign_stmt = lang_add_assignment (assign_op);
                assign_union = (lang_statement_union_type *) assign_stmt;
-               stat_ptr = old_stat_ptr;
+               pop_stat_ptr ();
 
                assign_union->header.next = l;
                *(*stack_p)->iterloc.loc = assign_union;
@@ -1443,31 +1929,59 @@ ld_xtensa_insert_page_offsets (bfd_vma dot,
 
 EOF
 
-# Define some shell vars to insert bits of code into the standard elf
+# Define some shell vars to insert bits of code into the standard ELF
 # parse_args and list_options functions.
 #
 PARSE_AND_LIST_PROLOGUE='
-#define OPTION_NO_RELAX                        301
+#define OPTION_OPT_SIZEOPT              (300)
+#define OPTION_LITERAL_MOVEMENT                (OPTION_OPT_SIZEOPT + 1)
+#define OPTION_NO_LITERAL_MOVEMENT     (OPTION_LITERAL_MOVEMENT + 1)
+#define OPTION_ABI_WINDOWED            (OPTION_NO_LITERAL_MOVEMENT + 1)
+#define OPTION_ABI_CALL0               (OPTION_ABI_WINDOWED + 1)
+extern int elf32xtensa_size_opt;
+extern int elf32xtensa_no_literal_movement;
+extern int elf32xtensa_abi;
 '
 
 PARSE_AND_LIST_LONGOPTS='
-  { "no-relax", no_argument, NULL, OPTION_NO_RELAX},
+  { "size-opt", no_argument, NULL, OPTION_OPT_SIZEOPT},
+  { "literal-movement", no_argument, NULL, OPTION_LITERAL_MOVEMENT},
+  { "no-literal-movement", no_argument, NULL, OPTION_NO_LITERAL_MOVEMENT},
+  { "abi-windowed", no_argument, NULL, OPTION_ABI_WINDOWED},
+  { "abi-call0", no_argument, NULL, OPTION_ABI_CALL0},
 '
 
 PARSE_AND_LIST_OPTIONS='
-  fprintf (file, _("  --no-relax\t\tDo not relax branches or coalesce literals\n"));
+  fprintf (file, _("\
+  --size-opt                  When relaxing longcalls, prefer size\n\
+                                optimization over branch target alignment\n"));
+  fprintf (file, _("\
+  --abi-windowed              Choose windowed ABI for the output object\n"));
+  fprintf (file, _("\
+  --abi-call0                 Choose call0 ABI for the output object\n"));
 '
 
 PARSE_AND_LIST_ARGS_CASES='
-    case OPTION_NO_RELAX:
-      disable_relaxation = TRUE;
+    case OPTION_OPT_SIZEOPT:
+      elf32xtensa_size_opt = 1;
+      break;
+    case OPTION_LITERAL_MOVEMENT:
+      elf32xtensa_no_literal_movement = 0;
+      break;
+    case OPTION_NO_LITERAL_MOVEMENT:
+      elf32xtensa_no_literal_movement = 1;
+      break;
+    case OPTION_ABI_WINDOWED:
+      elf32xtensa_abi = XTHAL_ABI_WINDOWED;
+      break;
+    case OPTION_ABI_CALL0:
+      elf32xtensa_abi = XTHAL_ABI_CALL0;
       break;
 '
 
 # Replace some of the standard ELF functions with our own versions.
 #
 LDEMUL_BEFORE_PARSE=elf_xtensa_before_parse
+LDEMUL_AFTER_OPEN=elf_xtensa_after_open
 LDEMUL_CHOOSE_TARGET=elf_xtensa_choose_target
-LDEMUL_PLACE_ORPHAN=elf_xtensa_place_orphan
 LDEMUL_BEFORE_ALLOCATION=elf_xtensa_before_allocation
-