gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / bfd / elf32-xtensa.c
index 12788ec5fac7f339eb2b70b1267879dce724c6dd..05c4f8430a2046a9c48546c815f0eb2ebdb2f728 100644 (file)
@@ -1,6 +1,5 @@
 /* Xtensa-specific support for 32-bit ELF.
-   Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
-   Free Software Foundation, Inc.
+   Copyright (C) 2003-2020 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
 #include "libbfd.h"
 #include "elf-bfd.h"
 #include "elf/xtensa.h"
+#include "splay-tree.h"
 #include "xtensa-isa.h"
 #include "xtensa-config.h"
 
+/* All users of this file have bfd_octets_per_byte (abfd, sec) == 1.  */
+#define OCTETS_PER_BYTE(ABFD, SEC) 1
+
 #define XTENSA_NO_NOP_REMOVAL 0
 
 /* Local helper functions.  */
@@ -63,6 +66,8 @@ static bfd_boolean is_alt_relocation (int);
 static bfd_boolean is_operand_relocation (int);
 static bfd_size_type insn_decode_len
   (bfd_byte *, bfd_size_type, bfd_size_type);
+static int insn_num_slots
+  (bfd_byte *, bfd_size_type, bfd_size_type);
 static xtensa_opcode insn_decode_opcode
   (bfd_byte *, bfd_size_type, bfd_size_type, int);
 static bfd_boolean check_branch_target_aligned
@@ -110,7 +115,6 @@ static bfd_boolean xtensa_is_proptable_section (asection *);
 static int internal_reloc_compare (const void *, const void *);
 static int internal_reloc_matches (const void *, const void *);
 static asection *xtensa_get_property_section (asection *, const char *);
-extern asection *xtensa_make_property_section (asection *, const char *);
 static flagword xtensa_get_property_predef_flags (asection *);
 
 /* Other functions called directly by the linker.  */
@@ -155,6 +159,11 @@ static bfd_boolean relaxing_section = FALSE;
 
 int elf32xtensa_no_literal_movement = 1;
 
+/* Place property records for a section into individual property section
+   with xt.prop. prefix.  */
+
+bfd_boolean elf32xtensa_separate_props = FALSE;
+
 /* Rename one of the generic section flags to better document how it
    is used here.  */
 /* Whether relocations have been processed.  */
@@ -162,7 +171,7 @@ int elf32xtensa_no_literal_movement = 1;
 \f
 static reloc_howto_type elf_howto_table[] =
 {
-  HOWTO (R_XTENSA_NONE, 0, 0, 0, FALSE, 0, complain_overflow_dont,
+  HOWTO (R_XTENSA_NONE, 0, 3, 0, FALSE, 0, complain_overflow_dont,
         bfd_elf_xtensa_reloc, "R_XTENSA_NONE",
         FALSE, 0, 0, FALSE),
   HOWTO (R_XTENSA_32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
@@ -215,19 +224,19 @@ static reloc_howto_type elf_howto_table[] =
 
   /* GNU extension to record C++ vtable hierarchy.  */
   HOWTO (R_XTENSA_GNU_VTINHERIT, 0, 2, 0, FALSE, 0, complain_overflow_dont,
-         NULL, "R_XTENSA_GNU_VTINHERIT",
+        NULL, "R_XTENSA_GNU_VTINHERIT",
         FALSE, 0, 0, FALSE),
   /* GNU extension to record C++ vtable member usage.  */
   HOWTO (R_XTENSA_GNU_VTENTRY, 0, 2, 0, FALSE, 0, complain_overflow_dont,
-         _bfd_elf_rel_vtable_reloc_fn, "R_XTENSA_GNU_VTENTRY",
+        _bfd_elf_rel_vtable_reloc_fn, "R_XTENSA_GNU_VTENTRY",
         FALSE, 0, 0, FALSE),
 
   /* Relocations for supporting difference of symbols.  */
-  HOWTO (R_XTENSA_DIFF8, 0, 0, 8, FALSE, 0, complain_overflow_bitfield,
+  HOWTO (R_XTENSA_DIFF8, 0, 0, 8, FALSE, 0, complain_overflow_signed,
         bfd_elf_xtensa_reloc, "R_XTENSA_DIFF8", FALSE, 0, 0xff, FALSE),
-  HOWTO (R_XTENSA_DIFF16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+  HOWTO (R_XTENSA_DIFF16, 0, 1, 16, FALSE, 0, complain_overflow_signed,
         bfd_elf_xtensa_reloc, "R_XTENSA_DIFF16", FALSE, 0, 0xffff, FALSE),
-  HOWTO (R_XTENSA_DIFF32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+  HOWTO (R_XTENSA_DIFF32, 0, 2, 32, FALSE, 0, complain_overflow_signed,
         bfd_elf_xtensa_reloc, "R_XTENSA_DIFF32", FALSE, 0, 0xffffffff, FALSE),
 
   /* General immediate operand relocations.  */
@@ -316,6 +325,20 @@ static reloc_howto_type elf_howto_table[] =
   HOWTO (R_XTENSA_TLS_CALL, 0, 0, 0, FALSE, 0, complain_overflow_dont,
         bfd_elf_xtensa_reloc, "R_XTENSA_TLS_CALL",
         FALSE, 0, 0, FALSE),
+
+  HOWTO (R_XTENSA_PDIFF8, 0, 0, 8, FALSE, 0, complain_overflow_bitfield,
+        bfd_elf_xtensa_reloc, "R_XTENSA_PDIFF8", FALSE, 0, 0xff, FALSE),
+  HOWTO (R_XTENSA_PDIFF16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+        bfd_elf_xtensa_reloc, "R_XTENSA_PDIFF16", FALSE, 0, 0xffff, FALSE),
+  HOWTO (R_XTENSA_PDIFF32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+        bfd_elf_xtensa_reloc, "R_XTENSA_PDIFF32", FALSE, 0, 0xffffffff, FALSE),
+
+  HOWTO (R_XTENSA_NDIFF8, 0, 0, 8, FALSE, 0, complain_overflow_bitfield,
+        bfd_elf_xtensa_reloc, "R_XTENSA_NDIFF8", FALSE, 0, 0xff, FALSE),
+  HOWTO (R_XTENSA_NDIFF16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+        bfd_elf_xtensa_reloc, "R_XTENSA_NDIFF16", FALSE, 0, 0xffff, FALSE),
+  HOWTO (R_XTENSA_NDIFF32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+        bfd_elf_xtensa_reloc, "R_XTENSA_NDIFF32", FALSE, 0, 0xffffffff, FALSE),
 };
 
 #if DEBUG_GEN_RELOC
@@ -355,6 +378,30 @@ elf_xtensa_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
       TRACE ("BFD_RELOC_XTENSA_DIFF32");
       return &elf_howto_table[(unsigned) R_XTENSA_DIFF32 ];
 
+    case BFD_RELOC_XTENSA_PDIFF8:
+      TRACE ("BFD_RELOC_XTENSA_PDIFF8");
+      return &elf_howto_table[(unsigned) R_XTENSA_PDIFF8 ];
+
+    case BFD_RELOC_XTENSA_PDIFF16:
+      TRACE ("BFD_RELOC_XTENSA_PDIFF16");
+      return &elf_howto_table[(unsigned) R_XTENSA_PDIFF16 ];
+
+    case BFD_RELOC_XTENSA_PDIFF32:
+      TRACE ("BFD_RELOC_XTENSA_PDIFF32");
+      return &elf_howto_table[(unsigned) R_XTENSA_PDIFF32 ];
+
+    case BFD_RELOC_XTENSA_NDIFF8:
+      TRACE ("BFD_RELOC_XTENSA_NDIFF8");
+      return &elf_howto_table[(unsigned) R_XTENSA_NDIFF8 ];
+
+    case BFD_RELOC_XTENSA_NDIFF16:
+      TRACE ("BFD_RELOC_XTENSA_NDIFF16");
+      return &elf_howto_table[(unsigned) R_XTENSA_NDIFF16 ];
+
+    case BFD_RELOC_XTENSA_NDIFF32:
+      TRACE ("BFD_RELOC_XTENSA_NDIFF32");
+      return &elf_howto_table[(unsigned) R_XTENSA_NDIFF32 ];
+
     case BFD_RELOC_XTENSA_RTLD:
       TRACE ("BFD_RELOC_XTENSA_RTLD");
       return &elf_howto_table[(unsigned) R_XTENSA_RTLD ];
@@ -451,6 +498,9 @@ elf_xtensa_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
       break;
     }
 
+  /* xgettext:c-format */
+  _bfd_error_handler (_("%pB: unsupported relocation type %#x"), abfd, (int) code);
+  bfd_set_error (bfd_error_bad_value);
   TRACE ("Unknown");
   return NULL;
 }
@@ -473,15 +523,23 @@ elf_xtensa_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
 /* Given an ELF "rela" relocation, find the corresponding howto and record
    it in the BFD internal arelent representation of the relocation.  */
 
-static void
-elf_xtensa_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED,
+static bfd_boolean
+elf_xtensa_info_to_howto_rela (bfd *abfd,
                               arelent *cache_ptr,
                               Elf_Internal_Rela *dst)
 {
   unsigned int r_type = ELF32_R_TYPE (dst->r_info);
 
-  BFD_ASSERT (r_type < (unsigned int) R_XTENSA_max);
+  if (r_type >= (unsigned int) R_XTENSA_max)
+    {
+      /* xgettext:c-format */
+      _bfd_error_handler (_("%pB: unsupported relocation type %#x"),
+                         abfd, r_type);
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
+    }
   cache_ptr->howto = &elf_howto_table[r_type];
+  return TRUE;
 }
 
 \f
@@ -515,24 +573,42 @@ elf_xtensa_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED,
    invoked.  Note: the 32-byte frame size used here cannot be changed
    without a corresponding change in the runtime linker.  */
 
-static const bfd_byte elf_xtensa_be_plt_entry[PLT_ENTRY_SIZE] =
+static const bfd_byte elf_xtensa_be_plt_entry[][PLT_ENTRY_SIZE] =
 {
-  0x6c, 0x10, 0x04,    /* entry sp, 32 */
-  0x18, 0x00, 0x00,    /* l32r  a8, [got entry for rtld's resolver] */
-  0x1a, 0x00, 0x00,    /* l32r  a10, [got entry for rtld's link map] */
-  0x1b, 0x00, 0x00,    /* l32r  a11, [literal for reloc index] */
-  0x0a, 0x80, 0x00,    /* jx    a8 */
-  0                    /* unused */
+    {
+      0x6c, 0x10, 0x04,        /* entry sp, 32 */
+      0x18, 0x00, 0x00,        /* l32r  a8, [got entry for rtld's resolver] */
+      0x1a, 0x00, 0x00,        /* l32r  a10, [got entry for rtld's link map] */
+      0x1b, 0x00, 0x00,        /* l32r  a11, [literal for reloc index] */
+      0x0a, 0x80, 0x00,        /* jx    a8 */
+      0                        /* unused */
+    },
+    {
+      0x18, 0x00, 0x00,        /* l32r  a8, [got entry for rtld's resolver] */
+      0x1a, 0x00, 0x00,        /* l32r  a10, [got entry for rtld's link map] */
+      0x1b, 0x00, 0x00,        /* l32r  a11, [literal for reloc index] */
+      0x0a, 0x80, 0x00,        /* jx    a8 */
+      0                        /* unused */
+    }
 };
 
-static const bfd_byte elf_xtensa_le_plt_entry[PLT_ENTRY_SIZE] =
+static const bfd_byte elf_xtensa_le_plt_entry[][PLT_ENTRY_SIZE] =
 {
-  0x36, 0x41, 0x00,    /* entry sp, 32 */
-  0x81, 0x00, 0x00,    /* l32r  a8, [got entry for rtld's resolver] */
-  0xa1, 0x00, 0x00,    /* l32r  a10, [got entry for rtld's link map] */
-  0xb1, 0x00, 0x00,    /* l32r  a11, [literal for reloc index] */
-  0xa0, 0x08, 0x00,    /* jx    a8 */
-  0                    /* unused */
+    {
+      0x36, 0x41, 0x00,        /* entry sp, 32 */
+      0x81, 0x00, 0x00,        /* l32r  a8, [got entry for rtld's resolver] */
+      0xa1, 0x00, 0x00,        /* l32r  a10, [got entry for rtld's link map] */
+      0xb1, 0x00, 0x00,        /* l32r  a11, [literal for reloc index] */
+      0xa0, 0x08, 0x00,        /* jx    a8 */
+      0                        /* unused */
+    },
+    {
+      0x81, 0x00, 0x00,        /* l32r  a8, [got entry for rtld's resolver] */
+      0xa1, 0x00, 0x00,        /* l32r  a10, [got entry for rtld's link map] */
+      0xb1, 0x00, 0x00,        /* l32r  a11, [literal for reloc index] */
+      0xa0, 0x08, 0x00,        /* jx    a8 */
+      0                        /* unused */
+    }
 };
 
 /* The size of the thread control block.  */
@@ -592,11 +668,6 @@ struct elf_xtensa_link_hash_table
   struct elf_link_hash_table elf;
 
   /* Short-cuts to get to dynamic linker sections.  */
-  asection *sgot;
-  asection *sgotplt;
-  asection *srelgot;
-  asection *splt;
-  asection *srelplt;
   asection *sgotloc;
   asection *spltlittbl;
 
@@ -653,9 +724,9 @@ elf_xtensa_link_hash_table_create (bfd *abfd)
 {
   struct elf_link_hash_entry *tlsbase;
   struct elf_xtensa_link_hash_table *ret;
-  bfd_size_type amt = sizeof (struct elf_xtensa_link_hash_table);
+  size_t amt = sizeof (struct elf_xtensa_link_hash_table);
 
-  ret = bfd_malloc (amt);
+  ret = bfd_zmalloc (amt);
   if (ret == NULL)
     return NULL;
 
@@ -668,16 +739,6 @@ elf_xtensa_link_hash_table_create (bfd *abfd)
       return NULL;
     }
 
-  ret->sgot = NULL;
-  ret->sgotplt = NULL;
-  ret->srelgot = NULL;
-  ret->splt = NULL;
-  ret->srelplt = NULL;
-  ret->sgotloc = NULL;
-  ret->spltlittbl = NULL;
-
-  ret->plt_reloc_count = 0;
-
   /* Create a hash entry for "_TLS_MODULE_BASE_" to speed up checking
      for it later.  */
   tlsbase = elf_link_hash_lookup (&ret->elf, "_TLS_MODULE_BASE_",
@@ -752,7 +813,7 @@ property_table_compare (const void *ap, const void *bp)
              != GET_XTENSA_PROP_ALIGNMENT (b->flags)))
        return (GET_XTENSA_PROP_ALIGNMENT (a->flags)
                - GET_XTENSA_PROP_ALIGNMENT (b->flags));
-      
+
       if ((a->flags & XTENSA_PROP_UNREACHABLE)
          != (b->flags & XTENSA_PROP_UNREACHABLE))
        return ((b->flags & XTENSA_PROP_UNREACHABLE)
@@ -784,7 +845,7 @@ property_table_matches (const void *ap, const void *bp)
    section.  Sets TABLE_P and returns the number of entries.  On
    error, returns a negative value.  */
 
-static int
+int
 xtensa_read_table_entries (bfd *abfd,
                           asection *section,
                           property_table_entry **table_p,
@@ -814,7 +875,7 @@ xtensa_read_table_entries (bfd *abfd,
   if (table_section)
     table_size = table_section->size;
 
-  if (table_size == 0) 
+  if (table_size == 0)
     {
       *table_p = NULL;
       return 0;
@@ -849,7 +910,7 @@ xtensa_read_table_entries (bfd *abfd,
   section_limit = bfd_get_section_limit (abfd, section);
   rel_end = internal_relocs + table_section->reloc_count;
 
-  for (off = 0; off < table_size; off += table_entry_size) 
+  for (off = 0; off < table_size; off += table_entry_size)
     {
       bfd_vma address = bfd_get_32 (abfd, table_data + off);
 
@@ -905,7 +966,7 @@ xtensa_read_table_entries (bfd *abfd,
             property_table_compare);
 
       /* Check that the table contents are valid.  Problems may occur,
-         for example, if an unrelocated object file is stripped.  */
+        for example, if an unrelocated object file is stripped.  */
       for (blk = 1; blk < block_count; blk++)
        {
          /* The only circumstance where two entries may legitimately
@@ -915,8 +976,9 @@ xtensa_read_table_entries (bfd *abfd,
          if (blocks[blk - 1].address == blocks[blk].address &&
              blocks[blk - 1].size != 0)
            {
-             (*_bfd_error_handler) (_("%B(%A): invalid property table"),
-                                    abfd, section);
+             /* xgettext:c-format */
+             _bfd_error_handler (_("%pB(%pA): invalid property table"),
+                                 abfd, section);
              bfd_set_error (bfd_error_bad_value);
              free (blocks);
              return -1;
@@ -977,7 +1039,7 @@ elf_xtensa_check_relocs (bfd *abfd,
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
 
-  if (info->relocatable || (sec->flags & SEC_ALLOC) == 0)
+  if (bfd_link_relocatable (info) || (sec->flags & SEC_ALLOC) == 0)
     return TRUE;
 
   BFD_ASSERT (is_xtensa_elf (abfd));
@@ -993,7 +1055,7 @@ elf_xtensa_check_relocs (bfd *abfd,
   for (rel = relocs; rel < rel_end; rel++)
     {
       unsigned int r_type;
-      unsigned long r_symndx;
+      unsigned r_symndx;
       struct elf_link_hash_entry *h = NULL;
       struct elf_xtensa_link_hash_entry *eh;
       int tls_type, old_tls_type;
@@ -1006,8 +1068,9 @@ elf_xtensa_check_relocs (bfd *abfd,
 
       if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
        {
-         (*_bfd_error_handler) (_("%B: bad symbol index: %d"),
-                                abfd, r_symndx);
+         /* xgettext:c-format */
+         _bfd_error_handler (_("%pB: bad symbol index: %d"),
+                             abfd, r_symndx);
          return FALSE;
        }
 
@@ -1023,7 +1086,7 @@ elf_xtensa_check_relocs (bfd *abfd,
       switch (r_type)
        {
        case R_XTENSA_TLSDESC_FN:
-         if (info->shared)
+         if (bfd_link_pic (info))
            {
              tls_type = GOT_TLS_GD;
              is_got = TRUE;
@@ -1034,7 +1097,7 @@ elf_xtensa_check_relocs (bfd *abfd,
          break;
 
        case R_XTENSA_TLSDESC_ARG:
-         if (info->shared)
+         if (bfd_link_pic (info))
            {
              tls_type = GOT_TLS_GD;
              is_got = TRUE;
@@ -1048,7 +1111,7 @@ elf_xtensa_check_relocs (bfd *abfd,
          break;
 
        case R_XTENSA_TLS_DTPOFF:
-         if (info->shared)
+         if (bfd_link_pic (info))
            tls_type = GOT_TLS_GD;
          else
            tls_type = GOT_TLS_IE;
@@ -1056,9 +1119,9 @@ elf_xtensa_check_relocs (bfd *abfd,
 
        case R_XTENSA_TLS_TPOFF:
          tls_type = GOT_TLS_IE;
-         if (info->shared)
+         if (bfd_link_pic (info))
            info->flags |= DF_STATIC_TLS;
-         if (info->shared || h)
+         if (bfd_link_pic (info) || h)
            is_got = TRUE;
          break;
 
@@ -1082,9 +1145,7 @@ elf_xtensa_check_relocs (bfd *abfd,
        case R_XTENSA_GNU_VTENTRY:
          /* This relocation describes which C++ vtable entries are actually
             used.  Record for later use during GC.  */
-         BFD_ASSERT (h != NULL);
-         if (h != NULL
-             && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+         if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
            return FALSE;
          continue;
 
@@ -1178,8 +1239,9 @@ elf_xtensa_check_relocs (bfd *abfd,
            tls_type |= old_tls_type;
          else
            {
-             (*_bfd_error_handler)
-               (_("%B: `%s' accessed both as normal and thread local symbol"),
+             _bfd_error_handler
+               /* xgettext:c-format */
+               (_("%pB: `%s' accessed both as normal and thread local symbol"),
                 abfd,
                 h ? h->root.root.string : "<local>");
              return FALSE;
@@ -1201,19 +1263,19 @@ elf_xtensa_check_relocs (bfd *abfd,
 
 static void
 elf_xtensa_make_sym_local (struct bfd_link_info *info,
-                           struct elf_link_hash_entry *h)
+                          struct elf_link_hash_entry *h)
 {
-  if (info->shared)
+  if (bfd_link_pic (info))
     {
       if (h->plt.refcount > 0)
-        {
+       {
          /* For shared objects, there's no need for PLT entries for local
             symbols (use RELATIVE relocs instead of JMP_SLOT relocs).  */
-          if (h->got.refcount < 0)
-            h->got.refcount = 0;
-          h->got.refcount += h->plt.refcount;
-          h->plt.refcount = 0;
-        }
+         if (h->got.refcount < 0)
+           h->got.refcount = 0;
+         h->got.refcount += h->plt.refcount;
+         h->plt.refcount = 0;
+       }
     }
   else
     {
@@ -1226,8 +1288,8 @@ elf_xtensa_make_sym_local (struct bfd_link_info *info,
 
 static void
 elf_xtensa_hide_symbol (struct bfd_link_info *info,
-                        struct elf_link_hash_entry *h,
-                        bfd_boolean force_local)
+                       struct elf_link_hash_entry *h,
+                       bfd_boolean force_local)
 {
   /* For a shared link, move the plt refcount to the got refcount to leave
      space for RELATIVE relocs.  */
@@ -1272,133 +1334,6 @@ elf_xtensa_gc_mark_hook (asection *sec,
 }
 
 
-/* Update the GOT & PLT entry reference counts
-   for the section being removed.  */
-
-static bfd_boolean
-elf_xtensa_gc_sweep_hook (bfd *abfd,
-                         struct bfd_link_info *info,
-                         asection *sec,
-                         const Elf_Internal_Rela *relocs)
-{
-  Elf_Internal_Shdr *symtab_hdr;
-  struct elf_link_hash_entry **sym_hashes;
-  const Elf_Internal_Rela *rel, *relend;
-  struct elf_xtensa_link_hash_table *htab;
-
-  htab = elf_xtensa_hash_table (info);
-  if (htab == NULL)
-    return FALSE;
-
-  if (info->relocatable)
-    return TRUE;
-
-  if ((sec->flags & SEC_ALLOC) == 0)
-    return TRUE;
-
-  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-  sym_hashes = elf_sym_hashes (abfd);
-
-  relend = relocs + sec->reloc_count;
-  for (rel = relocs; rel < relend; rel++)
-    {
-      unsigned long r_symndx;
-      unsigned int r_type;
-      struct elf_link_hash_entry *h = NULL;
-      struct elf_xtensa_link_hash_entry *eh;
-      bfd_boolean is_got = FALSE;
-      bfd_boolean is_plt = FALSE;
-      bfd_boolean is_tlsfunc = FALSE;
-
-      r_symndx = ELF32_R_SYM (rel->r_info);
-      if (r_symndx >= symtab_hdr->sh_info)
-       {
-         h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-         while (h->root.type == bfd_link_hash_indirect
-                || h->root.type == bfd_link_hash_warning)
-           h = (struct elf_link_hash_entry *) h->root.u.i.link;
-       }
-      eh = elf_xtensa_hash_entry (h);
-
-      r_type = ELF32_R_TYPE (rel->r_info);
-      switch (r_type)
-       {
-       case R_XTENSA_TLSDESC_FN:
-         if (info->shared)
-           {
-             is_got = TRUE;
-             is_tlsfunc = TRUE;
-           }
-         break;
-
-       case R_XTENSA_TLSDESC_ARG:
-         if (info->shared)
-           is_got = TRUE;
-         else
-           {
-             if (h && elf_xtensa_hash_entry (h) != htab->tlsbase)
-               is_got = TRUE;
-           }
-         break;
-
-       case R_XTENSA_TLS_TPOFF:
-         if (info->shared || h)
-           is_got = TRUE;
-         break;
-
-       case R_XTENSA_32:
-         is_got = TRUE;
-         break;
-
-       case R_XTENSA_PLT:
-         is_plt = TRUE;
-         break;
-
-       default:
-         continue;
-       }
-
-      if (h)
-       {
-         if (is_plt)
-           {
-             if (h->plt.refcount > 0)
-               h->plt.refcount--;
-           }
-         else if (is_got)
-           {
-             if (h->got.refcount > 0)
-               h->got.refcount--;
-           }
-         if (is_tlsfunc)
-           {
-             if (eh->tlsfunc_refcount > 0)
-               eh->tlsfunc_refcount--;
-           }
-       }
-      else
-       {
-         if (is_got || is_plt)
-           {
-             bfd_signed_vma *got_refcount
-               = &elf_local_got_refcounts (abfd) [r_symndx];
-             if (*got_refcount > 0)
-               *got_refcount -= 1;
-           }
-         if (is_tlsfunc)
-           {
-             bfd_signed_vma *tlsfunc_refcount
-               = &elf_xtensa_local_tlsfunc_refcounts (abfd) [r_symndx];
-             if (*tlsfunc_refcount > 0)
-               *tlsfunc_refcount -= 1;
-           }
-       }
-    }
-
-  return TRUE;
-}
-
-
 /* Create all the dynamic sections.  */
 
 static bfd_boolean
@@ -1414,11 +1349,6 @@ elf_xtensa_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
   /* First do all the standard stuff.  */
   if (! _bfd_elf_create_dynamic_sections (dynobj, info))
     return FALSE;
-  htab->splt = bfd_get_section_by_name (dynobj, ".plt");
-  htab->srelplt = bfd_get_section_by_name (dynobj, ".rela.plt");
-  htab->sgot = bfd_get_section_by_name (dynobj, ".got");
-  htab->sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
-  htab->srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
 
   /* Create any extra PLT sections in case check_relocs has already
      been called on all the non-dynamic input files.  */
@@ -1430,21 +1360,22 @@ elf_xtensa_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
   flags = noalloc_flags | SEC_ALLOC | SEC_LOAD;
 
   /* Mark the ".got.plt" section READONLY.  */
-  if (htab->sgotplt == NULL
-      || ! bfd_set_section_flags (dynobj, htab->sgotplt, flags))
+  if (htab->elf.sgotplt == NULL
+      || !bfd_set_section_flags (htab->elf.sgotplt, flags))
     return FALSE;
 
   /* Create ".got.loc" (literal tables for use by dynamic linker).  */
-  htab->sgotloc = bfd_make_section_with_flags (dynobj, ".got.loc", flags);
+  htab->sgotloc = bfd_make_section_anyway_with_flags (dynobj, ".got.loc",
+                                                     flags);
   if (htab->sgotloc == NULL
-      || ! bfd_set_section_alignment (dynobj, htab->sgotloc, 2))
+      || !bfd_set_section_alignment (htab->sgotloc, 2))
     return FALSE;
 
   /* Create ".xt.lit.plt" (literal table for ".got.plt*").  */
-  htab->spltlittbl = bfd_make_section_with_flags (dynobj, ".xt.lit.plt",
-                                                 noalloc_flags);
+  htab->spltlittbl = bfd_make_section_anyway_with_flags (dynobj, ".xt.lit.plt",
+                                                        noalloc_flags);
   if (htab->spltlittbl == NULL
-      || ! bfd_set_section_alignment (dynobj, htab->spltlittbl, 2))
+      || !bfd_set_section_alignment (htab->spltlittbl, 2))
     return FALSE;
 
   return TRUE;
@@ -1474,16 +1405,16 @@ add_extra_plt_sections (struct bfd_link_info *info, int count)
 
       sname = (char *) bfd_malloc (10);
       sprintf (sname, ".plt.%u", chunk);
-      s = bfd_make_section_with_flags (dynobj, sname, flags | SEC_CODE);
+      s = bfd_make_section_anyway_with_flags (dynobj, sname, flags | SEC_CODE);
       if (s == NULL
-         || ! bfd_set_section_alignment (dynobj, s, 2))
+         || !bfd_set_section_alignment (s, 2))
        return FALSE;
 
       sname = (char *) bfd_malloc (14);
       sprintf (sname, ".got.plt.%u", chunk);
-      s = bfd_make_section_with_flags (dynobj, sname, flags);
+      s = bfd_make_section_anyway_with_flags (dynobj, sname, flags);
       if (s == NULL
-         || ! bfd_set_section_alignment (dynobj, s, 2))
+         || !bfd_set_section_alignment (s, 2))
        return FALSE;
     }
 
@@ -1504,12 +1435,12 @@ elf_xtensa_adjust_dynamic_symbol (struct bfd_link_info *info ATTRIBUTE_UNUSED,
   /* If this is a weak symbol, and there is a real definition, the
      processor independent code will have arranged for us to see the
      real definition first, and we can just use the same value.  */
-  if (h->u.weakdef)
+  if (h->is_weakalias)
     {
-      BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined
-                 || h->u.weakdef->root.type == bfd_link_hash_defweak);
-      h->root.u.def.section = h->u.weakdef->root.u.def.section;
-      h->root.u.def.value = h->u.weakdef->root.u.def.value;
+      struct elf_link_hash_entry *def = weakdef (h);
+      BFD_ASSERT (def->root.type == bfd_link_hash_defined);
+      h->root.u.def.section = def->root.u.def.section;
+      h->root.u.def.value = def->root.u.def.value;
       return TRUE;
     }
 
@@ -1531,9 +1462,6 @@ elf_xtensa_allocate_dynrelocs (struct elf_link_hash_entry *h, void *arg)
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
 
-  if (h->root.type == bfd_link_hash_warning)
-    h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
   info = (struct bfd_link_info *) arg;
   htab = elf_xtensa_hash_table (info);
   if (htab == NULL)
@@ -1550,11 +1478,15 @@ elf_xtensa_allocate_dynrelocs (struct elf_link_hash_entry *h, void *arg)
   if (! elf_xtensa_dynamic_symbol_p (h, info))
     elf_xtensa_make_sym_local (info, h);
 
+  if (! elf_xtensa_dynamic_symbol_p (h, info)
+      && h->root.type == bfd_link_hash_undefweak)
+    return TRUE;
+
   if (h->plt.refcount > 0)
-    htab->srelplt->size += (h->plt.refcount * sizeof (Elf32_External_Rela));
+    htab->elf.srelplt->size += (h->plt.refcount * sizeof (Elf32_External_Rela));
 
   if (h->got.refcount > 0)
-    htab->srelgot->size += (h->got.refcount * sizeof (Elf32_External_Rela));
+    htab->elf.srelgot->size += (h->got.refcount * sizeof (Elf32_External_Rela));
 
   return TRUE;
 }
@@ -1570,7 +1502,7 @@ elf_xtensa_allocate_local_got_size (struct bfd_link_info *info)
   if (htab == NULL)
     return;
 
-  for (i = info->input_bfds; i; i = i->link_next)
+  for (i = info->input_bfds; i; i = i->link.next)
     {
       bfd_signed_vma *local_got_refcounts;
       bfd_size_type j, cnt;
@@ -1596,8 +1528,8 @@ elf_xtensa_allocate_local_got_size (struct bfd_link_info *info)
            }
 
          if (local_got_refcounts[j] > 0)
-           htab->srelgot->size += (local_got_refcounts[j]
-                                   * sizeof (Elf32_External_Rela));
+           htab->elf.srelgot->size += (local_got_refcounts[j]
+                                       * sizeof (Elf32_External_Rela));
        }
     }
 }
@@ -1625,21 +1557,21 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   dynobj = elf_hash_table (info)->dynobj;
   if (dynobj == NULL)
     abort ();
-  srelgot = htab->srelgot;
-  srelplt = htab->srelplt;
+  srelgot = htab->elf.srelgot;
+  srelplt = htab->elf.srelplt;
 
   if (elf_hash_table (info)->dynamic_sections_created)
     {
-      BFD_ASSERT (htab->srelgot != NULL
-                 && htab->srelplt != NULL
-                 && htab->sgot != NULL
+      BFD_ASSERT (htab->elf.srelgot != NULL
+                 && htab->elf.srelplt != NULL
+                 && htab->elf.sgot != NULL
                  && htab->spltlittbl != NULL
                  && htab->sgotloc != NULL);
 
       /* Set the contents of the .interp section to the interpreter.  */
-      if (info->executable)
+      if (bfd_link_executable (info) && !info->nointerp)
        {
-         s = bfd_get_section_by_name (dynobj, ".interp");
+         s = bfd_get_linker_section (dynobj, ".interp");
          if (s == NULL)
            abort ();
          s->size = sizeof ELF_DYNAMIC_INTERPRETER;
@@ -1647,7 +1579,7 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
        }
 
       /* Allocate room for one word in ".got".  */
-      htab->sgot->size = 4;
+      htab->elf.sgot->size = 4;
 
       /* Allocate space in ".rela.got" for literals that reference global
         symbols and space in ".rela.plt" for literals that have PLT
@@ -1659,7 +1591,7 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       /* If we are generating a shared object, we also need space in
         ".rela.got" for R_XTENSA_RELATIVE relocs for literals that
         reference local symbols.  */
-      if (info->shared)
+      if (bfd_link_pic (info))
        elf_xtensa_allocate_local_got_size (info);
 
       /* Allocate space in ".plt" to match the size of ".rela.plt".  For
@@ -1709,13 +1641,13 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
         literal tables.  */
       sgotloc = htab->sgotloc;
       sgotloc->size = spltlittbl->size;
-      for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
+      for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
        {
          if (abfd->flags & DYNAMIC)
            continue;
          for (s = abfd->sections; s != NULL; s = s->next)
            {
-             if (! elf_discarded_section (s)
+             if (! discarded_section (s)
                  && xtensa_is_littable_section (s)
                  && s != spltlittbl)
                sgotloc->size += s->size;
@@ -1735,7 +1667,7 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 
       /* It's OK to base decisions on the section name, because none
         of the dynobj section names depend upon the input files.  */
-      name = bfd_get_section_name (dynobj, s);
+      name = bfd_section_name (s);
 
       if (CONST_STRNEQ (name, ".rela"))
        {
@@ -1815,7 +1747,7 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 #define add_dynamic_entry(TAG, VAL) \
   _bfd_elf_add_dynamic_entry (info, TAG, VAL)
 
-      if (info->executable)
+      if (bfd_link_executable (info))
        {
          if (!add_dynamic_entry (DT_DEBUG, 0))
            return FALSE;
@@ -1957,6 +1889,12 @@ elf_xtensa_do_reloc (reloc_howto_type *howto,
     case R_XTENSA_DIFF8:
     case R_XTENSA_DIFF16:
     case R_XTENSA_DIFF32:
+    case R_XTENSA_PDIFF8:
+    case R_XTENSA_PDIFF16:
+    case R_XTENSA_PDIFF32:
+    case R_XTENSA_NDIFF8:
+    case R_XTENSA_NDIFF16:
+    case R_XTENSA_NDIFF32:
     case R_XTENSA_TLS_FUNC:
     case R_XTENSA_TLS_ARG:
     case R_XTENSA_TLS_CALL:
@@ -1971,7 +1909,7 @@ elf_xtensa_do_reloc (reloc_howto_type *howto,
          if (is_windowed_call_opcode (opcode))
            {
              if ((self_address >> CALL_SEGMENT_BITS)
-                 != (relocation >> CALL_SEGMENT_BITS)) 
+                 != (relocation >> CALL_SEGMENT_BITS))
                {
                  *error_message = "windowed longcall crosses 1GB boundary; "
                    "return may fail";
@@ -1983,7 +1921,7 @@ elf_xtensa_do_reloc (reloc_howto_type *howto,
 
     case R_XTENSA_ASM_SIMPLIFY:
       {
-        /* Convert the L32R/CALLX to CALL.  */
+       /* Convert the L32R/CALLX to CALL.  */
        bfd_reloc_status_type retval =
          elf_xtensa_do_asm_simplify (contents, address, input_size,
                                      error_message);
@@ -2066,8 +2004,9 @@ elf_xtensa_do_reloc (reloc_howto_type *howto,
        }
       else if (opcode == get_const16_opcode ())
        {
-         /* ALT used for high 16 bits.  */
-         newval = relocation >> 16;
+         /* ALT used for high 16 bits.
+            Ignore 32-bit overflow.  */
+         newval = (relocation >> 16) & 0xffff;
          opnd = 1;
        }
       else
@@ -2144,7 +2083,7 @@ elf_xtensa_do_reloc (reloc_howto_type *howto,
       && is_windowed_call_opcode (opcode))
     {
       if ((self_address >> CALL_SEGMENT_BITS)
-         != (relocation >> CALL_SEGMENT_BITS)) 
+         != (relocation >> CALL_SEGMENT_BITS))
        {
          *error_message =
            "windowed call crosses 1GB boundary; return may fail";
@@ -2169,11 +2108,11 @@ vsprint_msg (const char *origmsg, const char *fmt, int arglen, ...)
   static char *message = NULL;
   bfd_size_type orig_len, len = 0;
   bfd_boolean is_append;
+  va_list ap;
 
-  VA_OPEN (ap, arglen);
-  VA_FIXEDARG (ap, const char *, origmsg);
-  
-  is_append = (origmsg == message);  
+  va_start (ap, arglen);
+
+  is_append = (origmsg == message);
 
   orig_len = strlen (origmsg);
   len = orig_len + strlen (fmt) + arglen + 20;
@@ -2188,7 +2127,7 @@ vsprint_msg (const char *origmsg, const char *fmt, int arglen, ...)
        memcpy (message, origmsg, orig_len);
       vsprintf (message + orig_len, fmt, ap);
     }
-  VA_CLOSE (ap);
+  va_end (ap);
   return message;
 }
 
@@ -2211,7 +2150,8 @@ bfd_elf_xtensa_reloc (bfd *abfd,
 {
   bfd_vma relocation;
   bfd_reloc_status_type flag;
-  bfd_size_type octets = reloc_entry->address * bfd_octets_per_byte (abfd);
+  bfd_size_type octets = (reloc_entry->address
+                         * OCTETS_PER_BYTE (abfd, input_section));
   bfd_vma output_base = 0;
   reloc_howto_type *howto = reloc_entry->howto;
   asection *reloc_target_output_section;
@@ -2317,7 +2257,7 @@ elf_xtensa_create_plt_entry (struct bfd_link_info *info,
 {
   asection *splt, *sgotplt;
   bfd_vma plt_base, got_base;
-  bfd_vma code_offset, lit_offset;
+  bfd_vma code_offset, lit_offset, abi_offset;
   int chunk;
 
   chunk = reloc_index / PLT_ENTRIES_PER_CHUNK;
@@ -2339,18 +2279,19 @@ elf_xtensa_create_plt_entry (struct bfd_link_info *info,
   /* Fill in the entry in the procedure linkage table.  */
   memcpy (splt->contents + code_offset,
          (bfd_big_endian (output_bfd)
-          ? elf_xtensa_be_plt_entry
-          : elf_xtensa_le_plt_entry),
+          ? elf_xtensa_be_plt_entry[XSHAL_ABI != XTHAL_ABI_WINDOWED]
+          : elf_xtensa_le_plt_entry[XSHAL_ABI != XTHAL_ABI_WINDOWED]),
          PLT_ENTRY_SIZE);
+  abi_offset = XSHAL_ABI == XTHAL_ABI_WINDOWED ? 3 : 0;
   bfd_put_16 (output_bfd, l32r_offset (got_base + 0,
-                                      plt_base + code_offset + 3),
-             splt->contents + code_offset + 4);
+                                      plt_base + code_offset + abi_offset),
+             splt->contents + code_offset + abi_offset + 1);
   bfd_put_16 (output_bfd, l32r_offset (got_base + 4,
-                                      plt_base + code_offset + 6),
-             splt->contents + code_offset + 7);
+                                      plt_base + code_offset + abi_offset + 3),
+             splt->contents + code_offset + abi_offset + 4);
   bfd_put_16 (output_bfd, l32r_offset (got_base + lit_offset,
-                                      plt_base + code_offset + 9),
-             splt->contents + code_offset + 10);
+                                      plt_base + code_offset + abi_offset + 6),
+             splt->contents + code_offset + abi_offset + 7);
 
   return plt_base + code_offset;
 }
@@ -2523,7 +2464,7 @@ replace_tls_insn (Elf_Internal_Rela *rel,
 
   xtensa_format_set_slot (isa, fmt, 0, ibuff, sbuff);
   xtensa_insnbuf_to_chars (isa, ibuff, contents + rel->r_offset,
-                           input_size - rel->r_offset);
+                          input_size - rel->r_offset);
 
   return TRUE;
 }
@@ -2566,7 +2507,11 @@ elf_xtensa_relocate_section (bfd *output_bfd,
   if (!xtensa_default_isa)
     xtensa_default_isa = xtensa_isa_init (0, 0);
 
-  BFD_ASSERT (is_xtensa_elf (input_bfd));
+  if (!is_xtensa_elf (input_bfd))
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return FALSE;
+    }
 
   htab = elf_xtensa_hash_table (info);
   if (htab == NULL)
@@ -2627,7 +2572,7 @@ elf_xtensa_relocate_section (bfd *output_bfd,
       unresolved_reloc = FALSE;
       warned = FALSE;
 
-      if (howto->partial_inplace && !info->relocatable)
+      if (howto->partial_inplace && !bfd_link_relocatable (info))
        {
          /* Because R_XTENSA_32 was made partial_inplace to fix some
             problems with DWARF info in partial links, there may be
@@ -2646,10 +2591,12 @@ elf_xtensa_relocate_section (bfd *output_bfd,
        }
       else
        {
+         bfd_boolean ignored;
+
          RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
                                   r_symndx, symtab_hdr, sym_hashes,
                                   h, sec, relocation,
-                                  unresolved_reloc, warned);
+                                  unresolved_reloc, warned, ignored);
 
          if (relocation == 0
              && !unresolved_reloc
@@ -2659,18 +2606,11 @@ elf_xtensa_relocate_section (bfd *output_bfd,
          sym_type = h->type;
        }
 
-      if (sec != NULL && elf_discarded_section (sec))
-       {
-         /* For relocs against symbols from removed linkonce sections,
-            or sections discarded by a linker script, we just want the
-            section contents zeroed.  Avoid any special processing.  */
-         _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset);
-         rel->r_info = 0;
-         rel->r_addend = 0;
-         continue;
-       }
+      if (sec != NULL && discarded_section (sec))
+       RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
+                                        rel, 1, relend, howto, 0, contents);
 
-      if (info->relocatable)
+      if (bfd_link_relocatable (info))
        {
          bfd_vma dest_addr;
          asection * sym_sec = get_elf_r_symndx_section (input_bfd, r_symndx);
@@ -2702,12 +2642,10 @@ elf_xtensa_relocate_section (bfd *output_bfd,
              r = contract_asm_expansion (contents, input_size, rel,
                                          &error_message);
              if (r != bfd_reloc_ok)
-               {
-                 if (!((*info->callbacks->reloc_dangerous)
-                       (info, error_message, input_bfd, input_section,
-                        rel->r_offset)))
-                   return FALSE;
-               }
+               (*info->callbacks->reloc_dangerous)
+                 (info, error_message,
+                  input_bfd, input_section, rel->r_offset);
+
              r_type = ELF32_R_TYPE (rel->r_info);
            }
 
@@ -2759,12 +2697,9 @@ elf_xtensa_relocate_section (bfd *output_bfd,
                }
            }
          if (r != bfd_reloc_ok)
-           {
-             if (!((*info->callbacks->reloc_dangerous)
-                   (info, error_message, input_bfd, input_section,
-                    rel->r_offset)))
-               return FALSE;
-           }
+           (*info->callbacks->reloc_dangerous)
+             (info, error_message,
+              input_bfd, input_section, rel->r_offset);
 
          /* Done with work for relocatable link; continue with next reloc.  */
          continue;
@@ -2783,9 +2718,12 @@ elf_xtensa_relocate_section (bfd *output_bfd,
       if (rel->r_offset >= input_size
          && ELF32_R_TYPE (rel->r_info) != R_XTENSA_NONE)
        {
-         (*_bfd_error_handler)
-           (_("%B(%A+0x%lx): relocation offset out of range (size=0x%x)"),
-            input_bfd, input_section, rel->r_offset, input_size);
+         _bfd_error_handler
+           /* xgettext:c-format */
+           (_("%pB(%pA+%#" PRIx64 "): "
+              "relocation offset out of range (size=%#" PRIx64 ")"),
+            input_bfd, input_section, (uint64_t) rel->r_offset,
+            (uint64_t) input_size);
          bfd_set_error (bfd_error_bad_value);
          return FALSE;
        }
@@ -2797,23 +2735,25 @@ elf_xtensa_relocate_section (bfd *output_bfd,
          name = (bfd_elf_string_from_elf_section
                  (input_bfd, symtab_hdr->sh_link, sym->st_name));
          if (name == NULL || *name == '\0')
-           name = bfd_section_name (input_bfd, sec);
+           name = bfd_section_name (sec);
        }
 
-      if (r_symndx != 0
+      if (r_symndx != STN_UNDEF
          && r_type != R_XTENSA_NONE
          && (h == NULL
              || h->root.type == bfd_link_hash_defined
              || h->root.type == bfd_link_hash_defweak)
          && IS_XTENSA_TLS_RELOC (r_type) != (sym_type == STT_TLS))
        {
-         (*_bfd_error_handler)
+         _bfd_error_handler
            ((sym_type == STT_TLS
-             ? _("%B(%A+0x%lx): %s used with TLS symbol %s")
-             : _("%B(%A+0x%lx): %s used with non-TLS symbol %s")),
+             /* xgettext:c-format */
+             ? _("%pB(%pA+%#" PRIx64 "): %s used with TLS symbol %s")
+             /* xgettext:c-format */
+             : _("%pB(%pA+%#" PRIx64 "): %s used with non-TLS symbol %s")),
             input_bfd,
             input_section,
-            (long) rel->r_offset,
+            (uint64_t) rel->r_offset,
             howto->name,
             name);
        }
@@ -2832,16 +2772,16 @@ elf_xtensa_relocate_section (bfd *output_bfd,
        case R_XTENSA_PLT:
          if (elf_hash_table (info)->dynamic_sections_created
              && (input_section->flags & SEC_ALLOC) != 0
-             && (dynamic_symbol || info->shared))
+             && (dynamic_symbol || bfd_link_pic (info)))
            {
              Elf_Internal_Rela outrel;
              bfd_byte *loc;
              asection *srel;
 
              if (dynamic_symbol && r_type == R_XTENSA_PLT)
-               srel = htab->srelplt;
+               srel = htab->elf.srelplt;
              else
-               srel = htab->srelgot;
+               srel = htab->elf.srelgot;
 
              BFD_ASSERT (srel != NULL);
 
@@ -2864,10 +2804,9 @@ elf_xtensa_relocate_section (bfd *output_bfd,
                    {
                      error_message =
                        _("dynamic relocation in read-only section");
-                     if (!((*info->callbacks->reloc_dangerous)
-                           (info, error_message, input_bfd, input_section,
-                            rel->r_offset)))
-                       return FALSE;
+                     (*info->callbacks->reloc_dangerous)
+                       (info, error_message,
+                        input_bfd, input_section, rel->r_offset);
                    }
 
                  if (dynamic_symbol)
@@ -2895,12 +2834,16 @@ elf_xtensa_relocate_section (bfd *output_bfd,
                        }
                      unresolved_reloc = FALSE;
                    }
-                 else
+                 else if (!is_weak_undef)
                    {
                      /* Generate a RELATIVE relocation.  */
                      outrel.r_info = ELF32_R_INFO (0, R_XTENSA_RELATIVE);
                      outrel.r_addend = 0;
                    }
+                 else
+                   {
+                     continue;
+                   }
                }
 
              loc = (srel->contents
@@ -2920,7 +2863,7 @@ elf_xtensa_relocate_section (bfd *output_bfd,
 
        case R_XTENSA_TLS_TPOFF:
          /* Switch to LE model for local symbols in an executable.  */
-         if (! info->shared && ! dynamic_symbol)
+         if (! bfd_link_pic (info) && ! dynamic_symbol)
            {
              relocation = tpoff (info, relocation);
              break;
@@ -2932,12 +2875,12 @@ elf_xtensa_relocate_section (bfd *output_bfd,
          {
            if (r_type == R_XTENSA_TLSDESC_FN)
              {
-               if (! info->shared || (tls_type & GOT_TLS_IE) != 0)
+               if (! bfd_link_pic (info) || (tls_type & GOT_TLS_IE) != 0)
                  r_type = R_XTENSA_NONE;
              }
            else if (r_type == R_XTENSA_TLSDESC_ARG)
              {
-               if (info->shared)
+               if (bfd_link_pic (info))
                  {
                    if ((tls_type & GOT_TLS_IE) != 0)
                      r_type = R_XTENSA_TLS_TPOFF;
@@ -2961,16 +2904,15 @@ elf_xtensa_relocate_section (bfd *output_bfd,
              {
                error_message =
                  _("TLS relocation invalid without dynamic sections");
-               if (!((*info->callbacks->reloc_dangerous)
-                     (info, error_message, input_bfd, input_section,
-                      rel->r_offset)))
-                 return FALSE;
+               (*info->callbacks->reloc_dangerous)
+                 (info, error_message,
+                  input_bfd, input_section, rel->r_offset);
              }
            else
              {
                Elf_Internal_Rela outrel;
                bfd_byte *loc;
-               asection *srel = htab->srelgot;
+               asection *srel = htab->elf.srelgot;
                int indx;
 
                outrel.r_offset = (input_section->output_section->vma
@@ -2985,10 +2927,9 @@ elf_xtensa_relocate_section (bfd *output_bfd,
                  {
                    error_message =
                      _("dynamic relocation in read-only section");
-                   if (!((*info->callbacks->reloc_dangerous)
-                         (info, error_message, input_bfd, input_section,
-                          rel->r_offset)))
-                     return FALSE;
+                   (*info->callbacks->reloc_dangerous)
+                     (info, error_message,
+                      input_bfd, input_section, rel->r_offset);
                  }
 
                indx = h && h->dynindx != -1 ? h->dynindx : 0;
@@ -3013,7 +2954,7 @@ elf_xtensa_relocate_section (bfd *output_bfd,
          break;
 
        case R_XTENSA_TLS_DTPOFF:
-         if (! info->shared)
+         if (! bfd_link_pic (info))
            /* Switch from LD model to LE model.  */
            relocation = tpoff (info, relocation);
          else
@@ -3030,12 +2971,9 @@ elf_xtensa_relocate_section (bfd *output_bfd,
                (h && elf_xtensa_hash_entry (h) == htab->tlsbase);
              if (! replace_tls_insn (rel, input_bfd, input_section, contents,
                                      is_ld_model, &error_message))
-               {
-                 if (!((*info->callbacks->reloc_dangerous)
-                       (info, error_message, input_bfd, input_section,
-                        rel->r_offset)))
-                   return FALSE;
-               }
+               (*info->callbacks->reloc_dangerous)
+                 (info, error_message,
+                  input_bfd, input_section, rel->r_offset);
 
              if (r_type != R_XTENSA_TLS_ARG || is_ld_model)
                {
@@ -3054,10 +2992,8 @@ elf_xtensa_relocate_section (bfd *output_bfd,
              error_message =
                vsprint_msg ("invalid relocation for dynamic symbol", ": %s",
                             strlen (name) + 2, name);
-             if (!((*info->callbacks->reloc_dangerous)
-                   (info, error_message, input_bfd, input_section,
-                    rel->r_offset)))
-               return FALSE;
+             (*info->callbacks->reloc_dangerous)
+               (info, error_message, input_bfd, input_section, rel->r_offset);
              continue;
            }
          break;
@@ -3068,13 +3004,17 @@ elf_xtensa_relocate_section (bfd *output_bfd,
         not process them.  */
       if (unresolved_reloc
          && !((input_section->flags & SEC_DEBUGGING) != 0
-              && h->def_dynamic))
-       {
-         (*_bfd_error_handler)
-           (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
+              && h->def_dynamic)
+         && _bfd_elf_section_offset (output_bfd, info, input_section,
+                                     rel->r_offset) != (bfd_vma) -1)
+       {
+         _bfd_error_handler
+           /* xgettext:c-format */
+           (_("%pB(%pA+%#" PRIx64 "): "
+              "unresolvable %s relocation against symbol `%s'"),
             input_bfd,
             input_section,
-            (long) rel->r_offset,
+            (uint64_t) rel->r_offset,
             howto->name,
             name);
          return FALSE;
@@ -3103,16 +3043,12 @@ elf_xtensa_relocate_section (bfd *output_bfd,
                                         strlen (name) + 22,
                                         name, (int) rel->r_addend);
 
-         if (!((*info->callbacks->reloc_dangerous)
-               (info, error_message, input_bfd, input_section,
-                rel->r_offset)))
-           return FALSE;
+         (*info->callbacks->reloc_dangerous)
+           (info, error_message, input_bfd, input_section, rel->r_offset);
        }
     }
 
-  if (lit_table)
-    free (lit_table);
-
+  free (lit_table);
   input_section->reloc_done = TRUE;
 
   return TRUE;
@@ -3142,7 +3078,7 @@ elf_xtensa_finish_dynamic_symbol (bfd *output_bfd ATTRIBUTE_UNUSED,
     }
 
   /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute.  */
-  if (strcmp (h->root.root.string, "_DYNAMIC") == 0
+  if (h == elf_hash_table (info)->hdynamic
       || h == elf_hash_table (info)->hgot)
     sym->st_shndx = SHN_ABS;
 
@@ -3176,7 +3112,7 @@ elf_xtensa_combine_prop_entries (bfd *output_bfd,
   sgotloc_size = sgotloc->size;
   if (sgotloc_size != section_size)
     {
-      (*_bfd_error_handler)
+      _bfd_error_handler
        (_("internal inconsistency in size of .got.loc section"));
       return -1;
     }
@@ -3192,8 +3128,7 @@ elf_xtensa_combine_prop_entries (bfd *output_bfd,
 
   if (!bfd_malloc_and_get_section (output_bfd, sxtlit, &contents))
     {
-      if (contents != 0)
-       free (contents);
+      free (contents);
       free (table);
       return -1;
     }
@@ -3271,7 +3206,7 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd,
 {
   struct elf_xtensa_link_hash_table *htab;
   bfd *dynobj;
-  asection *sdyn, *srelplt, *sgot, *sxtlit, *sgotloc;
+  asection *sdyn, *srelplt, *srelgot, *sgot, *sxtlit, *sgotloc;
   Elf32_External_Dyn *dyncon, *dynconend;
   int num_xtlit_entries = 0;
 
@@ -3283,12 +3218,12 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd,
     return FALSE;
 
   dynobj = elf_hash_table (info)->dynobj;
-  sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
+  sdyn = bfd_get_linker_section (dynobj, ".dynamic");
   BFD_ASSERT (sdyn != NULL);
 
   /* Set the first entry in the global offset table to the address of
      the dynamic section.  */
-  sgot = htab->sgot;
+  sgot = htab->elf.sgot;
   if (sgot)
     {
       BFD_ASSERT (sgot->size == 4);
@@ -3300,16 +3235,16 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd,
                    sgot->contents);
     }
 
-  srelplt = htab->srelplt;
+  srelplt = htab->elf.srelplt;
+  srelgot = htab->elf.srelgot;
   if (srelplt && srelplt->size != 0)
     {
-      asection *sgotplt, *srelgot, *spltlittbl;
+      asection *sgotplt, *spltlittbl;
       int chunk, plt_chunks, plt_entries;
       Elf_Internal_Rela irela;
       bfd_byte *loc;
       unsigned rtld_reloc;
 
-      srelgot = htab->srelgot;
       spltlittbl = htab->spltlittbl;
       BFD_ASSERT (srelgot != NULL && spltlittbl != NULL);
 
@@ -3375,14 +3310,6 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd,
                      spltlittbl->contents + (chunk * 8) + 4);
        }
 
-      /* All the dynamic relocations have been emitted at this point.
-        Make sure the relocation sections are the correct size.  */
-      if (srelgot->size != (sizeof (Elf32_External_Rela)
-                           * srelgot->reloc_count)
-         || srelplt->size != (sizeof (Elf32_External_Rela)
-                              * srelplt->reloc_count))
-       abort ();
-
      /* The .xt.lit.plt section has just been modified.  This must
        happen before the code below which combines adjacent literal
        table entries, and the .xt.lit.plt contents have to be forced to
@@ -3397,8 +3324,16 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd,
       spltlittbl->flags &= ~SEC_HAS_CONTENTS;
     }
 
+  /* All the dynamic relocations have been emitted at this point.
+     Make sure the relocation sections are the correct size.  */
+  if ((srelgot && srelgot->size != (sizeof (Elf32_External_Rela)
+                                   * srelgot->reloc_count))
+      || (srelplt && srelplt->size != (sizeof (Elf32_External_Rela)
+                                      * srelplt->reloc_count)))
+    abort ();
+
   /* Combine adjacent literal table entries.  */
-  BFD_ASSERT (! info->relocatable);
+  BFD_ASSERT (! bfd_link_relocatable (info));
   sxtlit = bfd_get_section_by_name (output_bfd, ".xt.lit");
   sgotloc = htab->sgotloc;
   BFD_ASSERT (sgotloc);
@@ -3428,30 +3363,22 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd,
          break;
 
        case DT_XTENSA_GOT_LOC_OFF:
-         dyn.d_un.d_ptr = htab->sgotloc->output_section->vma;
+         dyn.d_un.d_ptr = (htab->sgotloc->output_section->vma
+                           + htab->sgotloc->output_offset);
          break;
 
        case DT_PLTGOT:
-         dyn.d_un.d_ptr = htab->sgot->output_section->vma;
+         dyn.d_un.d_ptr = (htab->elf.sgot->output_section->vma
+                           + htab->elf.sgot->output_offset);
          break;
 
        case DT_JMPREL:
-         dyn.d_un.d_ptr = htab->srelplt->output_section->vma;
+         dyn.d_un.d_ptr = (htab->elf.srelplt->output_section->vma
+                           + htab->elf.srelplt->output_offset);
          break;
 
        case DT_PLTRELSZ:
-         dyn.d_un.d_val = htab->srelplt->output_section->size;
-         break;
-
-       case DT_RELASZ:
-         /* Adjust RELASZ to not include JMPREL.  This matches what
-            glibc expects and what is done for several other ELF
-            targets (e.g., i386, alpha), but the "correct" behavior
-            seems to be unresolved.  Since the linker script arranges
-            for .rela.plt to follow all other relocation sections, we
-            don't have to worry about changing the DT_RELA entry.  */
-         if (htab->srelplt)
-           dyn.d_un.d_val -= htab->srelplt->output_section->size;
+         dyn.d_un.d_val = htab->elf.srelplt->size;
          break;
        }
 
@@ -3468,13 +3395,14 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd,
    object file when linking.  */
 
 static bfd_boolean
-elf_xtensa_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
+elf_xtensa_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
 {
+  bfd *obfd = info->output_bfd;
   unsigned out_mach, in_mach;
   flagword out_flag, in_flag;
 
-  /* Check if we have the same endianess.  */
-  if (!_bfd_generic_verify_endian_match (ibfd, obfd))
+  /* Check if we have the same endianness.  */
+  if (!_bfd_generic_verify_endian_match (ibfd, info))
     return FALSE;
 
   /* Don't even pretend to support mixed-format linking.  */
@@ -3489,8 +3417,9 @@ elf_xtensa_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
   in_mach = in_flag & EF_XTENSA_MACH;
   if (out_mach != in_mach)
     {
-      (*_bfd_error_handler)
-       (_("%B: incompatible machine type. Output is 0x%x. Input is 0x%x"),
+      _bfd_error_handler
+       /* xgettext:c-format */
+       (_("%pB: incompatible machine type; output is 0x%x; input is 0x%x"),
         ibfd, out_mach, in_mach);
       bfd_set_error (bfd_error_wrong_format);
       return FALSE;
@@ -3509,10 +3438,10 @@ elf_xtensa_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
       return TRUE;
     }
 
-  if ((out_flag & EF_XTENSA_XT_INSN) != (in_flag & EF_XTENSA_XT_INSN)) 
+  if ((out_flag & EF_XTENSA_XT_INSN) != (in_flag & EF_XTENSA_XT_INSN))
     elf_elfheader (obfd)->e_flags &= (~ EF_XTENSA_XT_INSN);
 
-  if ((out_flag & EF_XTENSA_XT_LIT) != (in_flag & EF_XTENSA_XT_LIT)) 
+  if ((out_flag & EF_XTENSA_XT_LIT) != (in_flag & EF_XTENSA_XT_LIT))
     elf_elfheader (obfd)->e_flags &= (~ EF_XTENSA_XT_LIT);
 
   return TRUE;
@@ -3580,12 +3509,11 @@ elf_xtensa_object_p (bfd *abfd)
    file.  This gets the Xtensa architecture right based on the machine
    number.  */
 
-static void
-elf_xtensa_final_write_processing (bfd *abfd,
-                                  bfd_boolean linker ATTRIBUTE_UNUSED)
+static bfd_boolean
+elf_xtensa_final_write_processing (bfd *abfd)
 {
   int mach;
-  unsigned long val;
+  unsigned long val = elf_elfheader (abfd)->e_flags & EF_XTENSA_MACH;
 
   switch (mach = bfd_get_mach (abfd))
     {
@@ -3593,16 +3521,19 @@ elf_xtensa_final_write_processing (bfd *abfd,
       val = E_XTENSA_MACH;
       break;
     default:
-      return;
+      break;
     }
 
-  elf_elfheader (abfd)->e_flags &=  (~ EF_XTENSA_MACH);
+  elf_elfheader (abfd)->e_flags &= ~EF_XTENSA_MACH;
   elf_elfheader (abfd)->e_flags |= val;
+  return _bfd_elf_final_write_processing (abfd);
 }
 
 
 static enum elf_reloc_type_class
-elf_xtensa_reloc_type_class (const Elf_Internal_Rela *rela)
+elf_xtensa_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                            const asection *rel_sec ATTRIBUTE_UNUSED,
+                            const Elf_Internal_Rela *rela)
 {
   switch ((int) ELF32_R_TYPE (rela->r_info))
     {
@@ -3799,10 +3730,10 @@ elf_xtensa_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
      based on the size.  Just assume this is GNU/Linux.  */
 
   /* pr_cursig */
-  elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
+  elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12);
 
   /* pr_pid */
-  elf_tdata (abfd)->core_lwpid = bfd_get_32 (abfd, note->descdata + 24);
+  elf_tdata (abfd)->core->lwpid = bfd_get_32 (abfd, note->descdata + 24);
 
   /* pr_reg */
   offset = 72;
@@ -3823,9 +3754,9 @@ elf_xtensa_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
        return FALSE;
 
       case 128:                /* GNU/Linux elf_prpsinfo */
-       elf_tdata (abfd)->core_program
+       elf_tdata (abfd)->core->program
         = _bfd_elfcore_strndup (abfd, note->descdata + 32, 16);
-       elf_tdata (abfd)->core_command
+       elf_tdata (abfd)->core->command
         = _bfd_elfcore_strndup (abfd, note->descdata + 48, 80);
     }
 
@@ -3834,7 +3765,7 @@ elf_xtensa_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
      implementations, so strip it off if it exists.  */
 
   {
-    char *command = elf_tdata (abfd)->core_command;
+    char *command = elf_tdata (abfd)->core->command;
     int n = strlen (command);
 
     if (0 < n && command[n - 1] == ' ')
@@ -3973,6 +3904,33 @@ l32r_offset (bfd_vma addr, bfd_vma pc)
 }
 
 
+static xtensa_opcode
+get_rsr_lend_opcode (void)
+{
+  static xtensa_opcode rsr_lend_opcode = XTENSA_UNDEFINED;
+  static bfd_boolean done_lookup = FALSE;
+  if (!done_lookup)
+    {
+      rsr_lend_opcode = xtensa_opcode_lookup (xtensa_default_isa, "rsr.lend");
+      done_lookup = TRUE;
+    }
+  return rsr_lend_opcode;
+}
+
+static xtensa_opcode
+get_wsr_lbeg_opcode (void)
+{
+  static xtensa_opcode wsr_lbeg_opcode = XTENSA_UNDEFINED;
+  static bfd_boolean done_lookup = FALSE;
+  if (!done_lookup)
+    {
+      wsr_lbeg_opcode = xtensa_opcode_lookup (xtensa_default_isa, "wsr.lbeg");
+      done_lookup = TRUE;
+    }
+  return wsr_lbeg_opcode;
+}
+
+
 static int
 get_relocation_opnd (xtensa_opcode opcode, int r_type)
 {
@@ -4103,7 +4061,7 @@ get_asm_simplify_size (bfd_byte *contents,
     return 0;
 
   size += insnlen;
-  
+
   insnlen = insn_decode_len (contents, content_len, offset + size);
   if (insnlen == 0)
     return 0;
@@ -4142,7 +4100,7 @@ is_operand_relocation (int r_type)
   return FALSE;
 }
 
-      
+
 #define MIN_INSN_LENGTH 2
 
 /* Return 0 if it fails to decode.  */
@@ -4173,6 +4131,28 @@ insn_decode_len (bfd_byte *contents,
   return insn_len;
 }
 
+int
+insn_num_slots (bfd_byte *contents,
+               bfd_size_type content_len,
+               bfd_size_type offset)
+{
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_format fmt;
+  static xtensa_insnbuf ibuff = NULL;
+
+  if (offset + MIN_INSN_LENGTH > content_len)
+    return XTENSA_UNDEFINED;
+
+  if (ibuff == NULL)
+    ibuff = xtensa_insnbuf_alloc (isa);
+  xtensa_insnbuf_from_chars (isa, ibuff, &contents[offset],
+                            content_len - offset);
+  fmt = xtensa_format_decode (isa, ibuff);
+  if (fmt == XTENSA_UNDEFINED)
+    return XTENSA_UNDEFINED;
+  return xtensa_format_num_slots (isa, fmt);
+}
+
 
 /* Decode the opcode for a single slot instruction.
    Return 0 if it fails to decode or the instruction is multi-slot.  */
@@ -4243,7 +4223,7 @@ check_loop_aligned (bfd_byte *contents,
       BFD_ASSERT (FALSE);
       return FALSE;
     }
-  
+
   loop_len = insn_decode_len (contents, content_length, offset);
   insn_len = insn_decode_len (contents, content_length, offset + loop_len);
   if (loop_len == 0 || insn_len == 0)
@@ -4252,6 +4232,20 @@ check_loop_aligned (bfd_byte *contents,
       return FALSE;
     }
 
+  /* If this is relaxed loop, analyze first instruction of the actual loop
+     body.  It must be at offset 27 from the loop instruction address.  */
+  if (insn_len == 3
+      && insn_num_slots (contents, content_length, offset + loop_len) == 1
+      && insn_decode_opcode (contents, content_length,
+                            offset + loop_len, 0) == get_rsr_lend_opcode()
+      && insn_decode_len (contents, content_length, offset + loop_len + 3) == 3
+      && insn_num_slots (contents, content_length, offset + loop_len + 3) == 1
+      && insn_decode_opcode (contents, content_length,
+                            offset + loop_len + 3, 0) == get_wsr_lbeg_opcode())
+    {
+      loop_len = 27;
+      insn_len = insn_decode_len (contents, content_length, offset + loop_len);
+    }
   return check_branch_target_aligned_address (address + loop_len, insn_len);
 }
 
@@ -4635,7 +4629,7 @@ can_widen_instruction (xtensa_insnbuf slotbuf,
   return 0;
 }
 
-                      
+
 /* Attempt to widen an instruction.  If the widening is valid, perform
    the action in-place directly into the contents and return TRUE.  Otherwise,
    the return value is FALSE and the contents are not modified.  */
@@ -4719,7 +4713,7 @@ elf_xtensa_do_asm_simplify (bfd_byte *contents,
 
   if (content_length < address)
     {
-      *error_message = _("Attempt to convert L32R/CALLX to CALL failed");
+      *error_message = _("attempt to convert L32R/CALLX to CALL failed");
       return bfd_reloc_other;
     }
 
@@ -4727,15 +4721,15 @@ elf_xtensa_do_asm_simplify (bfd_byte *contents,
   direct_call_opcode = swap_callx_for_call_opcode (opcode);
   if (direct_call_opcode == XTENSA_UNDEFINED)
     {
-      *error_message = _("Attempt to convert L32R/CALLX to CALL failed");
+      *error_message = _("attempt to convert L32R/CALLX to CALL failed");
       return bfd_reloc_other;
     }
-  
+
   /* Assemble a NOP ("or a1, a1, a1") into the 0 byte offset.  */
   core_format = xtensa_format_lookup (isa, "x24");
   opcode = xtensa_opcode_lookup (isa, "or");
   xtensa_opcode_encode (isa, core_format, 0, slotbuf, opcode);
-  for (opn = 0; opn < 3; opn++) 
+  for (opn = 0; opn < 3; opn++)
     {
       uint32 regno = 1;
       xtensa_operand_encode (isa, opcode, opn, &regno);
@@ -4803,7 +4797,7 @@ swap_callx_for_call_opcode (xtensa_opcode opcode)
 #define CONST16_TARGET_REG_OPERAND 0
 #define CALLN_SOURCE_OPERAND 0
 
-static xtensa_opcode 
+static xtensa_opcode
 get_expanded_call_opcode (bfd_byte *buf, int bufsize, bfd_boolean *p_uses_l32r)
 {
   static xtensa_insnbuf insnbuf = NULL;
@@ -4879,7 +4873,7 @@ get_expanded_call_opcode (bfd_byte *buf, int bufsize, bfd_boolean *p_uses_l32r)
       || xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf))
     return XTENSA_UNDEFINED;
   opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
-  if (opcode == XTENSA_UNDEFINED 
+  if (opcode == XTENSA_UNDEFINED
       || !is_indirect_call_opcode (opcode))
     return XTENSA_UNDEFINED;
 
@@ -4910,7 +4904,7 @@ get_expanded_call_opcode (bfd_byte *buf, int bufsize, bfd_boolean *p_uses_l32r)
    For efficiency, an r_reloc also contains a "target_offset" field to
    cache the target-section-relative offset value that is represented by
    the relocation.
-   
+
    The r_reloc also contains a virtual offset that allows multiple
    inserted literals to be placed at the same "address" with
    different offsets.  */
@@ -5037,7 +5031,7 @@ print_r_reloc (FILE *fp, const r_reloc *r_rel)
       fprintf (fp, " + ");
       fprintf_vma (fp, r_rel->virtual_offset);
     }
-    
+
   fprintf (fp, ")");
 }
 
@@ -5157,7 +5151,7 @@ typedef struct value_map_hash_table_struct value_map_hash_table;
 
 struct literal_value_struct
 {
-  r_reloc r_rel; 
+  r_reloc r_rel;
   unsigned long value;
   bfd_boolean is_abs_literal;
 };
@@ -5198,7 +5192,7 @@ literal_value_equal (const literal_value *src1,
 {
   struct elf_link_hash_entry *h1, *h2;
 
-  if (r_reloc_is_const (&src1->r_rel) != r_reloc_is_const (&src2->r_rel)) 
+  if (r_reloc_is_const (&src1->r_rel) != r_reloc_is_const (&src2->r_rel))
     return FALSE;
 
   if (r_reloc_is_const (&src1->r_rel))
@@ -5210,13 +5204,13 @@ literal_value_equal (const literal_value *src1,
 
   if (src1->r_rel.target_offset != src2->r_rel.target_offset)
     return FALSE;
-   
+
   if (src1->r_rel.virtual_offset != src2->r_rel.virtual_offset)
     return FALSE;
 
   if (src1->value != src2->value)
     return FALSE;
-  
+
   /* Now check for the same section (if defined) or the same elf_hash
      (if undefined or weak).  */
   h1 = r_reloc_get_hash_entry (&src1->r_rel);
@@ -5258,7 +5252,7 @@ value_map_hash_table_init (void)
   values->count = 0;
   values->buckets = (value_map **)
     bfd_zmalloc (sizeof (value_map *) * values->bucket_count);
-  if (values->buckets == NULL) 
+  if (values->buckets == NULL)
     {
       free (values);
       return NULL;
@@ -5297,7 +5291,7 @@ literal_value_hash (const literal_value *src)
       hash_val += hash_bfd_vma (src->is_abs_literal * 1000);
       hash_val += hash_bfd_vma (src->r_rel.target_offset);
       hash_val += hash_bfd_vma (src->r_rel.virtual_offset);
-  
+
       /* Now check for the same section and the same elf_hash.  */
       if (r_reloc_is_defined (&src->r_rel))
        sec_or_hash = r_reloc_get_section (&src->r_rel);
@@ -5363,7 +5357,7 @@ add_value_map (value_map_hash_table *map,
   *bucket_p = val_e;
   map->count++;
   /* FIXME: Consider resizing the hash table if we get too many entries.  */
-  
+
   return val_e;
 }
 
@@ -5373,23 +5367,23 @@ add_value_map (value_map_hash_table *map,
 
 /* The following text actions are generated:
 
-   "ta_remove_insn"         remove an instruction or instructions
-   "ta_remove_longcall"     convert longcall to call
+   "ta_remove_insn"        remove an instruction or instructions
+   "ta_remove_longcall"            convert longcall to call
    "ta_convert_longcall"    convert longcall to nop/call
-   "ta_narrow_insn"         narrow a wide instruction
-   "ta_widen"               widen a narrow instruction
-   "ta_fill"                add fill or remove fill
+   "ta_narrow_insn"        narrow a wide instruction
+   "ta_widen"              widen a narrow instruction
+   "ta_fill"               add fill or remove fill
       removed < 0 is a fill; branches to the fill address will be
        changed to address + fill size (e.g., address - removed)
       removed >= 0 branches to the fill address will stay unchanged
-   "ta_remove_literal"      remove a literal; this action is
+   "ta_remove_literal"     remove a literal; this action is
                            indicated when a literal is removed
-                            or replaced.
-   "ta_add_literal"         insert a new literal; this action is
-                            indicated when a literal has been moved.
-                            It may use a virtual_offset because
+                           or replaced.
+   "ta_add_literal"        insert a new literal; this action is
+                           indicated when a literal has been moved.
+                           It may use a virtual_offset because
                            multiple literals can be placed at the
-                            same location.
+                           same location.
 
    For each of these text actions, we also record the number of bytes
    removed by performing the text action.  In the case of a "ta_widen"
@@ -5402,12 +5396,12 @@ typedef enum text_action_enum_t text_action_t;
 enum text_action_enum_t
 {
   ta_none,
-  ta_remove_insn,        /* removed = -size */
-  ta_remove_longcall,    /* removed = -size */
-  ta_convert_longcall,   /* removed = 0 */
-  ta_narrow_insn,        /* removed = -1 */
-  ta_widen_insn,         /* removed = +1 */
-  ta_fill,               /* removed = +size */
+  ta_remove_insn,       /* removed = -size */
+  ta_remove_longcall,   /* removed = -size */
+  ta_convert_longcall,  /* removed = 0 */
+  ta_narrow_insn,       /* removed = -1 */
+  ta_widen_insn,        /* removed = +1 */
+  ta_fill,              /* removed = +size */
   ta_remove_literal,
   ta_add_literal
 };
@@ -5422,35 +5416,49 @@ struct text_action_struct
   bfd_vma virtual_offset;  /* Zero except for adding literals.  */
   int removed_bytes;
   literal_value value; /* Only valid when adding literals.  */
+};
+
+struct removal_by_action_entry_struct
+{
+  bfd_vma offset;
+  int removed;
+  int eq_removed;
+  int eq_removed_before_fill;
+};
+typedef struct removal_by_action_entry_struct removal_by_action_entry;
 
-  text_action *next;
+struct removal_by_action_map_struct
+{
+  unsigned n_entries;
+  removal_by_action_entry *entry;
 };
+typedef struct removal_by_action_map_struct removal_by_action_map;
 
 
 /* List of all of the actions taken on a text section.  */
 struct text_action_list_struct
 {
-  text_action *head;
+  unsigned count;
+  splay_tree tree;
+  removal_by_action_map map;
 };
 
 
 static text_action *
 find_fill_action (text_action_list *l, asection *sec, bfd_vma offset)
 {
-  text_action **m_p;
+  text_action a;
 
   /* It is not necessary to fill at the end of a section.  */
   if (sec->size == offset)
     return NULL;
 
-  for (m_p = &l->head; *m_p && (*m_p)->offset <= offset; m_p = &(*m_p)->next)
-    {
-      text_action *t = *m_p;
-      /* When the action is another fill at the same address,
-        just increase the size.  */
-      if (t->offset == offset && t->action == ta_fill)
-       return t;
-    }
+  a.offset = offset;
+  a.action = ta_fill;
+
+  splay_tree_node node = splay_tree_lookup (l->tree, (splay_tree_key)&a);
+  if (node)
+    return (text_action *)node->value;
   return NULL;
 }
 
@@ -5498,20 +5506,63 @@ adjust_fill_action (text_action *ta, int fill_diff)
 }
 
 
+static int
+text_action_compare (splay_tree_key a, splay_tree_key b)
+{
+  text_action *pa = (text_action *)a;
+  text_action *pb = (text_action *)b;
+  static const int action_priority[] =
+    {
+      [ta_fill] = 0,
+      [ta_none] = 1,
+      [ta_convert_longcall] = 2,
+      [ta_narrow_insn] = 3,
+      [ta_remove_insn] = 4,
+      [ta_remove_longcall] = 5,
+      [ta_remove_literal] = 6,
+      [ta_widen_insn] = 7,
+      [ta_add_literal] = 8,
+    };
+
+  if (pa->offset == pb->offset)
+    {
+      if (pa->action == pb->action)
+         return 0;
+      return action_priority[pa->action] - action_priority[pb->action];
+    }
+  else
+    return pa->offset < pb->offset ? -1 : 1;
+}
+
+static text_action *
+action_first (text_action_list *action_list)
+{
+  splay_tree_node node = splay_tree_min (action_list->tree);
+  return node ? (text_action *)node->value : NULL;
+}
+
+static text_action *
+action_next (text_action_list *action_list, text_action *action)
+{
+  splay_tree_node node = splay_tree_successor (action_list->tree,
+                                              (splay_tree_key)action);
+  return node ? (text_action *)node->value : NULL;
+}
+
 /* Add a modification action to the text.  For the case of adding or
    removing space, modify any current fill and assume that
    "unreachable_space" bytes can be freely contracted.  Note that a
    negative removed value is a fill.  */
 
-static void 
+static void
 text_action_add (text_action_list *l,
                 text_action_t action,
                 asection *sec,
                 bfd_vma offset,
                 int removed)
 {
-  text_action **m_p;
   text_action *ta;
+  text_action a;
 
   /* It is not necessary to fill at the end of a section.  */
   if (action == ta_fill && sec->size == offset)
@@ -5521,34 +5572,30 @@ text_action_add (text_action_list *l,
   if (action == ta_fill && removed == 0)
     return;
 
-  for (m_p = &l->head; *m_p && (*m_p)->offset <= offset; m_p = &(*m_p)->next)
+  a.action = action;
+  a.offset = offset;
+
+  if (action == ta_fill)
     {
-      text_action *t = *m_p;
-      
-      if (action == ta_fill) 
+      splay_tree_node node = splay_tree_lookup (l->tree, (splay_tree_key)&a);
+
+      if (node)
        {
-         /* When the action is another fill at the same address,
-            just increase the size.  */
-         if (t->offset == offset && t->action == ta_fill)
-           {
-             t->removed_bytes += removed;
-             return;
-           }
-         /* Fills need to happen before widens so that we don't
-            insert fill bytes into the instruction stream.  */
-         if (t->offset == offset && t->action == ta_widen_insn)
-           break;
+         ta = (text_action *)node->value;
+         ta->removed_bytes += removed;
+         return;
        }
     }
+  else
+    BFD_ASSERT (splay_tree_lookup (l->tree, (splay_tree_key)&a) == NULL);
 
-  /* Create a new record and fill it up.  */
   ta = (text_action *) bfd_zmalloc (sizeof (text_action));
   ta->action = action;
   ta->sec = sec;
   ta->offset = offset;
   ta->removed_bytes = removed;
-  ta->next = (*m_p);
-  *m_p = ta;
+  splay_tree_insert (l->tree, (splay_tree_key)ta, (splay_tree_value)ta);
+  ++l->count;
 }
 
 
@@ -5559,7 +5606,6 @@ text_action_add_literal (text_action_list *l,
                         const literal_value *value,
                         int removed)
 {
-  text_action **m_p;
   text_action *ta;
   asection *sec = r_reloc_get_section (loc);
   bfd_vma offset = loc->target_offset;
@@ -5567,14 +5613,6 @@ text_action_add_literal (text_action_list *l,
 
   BFD_ASSERT (action == ta_add_literal);
 
-  for (m_p = &l->head; *m_p != NULL; m_p = &(*m_p)->next)
-    {
-      if ((*m_p)->offset > offset
-         && ((*m_p)->offset != offset
-             || (*m_p)->virtual_offset > virtual_offset))
-       break;
-    }
-
   /* Create a new record and fill it up.  */
   ta = (text_action *) bfd_zmalloc (sizeof (text_action));
   ta->action = action;
@@ -5583,8 +5621,10 @@ text_action_add_literal (text_action_list *l,
   ta->virtual_offset = virtual_offset;
   ta->value = *value;
   ta->removed_bytes = removed;
-  ta->next = (*m_p);
-  *m_p = ta;
+
+  BFD_ASSERT (splay_tree_lookup (l->tree, (splay_tree_key)ta) == NULL);
+  splay_tree_insert (l->tree, (splay_tree_key)ta, (splay_tree_value)ta);
+  ++l->count;
 }
 
 
@@ -5595,7 +5635,8 @@ text_action_add_literal (text_action_list *l,
    so that each search may begin where the previous one left off.  */
 
 static int
-removed_by_actions (text_action **p_start_action,
+removed_by_actions (text_action_list *action_list,
+                   text_action **p_start_action,
                    bfd_vma offset,
                    bfd_boolean before_fill)
 {
@@ -5603,6 +5644,13 @@ removed_by_actions (text_action **p_start_action,
   int removed = 0;
 
   r = *p_start_action;
+  if (r)
+    {
+      splay_tree_node node = splay_tree_lookup (action_list->tree,
+                                               (splay_tree_key)r);
+      BFD_ASSERT (node != NULL && r == (text_action *)node->value);
+    }
+
   while (r)
     {
       if (r->offset > offset)
@@ -5614,7 +5662,7 @@ removed_by_actions (text_action **p_start_action,
 
       removed += r->removed_bytes;
 
-      r = r->next;
+      r = action_next (action_list, r);
     }
 
   *p_start_action = r;
@@ -5622,24 +5670,125 @@ removed_by_actions (text_action **p_start_action,
 }
 
 
-static bfd_vma 
+static bfd_vma
 offset_with_removed_text (text_action_list *action_list, bfd_vma offset)
 {
-  text_action *r = action_list->head;
-  return offset - removed_by_actions (&r, offset, FALSE);
+  text_action *r = action_first (action_list);
+
+  return offset - removed_by_actions (action_list, &r, offset, FALSE);
 }
 
 
 static unsigned
 action_list_count (text_action_list *action_list)
 {
-  text_action *r = action_list->head;
-  unsigned count = 0;
-  for (r = action_list->head; r != NULL; r = r->next)
+  return action_list->count;
+}
+
+typedef struct map_action_fn_context_struct map_action_fn_context;
+struct map_action_fn_context_struct
+{
+  int removed;
+  removal_by_action_map map;
+  bfd_boolean eq_complete;
+};
+
+static int
+map_action_fn (splay_tree_node node, void *p)
+{
+  map_action_fn_context *ctx = p;
+  text_action *r = (text_action *)node->value;
+  removal_by_action_entry *ientry = ctx->map.entry + ctx->map.n_entries;
+
+  if (ctx->map.n_entries && (ientry - 1)->offset == r->offset)
+    {
+      --ientry;
+    }
+  else
+    {
+      ++ctx->map.n_entries;
+      ctx->eq_complete = FALSE;
+      ientry->offset = r->offset;
+      ientry->eq_removed_before_fill = ctx->removed;
+    }
+
+  if (!ctx->eq_complete)
+    {
+      if (r->action != ta_fill || r->removed_bytes >= 0)
+       {
+         ientry->eq_removed = ctx->removed;
+         ctx->eq_complete = TRUE;
+       }
+      else
+       ientry->eq_removed = ctx->removed + r->removed_bytes;
+    }
+
+  ctx->removed += r->removed_bytes;
+  ientry->removed = ctx->removed;
+  return 0;
+}
+
+static void
+map_removal_by_action (text_action_list *action_list)
+{
+  map_action_fn_context ctx;
+
+  ctx.removed = 0;
+  ctx.map.n_entries = 0;
+  ctx.map.entry = bfd_malloc (action_list_count (action_list) *
+                             sizeof (removal_by_action_entry));
+  ctx.eq_complete = FALSE;
+
+  splay_tree_foreach (action_list->tree, map_action_fn, &ctx);
+  action_list->map = ctx.map;
+}
+
+static int
+removed_by_actions_map (text_action_list *action_list, bfd_vma offset,
+                       bfd_boolean before_fill)
+{
+  unsigned a, b;
+
+  if (!action_list->map.entry)
+    map_removal_by_action (action_list);
+
+  if (!action_list->map.n_entries)
+    return 0;
+
+  a = 0;
+  b = action_list->map.n_entries;
+
+  while (b - a > 1)
+    {
+      unsigned c = (a + b) / 2;
+
+      if (action_list->map.entry[c].offset <= offset)
+       a = c;
+      else
+       b = c;
+    }
+
+  if (action_list->map.entry[a].offset < offset)
+    {
+      return action_list->map.entry[a].removed;
+    }
+  else if (action_list->map.entry[a].offset == offset)
+    {
+      return before_fill ?
+       action_list->map.entry[a].eq_removed_before_fill :
+       action_list->map.entry[a].eq_removed;
+    }
+  else
     {
-      count++;
+      return 0;
     }
-  return count;
+}
+
+static bfd_vma
+offset_with_removed_text_map (text_action_list *action_list, bfd_vma offset)
+{
+  int removed = removed_by_actions_map (action_list, offset, FALSE);
+  return offset - removed;
 }
 
 
@@ -5648,28 +5797,26 @@ action_list_count (text_action_list *action_list)
 static text_action *
 find_insn_action (text_action_list *action_list, bfd_vma offset)
 {
-  text_action *t;
-  for (t = action_list->head; t; t = t->next)
+  static const text_action_t action[] =
     {
-      if (t->offset == offset)
-       {
-         switch (t->action)
-           {
-           case ta_none:
-           case ta_fill:
-             break;
-           case ta_remove_insn:
-           case ta_remove_longcall:
-           case ta_convert_longcall:
-           case ta_narrow_insn:
-           case ta_widen_insn:
-             return t;
-           case ta_remove_literal:
-           case ta_add_literal:
-             BFD_ASSERT (0);
-             break;
-           }
-       }
+      ta_convert_longcall,
+      ta_remove_longcall,
+      ta_widen_insn,
+      ta_narrow_insn,
+      ta_remove_insn,
+    };
+  text_action a;
+  unsigned i;
+
+  a.offset = offset;
+  for (i = 0; i < sizeof (action) / sizeof (*action); ++i)
+    {
+      splay_tree_node node;
+
+      a.action = action[i];
+      node = splay_tree_lookup (action_list->tree, (splay_tree_key)&a);
+      if (node)
+       return (text_action *)node->value;
     }
   return NULL;
 }
@@ -5678,40 +5825,50 @@ find_insn_action (text_action_list *action_list, bfd_vma offset)
 #if DEBUG
 
 static void
-print_action_list (FILE *fp, text_action_list *action_list)
+print_action (FILE *fp, text_action *r)
+{
+  const char *t = "unknown";
+  switch (r->action)
+    {
+    case ta_remove_insn:
+      t = "remove_insn"; break;
+    case ta_remove_longcall:
+      t = "remove_longcall"; break;
+    case ta_convert_longcall:
+      t = "convert_longcall"; break;
+    case ta_narrow_insn:
+      t = "narrow_insn"; break;
+    case ta_widen_insn:
+      t = "widen_insn"; break;
+    case ta_fill:
+      t = "fill"; break;
+    case ta_none:
+      t = "none"; break;
+    case ta_remove_literal:
+      t = "remove_literal"; break;
+    case ta_add_literal:
+      t = "add_literal"; break;
+    }
+
+  fprintf (fp, "%s: %s[0x%lx] \"%s\" %d\n",
+          r->sec->owner->filename,
+          r->sec->name, (unsigned long) r->offset, t, r->removed_bytes);
+}
+
+static int
+print_action_list_fn (splay_tree_node node, void *p)
 {
-  text_action *r;
+  text_action *r = (text_action *)node->value;
 
-  fprintf (fp, "Text Action\n");
-  for (r = action_list->head; r != NULL; r = r->next)
-    {
-      const char *t = "unknown";
-      switch (r->action)
-       {
-       case ta_remove_insn:
-         t = "remove_insn"; break;
-       case ta_remove_longcall:
-         t = "remove_longcall"; break;
-       case ta_convert_longcall:
-         t = "convert_longcall"; break;
-       case ta_narrow_insn:
-         t = "narrow_insn"; break;
-       case ta_widen_insn:
-         t = "widen_insn"; break;
-       case ta_fill:
-         t = "fill"; break;
-       case ta_none:
-         t = "none"; break;
-       case ta_remove_literal:
-         t = "remove_literal"; break;
-       case ta_add_literal:
-         t = "add_literal"; break;
-       }
+  print_action (p, r);
+  return 0;
+}
 
-      fprintf (fp, "%s: %s[0x%lx] \"%s\" %d\n",
-              r->sec->owner->filename,
-              r->sec->name, r->offset, t, r->removed_bytes);
-    }
+static void
+print_action_list (FILE *fp, text_action_list *action_list)
+{
+  fprintf (fp, "Text Action\n");
+  splay_tree_foreach (action_list->tree, print_action_list_fn, fp);
 }
 
 #endif /* DEBUG */
@@ -5726,6 +5883,7 @@ print_action_list (FILE *fp, text_action_list *action_list)
    by the "from" offset field.  */
 
 typedef struct removed_literal_struct removed_literal;
+typedef struct removed_literal_map_entry_struct removed_literal_map_entry;
 typedef struct removed_literal_list_struct removed_literal_list;
 
 struct removed_literal_struct
@@ -5735,10 +5893,19 @@ struct removed_literal_struct
   removed_literal *next;
 };
 
+struct removed_literal_map_entry_struct
+{
+  bfd_vma addr;
+  removed_literal *literal;
+};
+
 struct removed_literal_list_struct
 {
   removed_literal *head;
   removed_literal *tail;
+
+  unsigned n_map;
+  removed_literal_map_entry *map;
 };
 
 
@@ -5760,9 +5927,9 @@ add_removed_literal (removed_literal_list *removed_list,
   else
     new_r->to.abfd = NULL;
   new_r->next = NULL;
-  
+
   r = removed_list->head;
-  if (r == NULL) 
+  if (r == NULL)
     {
       removed_list->head = new_r;
       removed_list->tail = new_r;
@@ -5775,7 +5942,7 @@ add_removed_literal (removed_literal_list *removed_list,
     }
   else
     {
-      while (r->from.target_offset < from->target_offset && r->next) 
+      while (r->from.target_offset < from->target_offset && r->next)
        {
          r = r->next;
        }
@@ -5787,6 +5954,39 @@ add_removed_literal (removed_literal_list *removed_list,
     }
 }
 
+static void
+map_removed_literal (removed_literal_list *removed_list)
+{
+  unsigned n_map = 0;
+  unsigned i;
+  removed_literal_map_entry *map = NULL;
+  removed_literal *r = removed_list->head;
+
+  for (i = 0; r; ++i, r = r->next)
+    {
+      if (i == n_map)
+       {
+         n_map = (n_map * 2) + 2;
+         map = bfd_realloc (map, n_map * sizeof (*map));
+       }
+      map[i].addr = r->from.target_offset;
+      map[i].literal = r;
+    }
+  removed_list->map = map;
+  removed_list->n_map = i;
+}
+
+static int
+removed_literal_compare (const void *a, const void *b)
+{
+  const removed_literal_map_entry *pa = a;
+  const removed_literal_map_entry *pb = b;
+
+  if (pa->addr == pb->addr)
+    return 0;
+  else
+    return pa->addr < pb->addr ? -1 : 1;
+}
 
 /* Check if the list of removed literals contains an entry for the
    given address.  Return the entry if found.  */
@@ -5794,12 +5994,21 @@ add_removed_literal (removed_literal_list *removed_list,
 static removed_literal *
 find_removed_literal (removed_literal_list *removed_list, bfd_vma addr)
 {
-  removed_literal *r = removed_list->head;
-  while (r && r->from.target_offset < addr)
-    r = r->next;
-  if (r && r->from.target_offset == addr)
-    return r;
-  return NULL;
+  removed_literal_map_entry *p;
+  removed_literal *r = NULL;
+
+  if (removed_list->map == NULL)
+    map_removed_literal (removed_list);
+
+  p = bsearch (&addr, removed_list->map, removed_list->n_map,
+              sizeof (*removed_list->map), removed_literal_compare);
+  if (p)
+    {
+      while (p != removed_list->map && (p - 1)->addr == addr)
+       --p;
+      r = p->literal;
+    }
+  return r;
 }
 
 
@@ -5853,7 +6062,7 @@ struct xtensa_relax_info_struct
      reallocated, the newly allocated relocations will be referenced
      here along with the actual size allocated.  The relocation
      count will always be found in the section structure.  */
-  Elf_Internal_Rela *allocated_relocs; 
+  Elf_Internal_Rela *allocated_relocs;
   unsigned relocs_count;
   unsigned allocated_relocs_count;
 };
@@ -5871,7 +6080,7 @@ elf_xtensa_new_section_hook (bfd *abfd, asection *sec)
   if (!sec->used_by_bfd)
     {
       struct elf_xtensa_section_data *sdata;
-      bfd_size_type amt = sizeof (*sdata);
+      size_t amt = sizeof (*sdata);
 
       sdata = bfd_zalloc (abfd, amt);
       if (sdata == NULL)
@@ -5913,13 +6122,16 @@ init_xtensa_relax_info (asection *sec)
   relax_info->removed_list.head = NULL;
   relax_info->removed_list.tail = NULL;
 
-  relax_info->action_list.head = NULL;
+  relax_info->action_list.tree = splay_tree_new (text_action_compare,
+                                                NULL, NULL);
+  relax_info->action_list.map.n_entries = 0;
+  relax_info->action_list.map.entry = NULL;
 
   relax_info->fix_list = NULL;
   relax_info->fix_array = NULL;
   relax_info->fix_array_count = 0;
 
-  relax_info->allocated_relocs = NULL; 
+  relax_info->allocated_relocs = NULL;
   relax_info->relocs_count = 0;
   relax_info->allocated_relocs_count = 0;
 }
@@ -5939,11 +6151,11 @@ struct reloc_bfd_fix_struct
   asection *src_sec;
   bfd_vma src_offset;
   unsigned src_type;                   /* Relocation type.  */
-  
+
   asection *target_sec;
   bfd_vma target_offset;
   bfd_boolean translated;
-  
+
   reloc_bfd_fix *next;
 };
 
@@ -6074,15 +6286,13 @@ init_section_cache (section_cache_t *sec_cache)
 
 
 static void
-clear_section_cache (section_cache_t *sec_cache)
+free_section_cache (section_cache_t *sec_cache)
 {
   if (sec_cache->sec)
     {
       release_contents (sec_cache->sec, sec_cache->contents);
       release_internal_relocs (sec_cache->sec, sec_cache->relocs);
-      if (sec_cache->ptbl)
-       free (sec_cache->ptbl);
-      memset (sec_cache, 0, sizeof (sec_cache));
+      free (sec_cache->ptbl);
     }
 }
 
@@ -6123,8 +6333,8 @@ section_cache_section (section_cache_t *sec_cache,
     goto err;
 
   /* Fill in the new section cache.  */
-  clear_section_cache (sec_cache);
-  memset (sec_cache, 0, sizeof (sec_cache));
+  free_section_cache (sec_cache);
+  init_section_cache (sec_cache);
 
   sec_cache->sec = sec;
   sec_cache->contents = contents;
@@ -6139,8 +6349,7 @@ section_cache_section (section_cache_t *sec_cache,
  err:
   release_contents (sec, contents);
   release_internal_relocs (sec, internal_relocs);
-  if (prop_table)
-    free (prop_table);
+  free (prop_table);
   return FALSE;
 }
 
@@ -6259,8 +6468,7 @@ init_ebb_constraint (ebb_constraint *c)
 static void
 free_ebb_constraint (ebb_constraint *c)
 {
-  if (c->actions)
-    free (c->actions);
+  free (c->actions);
 }
 
 
@@ -6336,9 +6544,12 @@ extend_ebb_bounds_forward (ebb_t *ebb)
                                  entry_end - ebb->end_offset);
       if (insn_block_len != (entry_end - ebb->end_offset))
        {
-         (*_bfd_error_handler)
-           (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"),
-            ebb->sec->owner, ebb->sec, ebb->end_offset + insn_block_len);
+         _bfd_error_handler
+           /* xgettext:c-format */
+           (_("%pB(%pA+%#" PRIx64 "): could not decode instruction; "
+              "possible configuration mismatch"),
+            ebb->sec->owner, ebb->sec,
+            (uint64_t) (ebb->end_offset + insn_block_len));
          return FALSE;
        }
       ebb->end_offset += insn_block_len;
@@ -6412,9 +6623,12 @@ extend_ebb_bounds_backward (ebb_t *ebb)
                                  ebb->start_offset - block_begin);
       if (insn_block_len != ebb->start_offset - block_begin)
        {
-         (*_bfd_error_handler)
-           (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"),
-            ebb->sec->owner, ebb->sec, ebb->end_offset + insn_block_len);
+         _bfd_error_handler
+           /* xgettext:c-format */
+           (_("%pB(%pA+%#" PRIx64 "): could not decode instruction; "
+              "possible configuration mismatch"),
+            ebb->sec->owner, ebb->sec,
+            (uint64_t) (ebb->end_offset + insn_block_len));
          return FALSE;
        }
       ebb->start_offset -= insn_block_len;
@@ -6488,8 +6702,7 @@ ebb_propose_action (ebb_constraint *c,
 
       for (i = 0; i < c->action_count; i++)
        new_actions[i] = c->actions[i];
-      if (c->actions)
-       free (c->actions);
+      free (c->actions);
       c->actions = new_actions;
       c->action_allocated = new_allocated;
     }
@@ -6541,8 +6754,7 @@ pin_internal_relocs (asection *sec, Elf_Internal_Rela *internal_relocs)
 static void
 release_internal_relocs (asection *sec, Elf_Internal_Rela *internal_relocs)
 {
-  if (internal_relocs
-      && elf_section_data (sec)->relocs != internal_relocs)
+  if (elf_section_data (sec)->relocs != internal_relocs)
     free (internal_relocs);
 }
 
@@ -6555,16 +6767,15 @@ retrieve_contents (bfd *abfd, asection *sec, bfd_boolean keep_memory)
 
   sec_size = bfd_get_section_limit (abfd, sec);
   contents = elf_section_data (sec)->this_hdr.contents;
-  
+
   if (contents == NULL && sec_size != 0)
     {
       if (!bfd_malloc_and_get_section (abfd, sec, &contents))
        {
-         if (contents)
-           free (contents);
+         free (contents);
          return NULL;
        }
-      if (keep_memory) 
+      if (keep_memory)
        elf_section_data (sec)->this_hdr.contents = contents;
     }
   return contents;
@@ -6581,7 +6792,7 @@ pin_contents (asection *sec, bfd_byte *contents)
 static void
 release_contents (asection *sec, bfd_byte *contents)
 {
-  if (contents && elf_section_data (sec)->this_hdr.contents != contents)
+  if (elf_section_data (sec)->this_hdr.contents != contents)
     free (contents);
 }
 
@@ -6626,25 +6837,26 @@ static bfd_boolean compute_text_actions
   (bfd *, asection *, struct bfd_link_info *);
 static bfd_boolean compute_ebb_proposed_actions (ebb_constraint *);
 static bfd_boolean compute_ebb_actions (ebb_constraint *);
+typedef struct reloc_range_list_struct reloc_range_list;
 static bfd_boolean check_section_ebb_pcrels_fit
-  (bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, const ebb_constraint *,
+  (bfd *, asection *, bfd_byte *, Elf_Internal_Rela *,
+   reloc_range_list *, const ebb_constraint *,
    const xtensa_opcode *);
 static bfd_boolean check_section_ebb_reduces (const ebb_constraint *);
 static void text_action_add_proposed
   (text_action_list *, const ebb_constraint *, asection *);
-static int compute_fill_extra_space (property_table_entry *);
 
 /* First pass: */
 static bfd_boolean compute_removed_literals
   (bfd *, asection *, struct bfd_link_info *, value_map_hash_table *);
 static Elf_Internal_Rela *get_irel_at_offset
   (asection *, Elf_Internal_Rela *, bfd_vma);
-static bfd_boolean is_removable_literal 
+static bfd_boolean is_removable_literal
   (const source_reloc *, int, const source_reloc *, int, asection *,
    property_table_entry *, int);
 static bfd_boolean remove_dead_literal
   (bfd *, asection *, struct bfd_link_info *, Elf_Internal_Rela *,
-   Elf_Internal_Rela *, source_reloc *, property_table_entry *, int); 
+   Elf_Internal_Rela *, source_reloc *, property_table_entry *, int);
 static bfd_boolean identify_literal_placement
   (bfd *, asection *, bfd_byte *, struct bfd_link_info *,
    value_map_hash_table *, bfd_boolean *, Elf_Internal_Rela *, int,
@@ -6674,7 +6886,7 @@ static bfd_boolean relax_property_section
 static bfd_boolean relax_section_symbols (bfd *, asection *);
 
 
-static bfd_boolean 
+static bfd_boolean
 elf_xtensa_relax_section (bfd *abfd,
                          asection *sec,
                          struct bfd_link_info *link_info,
@@ -6757,14 +6969,14 @@ analyze_relocations (struct bfd_link_info *link_info)
   bfd_boolean is_relaxable = FALSE;
 
   /* Initialize the per-section relaxation info.  */
-  for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next)
+  for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next)
     for (sec = abfd->sections; sec != NULL; sec = sec->next)
       {
        init_xtensa_relax_info (sec);
       }
 
   /* Mark relaxable sections (and count relocations against each one).  */
-  for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next)
+  for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next)
     for (sec = abfd->sections; sec != NULL; sec = sec->next)
       {
        if (!find_relaxable_sections (abfd, sec, link_info, &is_relaxable))
@@ -6776,7 +6988,7 @@ analyze_relocations (struct bfd_link_info *link_info)
     return TRUE;
 
   /* Allocate space for source_relocs.  */
-  for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next)
+  for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next)
     for (sec = abfd->sections; sec != NULL; sec = sec->next)
       {
        xtensa_relax_info *relax_info;
@@ -6793,7 +7005,7 @@ analyze_relocations (struct bfd_link_info *link_info)
       }
 
   /* Collect info on relocations against each relaxable section.  */
-  for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next)
+  for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next)
     for (sec = abfd->sections; sec != NULL; sec = sec->next)
       {
        if (!collect_source_relocs (abfd, sec, link_info))
@@ -6801,7 +7013,7 @@ analyze_relocations (struct bfd_link_info *link_info)
       }
 
   /* Compute the text actions.  */
-  for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link_next)
+  for (abfd = link_info->input_bfds; abfd != NULL; abfd = abfd->link.next)
     for (sec = abfd->sections; sec != NULL; sec = sec->next)
       {
        if (!compute_text_actions (abfd, sec, link_info))
@@ -6837,7 +7049,7 @@ find_relaxable_sections (bfd *abfd,
 
   internal_relocs = retrieve_internal_relocs (abfd, sec,
                                              link_info->keep_memory);
-  if (internal_relocs == NULL) 
+  if (internal_relocs == NULL)
     return ok;
 
   contents = retrieve_contents (abfd, sec, link_info->keep_memory);
@@ -6848,7 +7060,7 @@ find_relaxable_sections (bfd *abfd,
     }
 
   source_relax_info = get_xtensa_relax_info (sec);
-  for (i = 0; i < sec->reloc_count; i++) 
+  for (i = 0; i < sec->reloc_count; i++)
     {
       Elf_Internal_Rela *irel = &internal_relocs[i];
       r_reloc r_rel;
@@ -6882,7 +7094,7 @@ find_relaxable_sections (bfd *abfd,
        continue;
 
       /* Count PC-relative operand relocations against the target section.
-         Note: The conditions tested here must match the conditions under
+        Note: The conditions tested here must match the conditions under
         which init_source_reloc is called in collect_source_relocs().  */
       is_l32r_reloc = FALSE;
       if (is_operand_relocation (ELF32_R_TYPE (irel->r_info)))
@@ -6928,9 +7140,9 @@ collect_source_relocs (bfd *abfd,
   unsigned i;
   bfd_size_type sec_size;
 
-  internal_relocs = retrieve_internal_relocs (abfd, sec, 
+  internal_relocs = retrieve_internal_relocs (abfd, sec,
                                              link_info->keep_memory);
-  if (internal_relocs == NULL) 
+  if (internal_relocs == NULL)
     return ok;
 
   sec_size = bfd_get_section_limit (abfd, sec);
@@ -6942,7 +7154,7 @@ collect_source_relocs (bfd *abfd,
     }
 
   /* Record relocations against relaxable literal sections.  */
-  for (i = 0; i < sec->reloc_count; i++) 
+  for (i = 0; i < sec->reloc_count; i++)
     {
       Elf_Internal_Rela *irel = &internal_relocs[i];
       r_reloc r_rel;
@@ -7000,7 +7212,7 @@ collect_source_relocs (bfd *abfd,
      relocations associated with ASM_EXPANDs because they were just
      added in the preceding loop over the relocations.  */
 
-  for (i = 0; i < sec->reloc_count; i++) 
+  for (i = 0; i < sec->reloc_count; i++)
     {
       Elf_Internal_Rela *irel = &internal_relocs[i];
       bfd_boolean is_reachable;
@@ -7039,7 +7251,7 @@ collect_source_relocs (bfd *abfd,
                 the l32r_irel.  Note: The src_relocs array is not yet
                 sorted, but it wouldn't matter anyway because we're
                 searching by source offset instead of target offset.  */
-             s_reloc = find_source_reloc (target_relax_info->src_relocs, 
+             s_reloc = find_source_reloc (target_relax_info->src_relocs,
                                           target_relax_info->src_next,
                                           sec, l32r_irel);
              BFD_ASSERT (s_reloc);
@@ -7085,6 +7297,11 @@ is_resolvable_asm_expansion (bfd *abfd,
                             bfd_boolean *is_reachable_p)
 {
   asection *target_sec;
+  asection *s;
+  bfd_vma first_vma;
+  bfd_vma last_vma;
+  unsigned int first_align;
+  unsigned int adjust;
   bfd_vma target_offset;
   r_reloc r_rel;
   xtensa_opcode opcode, direct_call_opcode;
@@ -7098,7 +7315,7 @@ is_resolvable_asm_expansion (bfd *abfd,
   if (contents == NULL)
     return FALSE;
 
-  if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_ASM_EXPAND) 
+  if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_ASM_EXPAND)
     return FALSE;
 
   sec_size = bfd_get_section_limit (abfd, sec);
@@ -7107,7 +7324,7 @@ is_resolvable_asm_expansion (bfd *abfd,
   /* Optimization of longcalls that use CONST16 is not yet implemented.  */
   if (!uses_l32r)
     return FALSE;
-  
+
   direct_call_opcode = swap_callx_for_call_opcode (opcode);
   if (direct_call_opcode == XTENSA_UNDEFINED)
     return FALSE;
@@ -7126,20 +7343,103 @@ is_resolvable_asm_expansion (bfd *abfd,
      shouldn't crash regardless.  */
   if (!target_sec->output_section)
     return FALSE;
-      
+
   /* For relocatable sections, we can only simplify when the output
      section of the target is the same as the output section of the
      source.  */
-  if (link_info->relocatable
+  if (bfd_link_relocatable (link_info)
       && (target_sec->output_section != sec->output_section
          || is_reloc_sym_weak (abfd, irel)))
     return FALSE;
 
-  self_address = (sec->output_section->vma
-                 + sec->output_offset + irel->r_offset + 3);
-  dest_address = (target_sec->output_section->vma
-                 + target_sec->output_offset + target_offset);
-      
+  if (target_sec->output_section != sec->output_section)
+    {
+      /* If the two sections are sufficiently far away that relaxation
+        might take the call out of range, we can't simplify.  For
+        example, a positive displacement call into another memory
+        could get moved to a lower address due to literal removal,
+        but the destination won't move, and so the displacment might
+        get larger.
+
+        If the displacement is negative, assume the destination could
+        move as far back as the start of the output section.  The
+        self_address will be at least as far into the output section
+        as it is prior to relaxation.
+
+        If the displacement is postive, assume the destination will be in
+        it's pre-relaxed location (because relaxation only makes sections
+        smaller).  The self_address could go all the way to the beginning
+        of the output section.  */
+
+      dest_address = target_sec->output_section->vma;
+      self_address = sec->output_section->vma;
+
+      if (sec->output_section->vma > target_sec->output_section->vma)
+       self_address += sec->output_offset + irel->r_offset + 3;
+      else
+       dest_address += bfd_get_section_limit (abfd, target_sec->output_section);
+      /* Call targets should be four-byte aligned.  */
+      dest_address = (dest_address + 3) & ~3;
+    }
+  else
+    {
+
+      self_address = (sec->output_section->vma
+                     + sec->output_offset + irel->r_offset + 3);
+      dest_address = (target_sec->output_section->vma
+                     + target_sec->output_offset + target_offset);
+    }
+
+  /* Adjust addresses with alignments for the worst case to see if call insn
+     can fit.  Don't relax l32r + callx to call if the target can be out of
+     range due to alignment.
+     Caller and target addresses are highest and lowest address.
+     Search all sections between caller and target, looking for max alignment.
+     The adjustment is max alignment bytes.  If the alignment at the lowest
+     address is less than the adjustment, apply the adjustment to highest
+     address.  */
+
+  /* Start from lowest address.
+     Lowest address aligmnet is from input section.
+     Initial alignment (adjust) is from input section.  */
+  if (dest_address > self_address)
+    {
+      s = sec->output_section;
+      last_vma = dest_address;
+      first_align = sec->alignment_power;
+      adjust = target_sec->alignment_power;
+    }
+  else
+    {
+      s = target_sec->output_section;
+      last_vma = self_address;
+      first_align = target_sec->alignment_power;
+      adjust = sec->alignment_power;
+    }
+
+  first_vma = s->vma;
+
+  /* Find the largest alignment in output section list.  */
+  for (; s && s->vma >= first_vma && s->vma <= last_vma ; s = s->next)
+    {
+      if (s->alignment_power > adjust)
+       adjust = s->alignment_power;
+    }
+
+  if (adjust > first_align)
+    {
+      /* Alignment may enlarge the range, adjust highest address.  */
+      adjust = 1 << adjust;
+      if (dest_address > self_address)
+       {
+         dest_address += adjust;
+       }
+      else
+       {
+         self_address += adjust;
+       }
+    }
+
   *is_reachable_p = pcrel_reloc_fits (direct_call_opcode, 0,
                                      self_address, dest_address);
 
@@ -7160,7 +7460,7 @@ find_associated_l32r_irel (bfd *abfd,
 {
   unsigned i;
 
-  for (i = 0; i < sec->reloc_count; i++) 
+  for (i = 0; i < sec->reloc_count; i++)
     {
       Elf_Internal_Rela *irel = &internal_relocs[i];
 
@@ -7193,6 +7493,221 @@ build_reloc_opcodes (bfd *abfd,
   return reloc_opcodes;
 }
 
+struct reloc_range_struct
+{
+  bfd_vma addr;
+  bfd_boolean add; /* TRUE if start of a range, FALSE otherwise.  */
+  /* Original irel index in the array of relocations for a section.  */
+  unsigned irel_index;
+};
+typedef struct reloc_range_struct reloc_range;
+
+typedef struct reloc_range_list_entry_struct reloc_range_list_entry;
+struct reloc_range_list_entry_struct
+{
+  reloc_range_list_entry *next;
+  reloc_range_list_entry *prev;
+  Elf_Internal_Rela *irel;
+  xtensa_opcode opcode;
+  int opnum;
+};
+
+struct reloc_range_list_struct
+{
+  /* The rest of the structure is only meaningful when ok is TRUE.  */
+  bfd_boolean ok;
+
+  unsigned n_range; /* Number of range markers.  */
+  reloc_range *range; /* Sorted range markers.  */
+
+  unsigned first; /* Index of a first range element in the list.  */
+  unsigned last; /* One past index of a last range element in the list.  */
+
+  unsigned n_list; /* Number of list elements.  */
+  reloc_range_list_entry *reloc; /*  */
+  reloc_range_list_entry list_root;
+};
+
+static int
+reloc_range_compare (const void *a, const void *b)
+{
+  const reloc_range *ra = a;
+  const reloc_range *rb = b;
+
+  if (ra->addr != rb->addr)
+    return ra->addr < rb->addr ? -1 : 1;
+  if (ra->add != rb->add)
+    return ra->add ? -1 : 1;
+  return 0;
+}
+
+static void
+build_reloc_ranges (bfd *abfd, asection *sec,
+                   bfd_byte *contents,
+                   Elf_Internal_Rela *internal_relocs,
+                   xtensa_opcode *reloc_opcodes,
+                   reloc_range_list *list)
+{
+  unsigned i;
+  size_t n = 0;
+  size_t max_n = 0;
+  reloc_range *ranges = NULL;
+  reloc_range_list_entry *reloc =
+    bfd_malloc (sec->reloc_count * sizeof (*reloc));
+
+  memset (list, 0, sizeof (*list));
+  list->ok = TRUE;
+
+  for (i = 0; i < sec->reloc_count; i++)
+    {
+      Elf_Internal_Rela *irel = &internal_relocs[i];
+      int r_type = ELF32_R_TYPE (irel->r_info);
+      reloc_howto_type *howto = &elf_howto_table[r_type];
+      r_reloc r_rel;
+
+      if (r_type == R_XTENSA_ASM_SIMPLIFY
+         || r_type == R_XTENSA_32_PCREL
+         || !howto->pc_relative)
+       continue;
+
+      r_reloc_init (&r_rel, abfd, irel, contents,
+                   bfd_get_section_limit (abfd, sec));
+
+      if (r_reloc_get_section (&r_rel) != sec)
+       continue;
+
+      if (n + 2 > max_n)
+       {
+         max_n = (max_n + 2) * 2;
+         ranges = bfd_realloc (ranges, max_n * sizeof (*ranges));
+       }
+
+      ranges[n].addr = irel->r_offset;
+      ranges[n + 1].addr = r_rel.target_offset;
+
+      ranges[n].add = ranges[n].addr < ranges[n + 1].addr;
+      ranges[n + 1].add = !ranges[n].add;
+
+      ranges[n].irel_index = i;
+      ranges[n + 1].irel_index = i;
+
+      n += 2;
+
+      reloc[i].irel = irel;
+
+      /* Every relocation won't possibly be checked in the optimized version of
+        check_section_ebb_pcrels_fit, so this needs to be done here.  */
+      if (is_alt_relocation (ELF32_R_TYPE (irel->r_info)))
+       {
+         /* None of the current alternate relocs are PC-relative,
+            and only PC-relative relocs matter here.  */
+       }
+      else
+       {
+         xtensa_opcode opcode;
+         int opnum;
+
+         if (reloc_opcodes)
+           opcode = reloc_opcodes[i];
+         else
+           opcode = get_relocation_opcode (abfd, sec, contents, irel);
+
+         if (opcode == XTENSA_UNDEFINED)
+           {
+             list->ok = FALSE;
+             break;
+           }
+
+         opnum = get_relocation_opnd (opcode, ELF32_R_TYPE (irel->r_info));
+         if (opnum == XTENSA_UNDEFINED)
+           {
+             list->ok = FALSE;
+             break;
+           }
+
+         /* Record relocation opcode and opnum as we've calculated them
+            anyway and they won't change.  */
+         reloc[i].opcode = opcode;
+         reloc[i].opnum = opnum;
+       }
+    }
+
+  if (list->ok)
+    {
+      ranges = bfd_realloc (ranges, n * sizeof (*ranges));
+      qsort (ranges, n, sizeof (*ranges), reloc_range_compare);
+
+      list->n_range = n;
+      list->range = ranges;
+      list->reloc = reloc;
+      list->list_root.prev = &list->list_root;
+      list->list_root.next = &list->list_root;
+    }
+  else
+    {
+      free (ranges);
+      free (reloc);
+    }
+}
+
+static void reloc_range_list_append (reloc_range_list *list,
+                                    unsigned irel_index)
+{
+  reloc_range_list_entry *entry = list->reloc + irel_index;
+
+  entry->prev = list->list_root.prev;
+  entry->next = &list->list_root;
+  entry->prev->next = entry;
+  entry->next->prev = entry;
+  ++list->n_list;
+}
+
+static void reloc_range_list_remove (reloc_range_list *list,
+                                    unsigned irel_index)
+{
+  reloc_range_list_entry *entry = list->reloc + irel_index;
+
+  entry->next->prev = entry->prev;
+  entry->prev->next = entry->next;
+  --list->n_list;
+}
+
+/* Update relocation list object so that it lists all relocations that cross
+   [first; last] range.  Range bounds should not decrease with successive
+   invocations.  */
+static void reloc_range_list_update_range (reloc_range_list *list,
+                                          bfd_vma first, bfd_vma last)
+{
+  /* This should not happen: EBBs are iterated from lower addresses to higher.
+     But even if that happens there's no need to break: just flush current list
+     and start from scratch.  */
+  if ((list->last > 0 && list->range[list->last - 1].addr > last) ||
+      (list->first > 0 && list->range[list->first - 1].addr >= first))
+    {
+      list->first = 0;
+      list->last = 0;
+      list->n_list = 0;
+      list->list_root.next = &list->list_root;
+      list->list_root.prev = &list->list_root;
+      fprintf (stderr, "%s: move backwards requested\n", __func__);
+    }
+
+  for (; list->last < list->n_range &&
+       list->range[list->last].addr <= last; ++list->last)
+    if (list->range[list->last].add)
+      reloc_range_list_append (list, list->range[list->last].irel_index);
+
+  for (; list->first < list->n_range &&
+       list->range[list->first].addr < first; ++list->first)
+    if (!list->range[list->first].add)
+      reloc_range_list_remove (list, list->range[list->first].irel_index);
+}
+
+static void free_reloc_range_list (reloc_range_list *list)
+{
+  free (list->range);
+  free (list->reloc);
+}
 
 /* The compute_text_actions function will build a list of potential
    transformation actions for code in the extended basic block of each
@@ -7219,6 +7734,7 @@ compute_text_actions (bfd *abfd,
   property_table_entry *prop_table = 0;
   int ptblsize = 0;
   bfd_size_type sec_size;
+  reloc_range_list relevant_relocs;
 
   relax_info = get_xtensa_relax_info (sec);
   BFD_ASSERT (relax_info);
@@ -7251,6 +7767,12 @@ compute_text_actions (bfd *abfd,
       goto error_return;
     }
 
+  /* Precompute the opcode for each relocation.  */
+  reloc_opcodes = build_reloc_opcodes (abfd, sec, contents, internal_relocs);
+
+  build_reloc_ranges (abfd, sec, contents, internal_relocs, reloc_opcodes,
+                     &relevant_relocs);
+
   for (i = 0; i < sec->reloc_count; i++)
     {
       Elf_Internal_Rela *irel = &internal_relocs[i];
@@ -7268,9 +7790,12 @@ compute_text_actions (bfd *abfd,
       simplify_size = get_asm_simplify_size (contents, sec_size, r_offset);
       if (simplify_size == 0)
        {
-         (*_bfd_error_handler)
-           (_("%B(%A+0x%lx): could not decode instruction for XTENSA_ASM_SIMPLIFY relocation; possible configuration mismatch"),
-            sec->owner, sec, r_offset);
+         _bfd_error_handler
+           /* xgettext:c-format */
+           (_("%pB(%pA+%#" PRIx64 "): could not decode instruction for "
+              "XTENSA_ASM_SIMPLIFY relocation; "
+              "possible configuration mismatch"),
+            sec->owner, sec, (uint64_t) r_offset);
          continue;
        }
 
@@ -7314,17 +7839,13 @@ compute_text_actions (bfd *abfd,
       ebb->start_reloc_idx = i;
       ebb->end_reloc_idx = i;
 
-      /* Precompute the opcode for each relocation.  */
-      if (reloc_opcodes == NULL)
-       reloc_opcodes = build_reloc_opcodes (abfd, sec, contents,
-                                            internal_relocs);
-
       if (!extend_ebb_bounds (ebb)
          || !compute_ebb_proposed_actions (&ebb_table)
          || !compute_ebb_actions (&ebb_table)
          || !check_section_ebb_pcrels_fit (abfd, sec, contents,
-                                           internal_relocs, &ebb_table,
-                                           reloc_opcodes)
+                                           internal_relocs,
+                                           &relevant_relocs,
+                                           &ebb_table, reloc_opcodes)
          || !check_section_ebb_reduces (&ebb_table))
        {
          /* If anything goes wrong or we get unlucky and something does
@@ -7346,18 +7867,18 @@ compute_text_actions (bfd *abfd,
       free_ebb_constraint (&ebb_table);
     }
 
+  free_reloc_range_list (&relevant_relocs);
+
 #if DEBUG
-  if (relax_info->action_list.head)
+  if (action_list_count (&relax_info->action_list))
     print_action_list (stderr, &relax_info->action_list);
 #endif
 
-error_return:
+ error_return:
   release_contents (sec, contents);
   release_internal_relocs (sec, internal_relocs);
-  if (prop_table)
-    free (prop_table);
-  if (reloc_opcodes)
-    free (reloc_opcodes);
+  free (prop_table);
+  free (reloc_opcodes);
 
   return ok;
 }
@@ -7377,7 +7898,7 @@ prev_instr_is_a_loop (bfd_byte *contents,
     return FALSE;
   prev_opcode = insn_decode_opcode (contents, content_length, offset-3, 0);
   return (xtensa_opcode_is_loop (xtensa_default_isa, prev_opcode) == 1);
-} 
+}
 
 
 /* Find all of the possible actions for an extended basic block.  */
@@ -7427,7 +7948,7 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table)
 
          insn_len = insn_decode_len (ebb->contents, ebb->content_length,
                                      offset);
-         if (insn_len == 0) 
+         if (insn_len == 0)
            goto decode_error;
 
          if (check_branch_target_aligned_address (offset, insn_len))
@@ -7456,7 +7977,7 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table)
            {
              bfd_size_type simplify_size;
 
-             simplify_size = get_asm_simplify_size (ebb->contents, 
+             simplify_size = get_asm_simplify_size (ebb->contents,
                                                     ebb->content_length,
                                                     irel->r_offset);
              if (simplify_size == 0)
@@ -7464,7 +7985,7 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table)
 
              ebb_propose_action (ebb_table, EBB_NO_ALIGN, 0,
                                  ta_convert_longcall, offset, 0, TRUE);
-             
+
              offset += simplify_size;
              continue;
            }
@@ -7528,9 +8049,11 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table)
   return TRUE;
 
  decode_error:
-  (*_bfd_error_handler)
-    (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"),
-     ebb->sec->owner, ebb->sec, offset);
+  _bfd_error_handler
+    /* xgettext:c-format */
+    (_("%pB(%pA+%#" PRIx64 "): could not decode instruction; "
+       "possible configuration mismatch"),
+     ebb->sec->owner, ebb->sec, (uint64_t) offset);
   return FALSE;
 }
 
@@ -7780,7 +8303,7 @@ compute_ebb_actions (ebb_constraint *ebb_table)
       BFD_ASSERT (action->action == ta_fill);
       BFD_ASSERT (ebb->ends_unreachable->flags & XTENSA_PROP_UNREACHABLE);
 
-      extra_space = compute_fill_extra_space (ebb->ends_unreachable);
+      extra_space = xtensa_compute_fill_extra_space (ebb->ends_unreachable);
       br = action->removed_bytes + removed_bytes + extra_space;
       br = br & ((1 << ebb->sec->alignment_power ) - 1);
 
@@ -7799,8 +8322,8 @@ typedef struct xlate_map xlate_map_t;
 
 struct xlate_map_entry
 {
-  unsigned orig_address;
-  unsigned new_address;
+  bfd_vma orig_address;
+  bfd_vma new_address;
   unsigned size;
 };
 
@@ -7811,7 +8334,7 @@ struct xlate_map
 };
 
 
-static int 
+static int
 xlate_compare (const void *a_v, const void *b_v)
 {
   const xlate_map_entry_t *a = (const xlate_map_entry_t *) a_v;
@@ -7831,6 +8354,7 @@ xlate_offset_with_removed_text (const xlate_map_t *map,
 {
   void *r;
   xlate_map_entry_t *e;
+  struct xlate_map_entry se;
 
   if (map == NULL)
     return offset_with_removed_text (action_list, offset);
@@ -7838,16 +8362,73 @@ xlate_offset_with_removed_text (const xlate_map_t *map,
   if (map->entry_count == 0)
     return offset;
 
-  r = bsearch (&offset, map->entry, map->entry_count,
+  se.orig_address = offset;
+  r = bsearch (&se, map->entry, map->entry_count,
               sizeof (xlate_map_entry_t), &xlate_compare);
   e = (xlate_map_entry_t *) r;
-  
+
+  /* There could be a jump past the end of the section,
+     allow it using the last xlate map entry to translate its address.  */
+  if (e == NULL)
+    {
+      e = map->entry + map->entry_count - 1;
+      if (xlate_compare (&se, e) <= 0)
+       e = NULL;
+    }
   BFD_ASSERT (e != NULL);
   if (e == NULL)
     return offset;
   return e->new_address - e->orig_address + offset;
 }
 
+typedef struct xlate_map_context_struct xlate_map_context;
+struct xlate_map_context_struct
+{
+  xlate_map_t *map;
+  xlate_map_entry_t *current_entry;
+  int removed;
+};
+
+static int
+xlate_map_fn (splay_tree_node node, void *p)
+{
+  text_action *r = (text_action *)node->value;
+  xlate_map_context *ctx = p;
+  unsigned orig_size = 0;
+
+  switch (r->action)
+    {
+    case ta_none:
+    case ta_remove_insn:
+    case ta_convert_longcall:
+    case ta_remove_literal:
+    case ta_add_literal:
+      break;
+    case ta_remove_longcall:
+      orig_size = 6;
+      break;
+    case ta_narrow_insn:
+      orig_size = 3;
+      break;
+    case ta_widen_insn:
+      orig_size = 2;
+      break;
+    case ta_fill:
+      break;
+    }
+  ctx->current_entry->size =
+    r->offset + orig_size - ctx->current_entry->orig_address;
+  if (ctx->current_entry->size != 0)
+    {
+      ctx->current_entry++;
+      ctx->map->entry_count++;
+    }
+  ctx->current_entry->orig_address = r->offset + orig_size;
+  ctx->removed += r->removed_bytes;
+  ctx->current_entry->new_address = r->offset + orig_size - ctx->removed;
+  ctx->current_entry->size = 0;
+  return 0;
+}
 
 /* Build a binary searchable offset translation map from a section's
    action list.  */
@@ -7855,87 +8436,53 @@ xlate_offset_with_removed_text (const xlate_map_t *map,
 static xlate_map_t *
 build_xlate_map (asection *sec, xtensa_relax_info *relax_info)
 {
-  xlate_map_t *map = (xlate_map_t *) bfd_malloc (sizeof (xlate_map_t));
   text_action_list *action_list = &relax_info->action_list;
   unsigned num_actions = 0;
-  text_action *r;
-  int removed;
-  xlate_map_entry_t *current_entry;
+  xlate_map_context ctx;
 
-  if (map == NULL)
+  ctx.map = (xlate_map_t *) bfd_malloc (sizeof (xlate_map_t));
+
+  if (ctx.map == NULL)
     return NULL;
 
   num_actions = action_list_count (action_list);
-  map->entry = (xlate_map_entry_t *) 
+  ctx.map->entry = (xlate_map_entry_t *)
     bfd_malloc (sizeof (xlate_map_entry_t) * (num_actions + 1));
-  if (map->entry == NULL)
+  if (ctx.map->entry == NULL)
     {
-      free (map);
+      free (ctx.map);
       return NULL;
     }
-  map->entry_count = 0;
-  
-  removed = 0;
-  current_entry = &map->entry[0];
+  ctx.map->entry_count = 0;
 
-  current_entry->orig_address = 0;
-  current_entry->new_address = 0;
-  current_entry->size = 0;
+  ctx.removed = 0;
+  ctx.current_entry = &ctx.map->entry[0];
 
-  for (r = action_list->head; r != NULL; r = r->next)
-    {
-      unsigned orig_size = 0;
-      switch (r->action)
-       {
-       case ta_none:
-       case ta_remove_insn:
-       case ta_convert_longcall:
-       case ta_remove_literal:
-       case ta_add_literal:
-         break;
-       case ta_remove_longcall:
-         orig_size = 6;
-         break;
-       case ta_narrow_insn:
-         orig_size = 3;
-         break;
-       case ta_widen_insn:
-         orig_size = 2;
-         break;
-       case ta_fill:
-         break;
-       }
-      current_entry->size =
-       r->offset + orig_size - current_entry->orig_address;
-      if (current_entry->size != 0)
-       {
-         current_entry++;
-         map->entry_count++;
-       }
-      current_entry->orig_address = r->offset + orig_size;
-      removed += r->removed_bytes;
-      current_entry->new_address = r->offset + orig_size - removed;
-      current_entry->size = 0;
-    }
+  ctx.current_entry->orig_address = 0;
+  ctx.current_entry->new_address = 0;
+  ctx.current_entry->size = 0;
 
-  current_entry->size = (bfd_get_section_limit (sec->owner, sec)
-                        - current_entry->orig_address);
-  if (current_entry->size != 0)
-    map->entry_count++;
+  splay_tree_foreach (action_list->tree, xlate_map_fn, &ctx);
 
-  return map;
+  ctx.current_entry->size = (bfd_get_section_limit (sec->owner, sec)
+                            - ctx.current_entry->orig_address);
+  if (ctx.current_entry->size != 0)
+    ctx.map->entry_count++;
+
+  return ctx.map;
 }
 
 
 /* Free an offset translation map.  */
 
-static void 
+static void
 free_xlate_map (xlate_map_t *map)
 {
-  if (map && map->entry)
-    free (map->entry);
   if (map)
-    free (map);
+    {
+      free (map->entry);
+      free (map);
+    }
 }
 
 
@@ -7948,14 +8495,17 @@ check_section_ebb_pcrels_fit (bfd *abfd,
                              asection *sec,
                              bfd_byte *contents,
                              Elf_Internal_Rela *internal_relocs,
+                             reloc_range_list *relevant_relocs,
                              const ebb_constraint *constraint,
                              const xtensa_opcode *reloc_opcodes)
 {
   unsigned i, j;
+  unsigned n = sec->reloc_count;
   Elf_Internal_Rela *irel;
   xlate_map_t *xmap = NULL;
   bfd_boolean ok = TRUE;
   xtensa_relax_info *relax_info;
+  reloc_range_list_entry *entry = NULL;
 
   relax_info = get_xtensa_relax_info (sec);
 
@@ -7966,7 +8516,40 @@ check_section_ebb_pcrels_fit (bfd *abfd,
         can still be used.  */
     }
 
-  for (i = 0; i < sec->reloc_count; i++)
+  if (relevant_relocs && constraint->action_count)
+    {
+      if (!relevant_relocs->ok)
+       {
+         ok = FALSE;
+         n = 0;
+       }
+      else
+       {
+         bfd_vma min_offset, max_offset;
+         min_offset = max_offset = constraint->actions[0].offset;
+
+         for (i = 1; i < constraint->action_count; ++i)
+           {
+             proposed_action *action = &constraint->actions[i];
+             bfd_vma offset = action->offset;
+
+             if (offset < min_offset)
+               min_offset = offset;
+             if (offset > max_offset)
+               max_offset = offset;
+           }
+         reloc_range_list_update_range (relevant_relocs, min_offset,
+                                        max_offset);
+         n = relevant_relocs->n_list;
+         entry = &relevant_relocs->list_root;
+       }
+    }
+  else
+    {
+      relevant_relocs = NULL;
+    }
+
+  for (i = 0; i < n; i++)
     {
       r_reloc r_rel;
       bfd_vma orig_self_offset, orig_target_offset;
@@ -7975,7 +8558,15 @@ check_section_ebb_pcrels_fit (bfd *abfd,
       reloc_howto_type *howto;
       int self_removed_bytes, target_removed_bytes;
 
-      irel = &internal_relocs[i];
+      if (relevant_relocs)
+       {
+         entry = entry->next;
+         irel = entry->irel;
+       }
+      else
+       {
+         irel = internal_relocs + i;
+       }
       r_type = ELF32_R_TYPE (irel->r_info);
 
       howto = &elf_howto_table[r_type];
@@ -8041,21 +8632,30 @@ check_section_ebb_pcrels_fit (bfd *abfd,
          xtensa_opcode opcode;
          int opnum;
 
-         if (reloc_opcodes)
-           opcode = reloc_opcodes[i];
-         else
-           opcode = get_relocation_opcode (abfd, sec, contents, irel);
-         if (opcode == XTENSA_UNDEFINED)
+         if (relevant_relocs)
            {
-             ok = FALSE;
-             break;
+             opcode = entry->opcode;
+             opnum = entry->opnum;
            }
-
-         opnum = get_relocation_opnd (opcode, ELF32_R_TYPE (irel->r_info));
-         if (opnum == XTENSA_UNDEFINED)
+         else
            {
-             ok = FALSE;
-             break;
+             if (reloc_opcodes)
+               opcode = reloc_opcodes[relevant_relocs ?
+                 (unsigned)(entry - relevant_relocs->reloc) : i];
+             else
+               opcode = get_relocation_opcode (abfd, sec, contents, irel);
+             if (opcode == XTENSA_UNDEFINED)
+               {
+                 ok = FALSE;
+                 break;
+               }
+
+             opnum = get_relocation_opnd (opcode, ELF32_R_TYPE (irel->r_info));
+             if (opnum == XTENSA_UNDEFINED)
+               {
+                 ok = FALSE;
+                 break;
+               }
            }
 
          if (!pcrel_reloc_fits (opcode, opnum, self_offset, target_offset))
@@ -8066,8 +8666,7 @@ check_section_ebb_pcrels_fit (bfd *abfd,
        }
     }
 
-  if (xmap)
-    free_xlate_map (xmap);
+  free_xlate_map (xmap);
 
   return ok;
 }
@@ -8128,7 +8727,7 @@ text_action_add_proposed (text_action_list *l,
 
 
 int
-compute_fill_extra_space (property_table_entry *entry)
+xtensa_compute_fill_extra_space (property_table_entry *entry)
 {
   int fill_extra_space;
 
@@ -8161,7 +8760,7 @@ compute_fill_extra_space (property_table_entry *entry)
    add an entry to the per-section list of removed literals.  The
    actual changes are deferred until the next pass.  */
 
-static bfd_boolean 
+static bfd_boolean
 compute_removed_literals (bfd *abfd,
                          asection *sec,
                          struct bfd_link_info *link_info,
@@ -8188,7 +8787,7 @@ compute_removed_literals (bfd *abfd,
   if (!relax_info->is_relaxable_literal_section)
     return ok;
 
-  internal_relocs = retrieve_internal_relocs (abfd, sec, 
+  internal_relocs = retrieve_internal_relocs (abfd, sec,
                                              link_info->keep_memory);
 
   sec_size = bfd_get_section_limit (abfd, sec);
@@ -8240,14 +8839,14 @@ compute_removed_literals (bfd *abfd,
        continue;
       prev_i = i;
 
-      if (last_loc_is_prev && 
+      if (last_loc_is_prev &&
          last_target_offset + 4 != rel->r_rel.target_offset)
        last_loc_is_prev = FALSE;
 
       /* Check if the relocation was from an L32R that is being removed
         because a CALLX was converted to a direct CALL, and check if
         there are no other relocations to the literal.  */
-      if (is_removable_literal (rel, i, src_relocs, relax_info->src_count, 
+      if (is_removable_literal (rel, i, src_relocs, relax_info->src_count,
                                sec, prop_table, ptblsize))
        {
          if (!remove_dead_literal (abfd, sec, link_info, internal_relocs,
@@ -8261,8 +8860,8 @@ compute_removed_literals (bfd *abfd,
        }
 
       if (!identify_literal_placement (abfd, sec, contents, link_info,
-                                      values, 
-                                      &last_loc_is_prev, irel, 
+                                      values,
+                                      &last_loc_is_prev, irel,
                                       relax_info->src_count - i, rel,
                                       prop_table, ptblsize,
                                       &target_sec_cache, rel->is_abs_literal))
@@ -8278,9 +8877,9 @@ compute_removed_literals (bfd *abfd,
   print_action_list (stderr, &relax_info->action_list);
 #endif /* DEBUG */
 
-error_return:
-  if (prop_table) free (prop_table);
-  clear_section_cache (&target_sec_cache);
+ error_return:
+  free (prop_table);
+  free_section_cache (&target_sec_cache);
 
   release_contents (sec, contents);
   release_internal_relocs (sec, internal_relocs);
@@ -8298,7 +8897,7 @@ get_irel_at_offset (asection *sec,
   unsigned r_type;
   Elf_Internal_Rela key;
 
-  if (!internal_relocs) 
+  if (!internal_relocs)
     return NULL;
 
   key.r_offset = offset;
@@ -8342,8 +8941,8 @@ is_removable_literal (const source_reloc *rel,
 
   if (!rel->is_null)
     return FALSE;
-  
-  entry = elf_xtensa_find_property_entry (prop_table, ptblsize, 
+
+  entry = elf_xtensa_find_property_entry (prop_table, ptblsize,
                                          sec->vma + rel->r_rel.target_offset);
   if (entry && (entry->flags & XTENSA_PROP_NO_TRANSFORM))
     return FALSE;
@@ -8364,7 +8963,7 @@ is_removable_literal (const source_reloc *rel,
 }
 
 
-bfd_boolean 
+bfd_boolean
 remove_dead_literal (bfd *abfd,
                     asection *sec,
                     struct bfd_link_info *link_info,
@@ -8391,7 +8990,7 @@ remove_dead_literal (bfd *abfd,
                   ta_remove_literal, sec, rel->r_rel.target_offset, 4);
 
   /* If the section is 4-byte aligned, do not add fill.  */
-  if (sec->alignment_power > 2) 
+  if (sec->alignment_power > 2)
     {
       int fill_extra_space;
       bfd_vma entry_sec_offset;
@@ -8408,7 +9007,7 @@ remove_dead_literal (bfd *abfd,
         do not add fill.  */
       the_add_entry = elf_xtensa_find_property_entry (prop_table, ptblsize,
                                                      entry_sec_offset);
-      fill_extra_space = compute_fill_extra_space (the_add_entry);
+      fill_extra_space = xtensa_compute_fill_extra_space (the_add_entry);
 
       fa = find_fill_action (&relax_info->action_list, sec, entry_sec_offset);
       removed_diff = compute_removed_action_diff (fa, sec, entry_sec_offset,
@@ -8435,7 +9034,7 @@ remove_dead_literal (bfd *abfd,
 }
 
 
-bfd_boolean 
+bfd_boolean
 identify_literal_placement (bfd *abfd,
                            asection *sec,
                            bfd_byte *contents,
@@ -8466,7 +9065,7 @@ identify_literal_placement (bfd *abfd,
   sec_size = bfd_get_section_limit (abfd, sec);
 
   final_static_link =
-    (!link_info->relocatable
+    (!bfd_link_relocatable (link_info)
      && !elf_hash_table (link_info)->dynamic_sections_created);
 
   /* The placement algorithm first checks to see if the literal is
@@ -8505,7 +9104,7 @@ identify_literal_placement (bfd *abfd,
   /* For relocatable links, do not try to move literals.  To do it
      correctly might increase the number of relocations in an input
      section making the default relocatable linking fail.  */
-  if (!link_info->relocatable && !literal_placed 
+  if (!bfd_link_relocatable (link_info) && !literal_placed
       && values->has_last_loc && !(*last_loc_is_prev_p))
     {
       asection *target_sec = r_reloc_get_section (&values->last_loc);
@@ -8518,7 +9117,7 @@ identify_literal_placement (bfd *abfd,
          /* There is a last loc that was in the same output section.  */
          if (relocations_reach (rel, remaining_src_rels, &try_loc)
              && move_shared_literal (sec, link_info, rel,
-                                     prop_table, ptblsize, 
+                                     prop_table, ptblsize,
                                      &try_loc, &val, target_sec_cache))
            {
              values->last_loc.virtual_offset += 4;
@@ -8584,7 +9183,7 @@ relocations_reach (source_reloc *reloc,
        continue;
 
       /* The original and new output section for these must be the same
-         in order to coalesce.  */
+        in order to coalesce.  */
       if (r_reloc_get_section (&reloc[i].r_rel)->output_section
          != sec->output_section)
        return FALSE;
@@ -8618,7 +9217,7 @@ relocations_reach (source_reloc *reloc,
 /* Move a literal to another literal location because it is
    the same as the other literal value.  */
 
-static bfd_boolean 
+static bfd_boolean
 coalesce_shared_literal (asection *sec,
                         source_reloc *rel,
                         property_table_entry *prop_table,
@@ -8647,7 +9246,7 @@ coalesce_shared_literal (asection *sec,
                   ta_remove_literal, sec, rel->r_rel.target_offset, 4);
 
   /* If the section is 4-byte aligned, do not add fill.  */
-  if (sec->alignment_power > 2) 
+  if (sec->alignment_power > 2)
     {
       int fill_extra_space;
       bfd_vma entry_sec_offset;
@@ -8683,7 +9282,7 @@ coalesce_shared_literal (asection *sec,
    total amount of space used because of alignments so we need to do
    this carefully.  Also, it may make a branch go out of range.  */
 
-static bfd_boolean 
+static bfd_boolean
 move_shared_literal (asection *sec,
                     struct bfd_link_info *link_info,
                     source_reloc *rel,
@@ -8726,7 +9325,7 @@ move_shared_literal (asection *sec,
     return FALSE;
 
   target_entry = elf_xtensa_find_property_entry
-    (target_sec_cache->ptbl, target_sec_cache->pte_count, 
+    (target_sec_cache->ptbl, target_sec_cache->pte_count,
      target_sec->vma + target_loc->target_offset);
 
   if (!target_entry)
@@ -8737,7 +9336,7 @@ move_shared_literal (asection *sec,
 
   init_ebb_constraint (&ebb_table);
   ebb = &ebb_table.ebb;
-  init_ebb (ebb, target_sec_cache->sec, target_sec_cache->contents, 
+  init_ebb (ebb, target_sec_cache->sec, target_sec_cache->contents,
            target_sec_cache->content_length,
            target_sec_cache->ptbl, target_sec_cache->pte_count,
            target_sec_cache->relocs, target_sec_cache->reloc_count);
@@ -8749,24 +9348,24 @@ move_shared_literal (asection *sec,
                      -4 - (1 << target_sec->alignment_power), TRUE);
 
   /* Check all of the PC-relative relocations to make sure they still fit.  */
-  relocs_fit = check_section_ebb_pcrels_fit (target_sec->owner, target_sec, 
+  relocs_fit = check_section_ebb_pcrels_fit (target_sec->owner, target_sec,
                                             target_sec_cache->contents,
-                                            target_sec_cache->relocs,
+                                            target_sec_cache->relocs, NULL,
                                             &ebb_table, NULL);
 
-  if (!relocs_fit) 
+  if (!relocs_fit)
     return FALSE;
 
   text_action_add_literal (&target_relax_info->action_list,
                           ta_add_literal, target_loc, lit_value, -4);
 
-  if (target_sec->alignment_power > 2 && target_entry != src_entry) 
+  if (target_sec->alignment_power > 2 && target_entry != src_entry)
     {
       /* May need to add or remove some fill to maintain alignment.  */
       int fill_extra_space;
       bfd_vma entry_sec_offset;
 
-      entry_sec_offset = 
+      entry_sec_offset =
        target_entry->address - target_sec->vma + target_entry->size;
 
       /* If the literal range is at the end of the section,
@@ -8799,7 +9398,7 @@ move_shared_literal (asection *sec,
                   ta_remove_literal, sec, rel->r_rel.target_offset, 4);
 
   /* If the section is 4-byte aligned, do not add fill.  */
-  if (sec->alignment_power > 2 && target_entry != src_entry) 
+  if (sec->alignment_power > 2 && target_entry != src_entry)
     {
       int fill_extra_space;
       bfd_vma entry_sec_offset;
@@ -8833,6 +9432,16 @@ move_shared_literal (asection *sec,
 \f
 /* Second relaxation pass.  */
 
+static int
+action_remove_bytes_fn (splay_tree_node node, void *p)
+{
+  bfd_size_type *final_size = p;
+  text_action *action = (text_action *)node->value;
+
+  *final_size -= action->removed_bytes;
+  return 0;
+}
+
 /* Modify all of the relocations to point to the right spot, and if this
    is a relaxable section, delete the unwanted literals and fix the
    section size.  */
@@ -8863,9 +9472,9 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
       return relax_property_section (abfd, sec, link_info);
     }
 
-  internal_relocs = retrieve_internal_relocs (abfd, sec, 
+  internal_relocs = retrieve_internal_relocs (abfd, sec,
                                              link_info->keep_memory);
-  if (!internal_relocs && !relax_info->action_list.head)
+  if (!internal_relocs && !action_list_count (&relax_info->action_list))
     return TRUE;
 
   contents = retrieve_contents (abfd, sec, link_info->keep_memory);
@@ -8916,7 +9525,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
                  if (elf_hash_table (link_info)->dynamic_sections_created)
                    shrink_dynamic_reloc_sections (link_info, abfd, sec, irel);
                  irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE);
-                 irel->r_offset = offset_with_removed_text
+                 irel->r_offset = offset_with_removed_text_map
                    (&relax_info->action_list, irel->r_offset);
                  continue;
                }
@@ -8943,6 +9552,12 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
                        }
                      /* Update the action so that the code that moves
                         the contents will do the right thing.  */
+                     /* ta_remove_longcall and ta_remove_insn actions are
+                        grouped together in the tree as well as
+                        ta_convert_longcall and ta_none, so that changes below
+                        can be done w/o removing and reinserting action into
+                        the tree.  */
+
                      if (action->action == ta_remove_longcall)
                        action->action = ta_remove_insn;
                      else
@@ -8953,7 +9568,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
                    }
                }
 
-             source_offset = offset_with_removed_text
+             source_offset = offset_with_removed_text_map
                (&relax_info->action_list, irel->r_offset);
              irel->r_offset = source_offset;
            }
@@ -8970,9 +9585,9 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
             that here and adjust things accordingly.  */
          if (! elf_xtensa_ignore_discarded_relocs (sec)
              && elf_xtensa_action_discarded (sec) == PRETEND
-             && sec->sec_info_type != ELF_INFO_TYPE_STABS
+             && sec->sec_info_type != SEC_INFO_TYPE_STABS
              && target_sec != NULL
-             && elf_discarded_section (target_sec))
+             && discarded_section (target_sec))
            {
              /* It would be natural to call _bfd_elf_check_kept_section
                 here, but it's not exported from elflink.c.  It's also a
@@ -9021,9 +9636,16 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
 
              if (r_type == R_XTENSA_DIFF8
                  || r_type == R_XTENSA_DIFF16
-                 || r_type == R_XTENSA_DIFF32)
+                 || r_type == R_XTENSA_DIFF32
+                 || r_type == R_XTENSA_PDIFF8
+                 || r_type == R_XTENSA_PDIFF16
+                 || r_type == R_XTENSA_PDIFF32
+                 || r_type == R_XTENSA_NDIFF8
+                 || r_type == R_XTENSA_NDIFF16
+                 || r_type == R_XTENSA_NDIFF32)
                {
-                 bfd_vma diff_value = 0, new_end_offset, diff_mask = 0;
+                 bfd_signed_vma diff_value = 0;
+                 bfd_vma new_end_offset, diff_mask = 0;
 
                  if (bfd_get_section_limit (abfd, sec) < old_source_offset)
                    {
@@ -9036,20 +9658,46 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
                  switch (r_type)
                    {
                    case R_XTENSA_DIFF8:
+                     diff_mask = 0x7f;
                      diff_value =
-                       bfd_get_8 (abfd, &contents[old_source_offset]);
+                       bfd_get_signed_8 (abfd, &contents[old_source_offset]);
                      break;
                    case R_XTENSA_DIFF16:
+                     diff_mask = 0x7fff;
                      diff_value =
-                       bfd_get_16 (abfd, &contents[old_source_offset]);
+                       bfd_get_signed_16 (abfd, &contents[old_source_offset]);
                      break;
                    case R_XTENSA_DIFF32:
+                     diff_mask = 0x7fffffff;
+                     diff_value =
+                       bfd_get_signed_32 (abfd, &contents[old_source_offset]);
+                     break;
+                   case R_XTENSA_PDIFF8:
+                   case R_XTENSA_NDIFF8:
+                     diff_mask = 0xff;
+                     diff_value =
+                       bfd_get_8 (abfd, &contents[old_source_offset]);
+                     break;
+                   case R_XTENSA_PDIFF16:
+                   case R_XTENSA_NDIFF16:
+                     diff_mask = 0xffff;
+                     diff_value =
+                       bfd_get_16 (abfd, &contents[old_source_offset]);
+                     break;
+                   case R_XTENSA_PDIFF32:
+                   case R_XTENSA_NDIFF32:
+                     diff_mask = 0xffffffff;
                      diff_value =
                        bfd_get_32 (abfd, &contents[old_source_offset]);
                      break;
                    }
 
-                 new_end_offset = offset_with_removed_text
+                 if (r_type >= R_XTENSA_NDIFF8
+                     && r_type <= R_XTENSA_NDIFF32
+                     && diff_value)
+                   diff_value |= ~diff_mask;
+
+                 new_end_offset = offset_with_removed_text_map
                    (&target_relax_info->action_list,
                     r_rel.target_offset + diff_value);
                  diff_value = new_end_offset - new_reloc.target_offset;
@@ -9057,24 +9705,40 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
                  switch (r_type)
                    {
                    case R_XTENSA_DIFF8:
-                     diff_mask = 0xff;
-                     bfd_put_8 (abfd, diff_value,
+                     bfd_put_signed_8 (abfd, diff_value,
                                 &contents[old_source_offset]);
                      break;
                    case R_XTENSA_DIFF16:
-                     diff_mask = 0xffff;
-                     bfd_put_16 (abfd, diff_value,
+                     bfd_put_signed_16 (abfd, diff_value,
                                  &contents[old_source_offset]);
                      break;
                    case R_XTENSA_DIFF32:
-                     diff_mask = 0xffffffff;
+                     bfd_put_signed_32 (abfd, diff_value,
+                                 &contents[old_source_offset]);
+                     break;
+                   case R_XTENSA_PDIFF8:
+                   case R_XTENSA_NDIFF8:
+                     bfd_put_8 (abfd, diff_value,
+                                &contents[old_source_offset]);
+                     break;
+                   case R_XTENSA_PDIFF16:
+                   case R_XTENSA_NDIFF16:
+                     bfd_put_16 (abfd, diff_value,
+                                 &contents[old_source_offset]);
+                     break;
+                   case R_XTENSA_PDIFF32:
+                   case R_XTENSA_NDIFF32:
                      bfd_put_32 (abfd, diff_value,
                                  &contents[old_source_offset]);
                      break;
                    }
 
-                 /* Check for overflow.  */
-                 if ((diff_value & ~diff_mask) != 0)
+                 /* Check for overflow. Sign bits must be all zeroes or
+                    all ones.  When sign bits are all ones diff_value
+                    may not be zero.  */
+                 if (((diff_value & ~diff_mask) != 0
+                      && (diff_value & ~diff_mask) != ~diff_mask)
+                     || (diff_value && (bfd_vma) diff_value == ~diff_mask))
                    {
                      (*link_info->callbacks->reloc_dangerous)
                        (link_info, _("overflow after relaxation"),
@@ -9113,13 +9777,12 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
 
   if ((relax_info->is_relaxable_literal_section
        || relax_info->is_relaxable_asm_section)
-      && relax_info->action_list.head)
+      && action_list_count (&relax_info->action_list))
     {
       /* Walk through the planned actions and build up a table
         of move, copy and fill records.  Use the move, copy and
         fill records to perform the actions once.  */
 
-      int removed = 0;
       bfd_size_type final_size, copy_size, orig_insn_size;
       bfd_byte *scratch = NULL;
       bfd_byte *dup_contents = NULL;
@@ -9130,15 +9793,12 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
       bfd_vma orig_dot_vo = 0; /* Virtual offset from orig_dot.  */
       bfd_vma dup_dot = 0;
 
-      text_action *action = relax_info->action_list.head;
+      text_action *action;
 
       final_size = sec->size;
-      for (action = relax_info->action_list.head; action;
-          action = action->next)
-       {
-         final_size -= action->removed_bytes;
-       }
 
+      splay_tree_foreach (relax_info->action_list.tree,
+                         action_remove_bytes_fn, &final_size);
       scratch = (bfd_byte *) bfd_zmalloc (final_size);
       dup_contents = (bfd_byte *) bfd_zmalloc (final_size);
 
@@ -9147,8 +9807,8 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
       print_action_list (stderr, &relax_info->action_list);
 #endif
 
-      for (action = relax_info->action_list.head; action;
-          action = action->next)
+      for (action = action_first (&relax_info->action_list); action;
+          action = action_next (&relax_info->action_list, action))
        {
          virtual_action = FALSE;
          if (action->offset > orig_dot)
@@ -9193,7 +9853,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
                      dup_dot += copy_size;
                    }
                  virtual_action = TRUE;
-               } 
+               }
              else
                BFD_ASSERT (action->virtual_offset <= orig_dot_vo);
            }
@@ -9264,7 +9924,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
                                 relax_info, &internal_relocs, &action->value))
                goto error_return;
 
-             if (virtual_action) 
+             if (virtual_action)
                orig_dot_vo += copy_size;
 
              orig_dot += orig_insn_size;
@@ -9277,7 +9937,6 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
              break;
            }
 
-         removed += action->removed_bytes;
          BFD_ASSERT (dup_dot <= final_size);
          BFD_ASSERT (orig_dot <= orig_size);
        }
@@ -9329,7 +9988,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
 }
 
 
-static bfd_boolean 
+static bfd_boolean
 translate_section_fixes (asection *sec)
 {
   xtensa_relax_info *relax_info;
@@ -9350,7 +10009,7 @@ translate_section_fixes (asection *sec)
 /* Translate a fix given the mapping in the relax info for the target
    section.  If it has already been translated, no work is required.  */
 
-static bfd_boolean 
+static bfd_boolean
 translate_reloc_bfd_fix (reloc_bfd_fix *fix)
 {
   reloc_bfd_fix new_fix;
@@ -9396,7 +10055,7 @@ translate_reloc_bfd_fix (reloc_bfd_fix *fix)
                                      target_offset);
     }
 
-  if (removed) 
+  if (removed)
     {
       asection *new_sec;
 
@@ -9406,11 +10065,11 @@ translate_reloc_bfd_fix (reloc_bfd_fix *fix)
 
       /* This was moved to some other address (possibly another section).  */
       new_sec = r_reloc_get_section (&removed->to);
-      if (new_sec != sec) 
+      if (new_sec != sec)
        {
          sec = new_sec;
          relax_info = get_xtensa_relax_info (sec);
-         if (!relax_info || 
+         if (!relax_info ||
              (!relax_info->is_relaxable_literal_section
               && !relax_info->is_relaxable_asm_section))
            {
@@ -9446,7 +10105,6 @@ translate_reloc (const r_reloc *orig_rel, r_reloc *new_rel, asection *sec)
   xtensa_relax_info *relax_info;
   removed_literal *removed;
   bfd_vma target_offset, base_offset;
-  text_action *act;
 
   *new_rel = *orig_rel;
 
@@ -9499,19 +10157,26 @@ translate_reloc (const r_reloc *orig_rel, r_reloc *new_rel, asection *sec)
      offset.  */
 
   base_offset = r_reloc_get_target_offset (new_rel) - new_rel->rela.r_addend;
-  act = relax_info->action_list.head;
   if (base_offset <= target_offset)
     {
-      int base_removed = removed_by_actions (&act, base_offset, FALSE);
-      int addend_removed = removed_by_actions (&act, target_offset, FALSE);
+      int base_removed = removed_by_actions_map (&relax_info->action_list,
+                                                base_offset, FALSE);
+      int addend_removed = removed_by_actions_map (&relax_info->action_list,
+                                                  target_offset, FALSE) -
+       base_removed;
+
       new_rel->target_offset = target_offset - base_removed - addend_removed;
       new_rel->rela.r_addend -= addend_removed;
     }
   else
     {
       /* Handle a negative addend.  The base offset comes first.  */
-      int tgt_removed = removed_by_actions (&act, target_offset, FALSE);
-      int addend_removed = removed_by_actions (&act, base_offset, FALSE);
+      int tgt_removed = removed_by_actions_map (&relax_info->action_list,
+                                               target_offset, FALSE);
+      int addend_removed = removed_by_actions_map (&relax_info->action_list,
+                                                  base_offset, FALSE) -
+       tgt_removed;
+
       new_rel->target_offset = target_offset - tgt_removed;
       new_rel->rela.r_addend += addend_removed;
     }
@@ -9562,18 +10227,20 @@ shrink_dynamic_reloc_sections (struct bfd_link_info *info,
 
   if ((r_type == R_XTENSA_32 || r_type == R_XTENSA_PLT)
       && (input_section->flags & SEC_ALLOC) != 0
-      && (dynamic_symbol || info->shared))
+      && (dynamic_symbol
+         || (bfd_link_pic (info)
+             && (!h || h->root.type != bfd_link_hash_undefweak))))
     {
       asection *srel;
       bfd_boolean is_plt = FALSE;
 
       if (dynamic_symbol && r_type == R_XTENSA_PLT)
        {
-         srel = htab->srelplt;
+         srel = htab->elf.srelplt;
          is_plt = TRUE;
        }
       else
-       srel = htab->srelgot;
+       srel = htab->elf.srelgot;
 
       /* Reduce size of the .rela.* section by one reloc.  */
       BFD_ASSERT (srel != NULL);
@@ -9602,7 +10269,7 @@ shrink_dynamic_reloc_sections (struct bfd_link_info *info,
          if (reloc_index % PLT_ENTRIES_PER_CHUNK == 0)
            {
              /* The two magic GOT entries for that chunk can go away.  */
-             srelgot = htab->srelgot;
+             srelgot = htab->elf.srelgot;
              BFD_ASSERT (srelgot != NULL);
              srelgot->reloc_count -= 2;
              srelgot->size -= 2 * sizeof (Elf32_External_Rela);
@@ -9668,7 +10335,7 @@ move_literal (bfd *abfd,
       bfd_put_32 (abfd, lit->value, contents + offset);
 
       /* Currently, we cannot move relocations during a relocatable link.  */
-      BFD_ASSERT (!link_info->relocatable);
+      BFD_ASSERT (!bfd_link_relocatable (link_info));
       fix = reloc_bfd_fix_init (sec, offset, r_type,
                                r_reloc_get_section (r_rel),
                                r_rel->target_offset + r_rel->virtual_offset,
@@ -9699,7 +10366,7 @@ move_literal (bfd *abfd,
          BFD_ASSERT (relax_info->allocated_relocs == NULL
                      || sec->reloc_count == relax_info->relocs_count);
 
-         if (relax_info->allocated_relocs_count == 0) 
+         if (relax_info->allocated_relocs_count == 0)
            new_relocs_count = (sec->reloc_count + 2) * 2;
          else
            new_relocs_count = (relax_info->allocated_relocs_count + 2) * 2;
@@ -9719,7 +10386,7 @@ move_literal (bfd *abfd,
          if (insert_at != sec->reloc_count)
            memcpy (new_relocs + insert_at + 1,
                    (*internal_relocs_p) + insert_at,
-                   (sec->reloc_count - insert_at) 
+                   (sec->reloc_count - insert_at)
                    * sizeof (Elf_Internal_Rela));
 
          if (*internal_relocs_p != relax_info->allocated_relocs)
@@ -9778,7 +10445,7 @@ relax_property_section (bfd *abfd,
   bfd_size_type entry_size;
 
   sec_size = bfd_get_section_limit (abfd, sec);
-  internal_relocs = retrieve_internal_relocs (abfd, sec, 
+  internal_relocs = retrieve_internal_relocs (abfd, sec,
                                              link_info->keep_memory);
   contents = retrieve_contents (abfd, sec, link_info->keep_memory);
   if (contents == NULL && sec_size != 0)
@@ -9834,9 +10501,10 @@ relax_property_section (bfd *abfd,
              bfd_vma old_offset = val.r_rel.target_offset;
              bfd_vma new_offset;
              long old_size, new_size;
-             text_action *act = target_relax_info->action_list.head;
-             new_offset = old_offset -
-               removed_by_actions (&act, old_offset, FALSE);
+             int removed_by_old_offset =
+               removed_by_actions_map (&target_relax_info->action_list,
+                                       old_offset, FALSE);
+             new_offset = old_offset - removed_by_old_offset;
 
              /* Assert that we are not out of bounds.  */
              old_size = bfd_get_32 (abfd, size_p);
@@ -9860,9 +10528,10 @@ relax_property_section (bfd *abfd,
 
                      /* Recompute the new_offset, but this time don't
                         include any fill inserted by relaxation.  */
-                     act = target_relax_info->action_list.head;
-                     new_offset = old_offset -
-                       removed_by_actions (&act, old_offset, TRUE);
+                     removed_by_old_offset =
+                       removed_by_actions_map (&target_relax_info->action_list,
+                                               old_offset, TRUE);
+                     new_offset = old_offset - removed_by_old_offset;
 
                      /* If it is not unreachable and we have not yet
                         seen an unreachable at this address, place it
@@ -9878,8 +10547,12 @@ relax_property_section (bfd *abfd,
                    }
                }
              else
-               new_size -=
-                   removed_by_actions (&act, old_offset + old_size, TRUE);
+               {
+                 int removed_by_old_offset_size =
+                   removed_by_actions_map (&target_relax_info->action_list,
+                                           old_offset + old_size, TRUE);
+                 new_size -= removed_by_old_offset_size - removed_by_old_offset;
+               }
 
              if (new_size != old_size)
                {
@@ -9901,7 +10574,7 @@ relax_property_section (bfd *abfd,
      finish_dynamic_sections() but at that point it's too late to
      reclaim the space in the output section, so we do this twice.  */
 
-  if (internal_relocs && (!link_info->relocatable
+  if (internal_relocs && (!bfd_link_relocatable (link_info)
                          || xtensa_is_littable_section (sec)))
     {
       Elf_Internal_Rela *last_irel = NULL;
@@ -9913,7 +10586,7 @@ relax_property_section (bfd *abfd,
       predef_flags = xtensa_get_property_predef_flags (sec);
 
       /* Walk over memory and relocations at the same time.
-         This REQUIRES that the internal_relocs be sorted by offset.  */
+        This REQUIRES that the internal_relocs be sorted by offset.  */
       qsort (internal_relocs, sec->reloc_count, sizeof (Elf_Internal_Rela),
             internal_reloc_compare);
 
@@ -10003,7 +10676,7 @@ relax_property_section (bfd *abfd,
          actual_offset = offset - removed_bytes;
          size = bfd_get_32 (abfd, &contents[actual_offset + 4]);
 
-         if (is_full_prop_section) 
+         if (is_full_prop_section)
            flags = bfd_get_32 (abfd, &contents[actual_offset + 8]);
          else
            flags = predef_flags;
@@ -10031,7 +10704,7 @@ relax_property_section (bfd *abfd,
                  bfd_vma new_address =
                    (offset_rel->r_addend
                     + bfd_get_32 (abfd, &contents[actual_offset]));
-                 if (is_full_prop_section) 
+                 if (is_full_prop_section)
                    old_flags = bfd_get_32
                      (abfd, &contents[last_irel->r_offset + 8]);
                  else
@@ -10137,14 +10810,16 @@ relax_section_symbols (bfd *abfd, asection *sec)
 
       if (isym->st_shndx == sec_shndx)
        {
-         text_action *act = relax_info->action_list.head;
          bfd_vma orig_addr = isym->st_value;
+         int removed = removed_by_actions_map (&relax_info->action_list,
+                                               orig_addr, FALSE);
 
-         isym->st_value -= removed_by_actions (&act, orig_addr, FALSE);
-
+         isym->st_value -= removed;
          if (ELF32_ST_TYPE (isym->st_info) == STT_FUNC)
            isym->st_size -=
-             removed_by_actions (&act, orig_addr + isym->st_size, FALSE);
+             removed_by_actions_map (&relax_info->action_list,
+                                     orig_addr + isym->st_size, FALSE) -
+             removed;
        }
     }
 
@@ -10162,15 +10837,17 @@ relax_section_symbols (bfd *abfd, asection *sec)
           || sym_hash->root.type == bfd_link_hash_defweak)
          && sym_hash->root.u.def.section == sec)
        {
-         text_action *act = relax_info->action_list.head;
          bfd_vma orig_addr = sym_hash->root.u.def.value;
+         int removed = removed_by_actions_map (&relax_info->action_list,
+                                               orig_addr, FALSE);
 
-         sym_hash->root.u.def.value -=
-           removed_by_actions (&act, orig_addr, FALSE);
+         sym_hash->root.u.def.value -= removed;
 
          if (sym_hash->type == STT_FUNC)
            sym_hash->size -=
-             removed_by_actions (&act, orig_addr + sym_hash->size, FALSE);
+             removed_by_actions_map (&relax_info->action_list,
+                                     orig_addr + sym_hash->size, FALSE) -
+             removed;
        }
     }
 
@@ -10208,9 +10885,10 @@ do_fix_for_relocatable_link (Elf_Internal_Rela *rel,
     {
       if (r_type != R_XTENSA_ASM_EXPAND)
        {
-         (*_bfd_error_handler)
-           (_("%B(%A+0x%lx): unexpected fix for %s relocation"),
-            input_bfd, input_section, rel->r_offset,
+         _bfd_error_handler
+           /* xgettext:c-format */
+           (_("%pB(%pA+%#" PRIx64 "): unexpected fix for %s relocation"),
+            input_bfd, input_section, (uint64_t) rel->r_offset,
             elf_howto_table[r_type].name);
          return FALSE;
        }
@@ -10268,43 +10946,30 @@ do_fix_for_final_link (Elf_Internal_Rela *rel,
 static asection *
 elf_xtensa_get_plt_section (struct bfd_link_info *info, int chunk)
 {
-  struct elf_xtensa_link_hash_table *htab;
   bfd *dynobj;
-  char plt_name[10];
+  char plt_name[17];
 
   if (chunk == 0)
-    {
-      htab = elf_xtensa_hash_table (info);
-      if (htab == NULL)
-       return NULL;
-
-      return htab->splt;
-    }
+    return elf_hash_table (info)->splt;
 
   dynobj = elf_hash_table (info)->dynobj;
   sprintf (plt_name, ".plt.%u", chunk);
-  return bfd_get_section_by_name (dynobj, plt_name);
+  return bfd_get_linker_section (dynobj, plt_name);
 }
 
 
 static asection *
 elf_xtensa_get_gotplt_section (struct bfd_link_info *info, int chunk)
 {
-  struct elf_xtensa_link_hash_table *htab;
   bfd *dynobj;
-  char got_name[14];
+  char got_name[21];
 
   if (chunk == 0)
-    {
-      htab = elf_xtensa_hash_table (info);
-      if (htab == NULL)
-       return NULL;
-      return htab->sgotplt;
-    }
+    return elf_hash_table (info)->sgotplt;
 
   dynobj = elf_hash_table (info)->dynobj;
   sprintf (got_name, ".got.plt.%u", chunk);
-  return bfd_get_section_by_name (dynobj, got_name);
+  return bfd_get_linker_section (dynobj, got_name);
 }
 
 
@@ -10344,8 +11009,8 @@ get_elf_r_symndx_section (bfd *abfd, unsigned long r_symndx)
       struct elf_link_hash_entry *h = elf_sym_hashes (abfd)[indx];
 
       while (h->root.type == bfd_link_hash_indirect
-             || h->root.type == bfd_link_hash_warning)
-        h = (struct elf_link_hash_entry *) h->root.u.i.link;
+            || h->root.type == bfd_link_hash_warning)
+       h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
       switch (h->root.type)
        {
@@ -10409,10 +11074,10 @@ get_elf_r_symndx_offset (bfd *abfd, unsigned long r_symndx)
        elf_sym_hashes (abfd)[indx];
 
       while (h->root.type == bfd_link_hash_indirect
-             || h->root.type == bfd_link_hash_warning)
+            || h->root.type == bfd_link_hash_warning)
        h = (struct elf_link_hash_entry *) h->root.u.i.link;
       if (h->root.type == bfd_link_hash_defined
-          || h->root.type == bfd_link_hash_defweak)
+         || h->root.type == bfd_link_hash_defweak)
        offset = h->root.u.def.value;
     }
   return offset;
@@ -10447,7 +11112,7 @@ pcrel_reloc_fits (xtensa_opcode opc,
 }
 
 
-static bfd_boolean 
+static bfd_boolean
 xtensa_is_property_section (asection *sec)
 {
   if (xtensa_is_insntable_section (sec)
@@ -10459,7 +11124,7 @@ xtensa_is_property_section (asection *sec)
 }
 
 
-static bfd_boolean 
+static bfd_boolean
 xtensa_is_insntable_section (asection *sec)
 {
   if (CONST_STRNEQ (sec->name, XTENSA_INSN_SEC_NAME)
@@ -10470,7 +11135,7 @@ xtensa_is_insntable_section (asection *sec)
 }
 
 
-static bfd_boolean 
+static bfd_boolean
 xtensa_is_littable_section (asection *sec)
 {
   if (CONST_STRNEQ (sec->name, XTENSA_LIT_SEC_NAME)
@@ -10481,7 +11146,7 @@ xtensa_is_littable_section (asection *sec)
 }
 
 
-static bfd_boolean 
+static bfd_boolean
 xtensa_is_proptable_section (asection *sec)
 {
   if (CONST_STRNEQ (sec->name, XTENSA_PROP_SEC_NAME)
@@ -10534,7 +11199,7 @@ match_section_group (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
 {
   const char *gname = inf;
   const char *group_name = elf_group_name (sec);
-  
+
   return (group_name == gname
          || (group_name != NULL
              && gname != NULL
@@ -10542,10 +11207,30 @@ match_section_group (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
 }
 
 
+static char *
+xtensa_add_names (const char *base, const char *suffix)
+{
+  if (suffix)
+    {
+      size_t base_len = strlen (base);
+      size_t suffix_len = strlen (suffix);
+      char *str = bfd_malloc (base_len + suffix_len + 1);
+
+      memcpy (str, base, base_len);
+      memcpy (str + base_len, suffix, suffix_len + 1);
+      return str;
+    }
+  else
+    {
+      return strdup (base);
+    }
+}
+
 static int linkonce_len = sizeof (".gnu.linkonce.") - 1;
 
 static char *
-xtensa_property_section_name (asection *sec, const char *base_name)
+xtensa_property_section_name (asection *sec, const char *base_name,
+                             bfd_boolean separate_sections)
 {
   const char *suffix, *group_name;
   char *prop_sec_name;
@@ -10556,19 +11241,15 @@ xtensa_property_section_name (asection *sec, const char *base_name)
       suffix = strrchr (sec->name, '.');
       if (suffix == sec->name)
        suffix = 0;
-      prop_sec_name = (char *) bfd_malloc (strlen (base_name) + 1
-                                          + (suffix ? strlen (suffix) : 0));
-      strcpy (prop_sec_name, base_name);
-      if (suffix)
-       strcat (prop_sec_name, suffix);
+      prop_sec_name = xtensa_add_names (base_name, suffix);
     }
   else if (strncmp (sec->name, ".gnu.linkonce.", linkonce_len) == 0)
     {
       char *linkonce_kind = 0;
 
-      if (strcmp (base_name, XTENSA_INSN_SEC_NAME) == 0) 
+      if (strcmp (base_name, XTENSA_INSN_SEC_NAME) == 0)
        linkonce_kind = "x.";
-      else if (strcmp (base_name, XTENSA_LIT_SEC_NAME) == 0) 
+      else if (strcmp (base_name, XTENSA_LIT_SEC_NAME) == 0)
        linkonce_kind = "p.";
       else if (strcmp (base_name, XTENSA_PROP_SEC_NAME) == 0)
        linkonce_kind = "prop.";
@@ -10582,25 +11263,30 @@ xtensa_property_section_name (asection *sec, const char *base_name)
 
       suffix = sec->name + linkonce_len;
       /* For backward compatibility, replace "t." instead of inserting
-         the new linkonce_kind (but not for "prop" sections).  */
+        the new linkonce_kind (but not for "prop" sections).  */
       if (CONST_STRNEQ (suffix, "t.") && linkonce_kind[1] == '.')
-        suffix += 2;
+       suffix += 2;
       strcat (prop_sec_name + linkonce_len, suffix);
     }
   else
-    prop_sec_name = strdup (base_name);
+    {
+      prop_sec_name = xtensa_add_names (base_name,
+                                       separate_sections ? sec->name : NULL);
+    }
 
   return prop_sec_name;
 }
 
 
 static asection *
-xtensa_get_property_section (asection *sec, const char *base_name)
+xtensa_get_separate_property_section (asection *sec, const char *base_name,
+                                     bfd_boolean separate_section)
 {
   char *prop_sec_name;
   asection *prop_sec;
 
-  prop_sec_name = xtensa_property_section_name (sec, base_name);
+  prop_sec_name = xtensa_property_section_name (sec, base_name,
+                                               separate_section);
   prop_sec = bfd_get_section_by_name_if (sec->owner, prop_sec_name,
                                         match_section_group,
                                         (void *) elf_group_name (sec));
@@ -10608,6 +11294,21 @@ xtensa_get_property_section (asection *sec, const char *base_name)
   return prop_sec;
 }
 
+static asection *
+xtensa_get_property_section (asection *sec, const char *base_name)
+{
+  asection *prop_sec;
+
+  /* Try individual property section first.  */
+  prop_sec = xtensa_get_separate_property_section (sec, base_name, TRUE);
+
+  /* Refer to a common property section if individual is not present.  */
+  if (!prop_sec)
+    prop_sec = xtensa_get_separate_property_section (sec, base_name, FALSE);
+
+  return prop_sec;
+}
+
 
 asection *
 xtensa_make_property_section (asection *sec, const char *base_name)
@@ -10616,7 +11317,8 @@ xtensa_make_property_section (asection *sec, const char *base_name)
   asection *prop_sec;
 
   /* Check if the section already exists.  */
-  prop_sec_name = xtensa_property_section_name (sec, base_name);
+  prop_sec_name = xtensa_property_section_name (sec, base_name,
+                                               elf32xtensa_separate_props);
   prop_sec = bfd_get_section_by_name_if (sec->owner, prop_sec_name,
                                         match_section_group,
                                         (void *) elf_group_name (sec));
@@ -10624,7 +11326,7 @@ xtensa_make_property_section (asection *sec, const char *base_name)
   if (! prop_sec)
     {
       flagword flags = (SEC_RELOC | SEC_HAS_CONTENTS | SEC_READONLY);
-      flags |= (bfd_get_section_flags (sec->owner, sec)
+      flags |= (bfd_section_flags (sec)
                & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES));
 
       prop_sec = bfd_make_section_anyway_with_flags
@@ -10683,7 +11385,7 @@ xtensa_callback_required_dependence (bfd *abfd,
 
       /* Find the corresponding ".got.plt*" section.  */
       if (sec->name[4] == '\0')
-       sgotplt = bfd_get_section_by_name (sec->owner, ".got.plt");
+       sgotplt = elf_hash_table (link_info)->sgotplt;
       else
        {
          char got_name[14];
@@ -10693,7 +11395,7 @@ xtensa_callback_required_dependence (bfd *abfd,
          chunk = strtol (&sec->name[5], NULL, 10);
 
          sprintf (got_name, ".got.plt.%u", chunk);
-         sgotplt = bfd_get_section_by_name (sec->owner, got_name);
+         sgotplt = bfd_get_linker_section (sec->owner, got_name);
        }
       BFD_ASSERT (sgotplt);
 
@@ -10708,7 +11410,7 @@ xtensa_callback_required_dependence (bfd *abfd,
   if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
     return ok;
 
-  internal_relocs = retrieve_internal_relocs (abfd, sec, 
+  internal_relocs = retrieve_internal_relocs (abfd, sec,
                                              link_info->keep_memory);
   if (internal_relocs == NULL
       || sec->reloc_count == 0)
@@ -10761,26 +11463,23 @@ static const struct bfd_elf_special_section elf_xtensa_special_sections[] =
 {
   { STRING_COMMA_LEN (".fini.literal"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
   { STRING_COMMA_LEN (".init.literal"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { STRING_COMMA_LEN (".literal"),      0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { STRING_COMMA_LEN (".xtensa.info"),  0, SHT_NOTE,     0 },
-  { NULL,                       0,      0, 0,            0 }
+  { STRING_COMMA_LEN (".literal"),     0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
+  { STRING_COMMA_LEN (".xtensa.info"), 0, SHT_NOTE,     0 },
+  { NULL,                      0,      0, 0,            0 }
 };
 \f
+#define ELF_TARGET_ID                  XTENSA_ELF_DATA
 #ifndef ELF_ARCH
-#define TARGET_LITTLE_SYM              bfd_elf32_xtensa_le_vec
+#define TARGET_LITTLE_SYM              xtensa_elf32_le_vec
 #define TARGET_LITTLE_NAME             "elf32-xtensa-le"
-#define TARGET_BIG_SYM                 bfd_elf32_xtensa_be_vec
+#define TARGET_BIG_SYM                 xtensa_elf32_be_vec
 #define TARGET_BIG_NAME                        "elf32-xtensa-be"
 #define ELF_ARCH                       bfd_arch_xtensa
 
 #define ELF_MACHINE_CODE               EM_XTENSA
 #define ELF_MACHINE_ALT1               EM_XTENSA_OLD
 
-#if XCHAL_HAVE_MMU
-#define ELF_MAXPAGESIZE                        (1 << XCHAL_MMU_MIN_PTE_PAGE_SIZE)
-#else /* !XCHAL_HAVE_MMU */
-#define ELF_MAXPAGESIZE                        1
-#endif /* !XCHAL_HAVE_MMU */
+#define ELF_MAXPAGESIZE                        0x1000
 #endif /* ELF_ARCH */
 
 #define elf_backend_can_gc_sections    1
@@ -10789,6 +11488,7 @@ static const struct bfd_elf_special_section elf_xtensa_special_sections[] =
 #define elf_backend_got_header_size    4
 #define elf_backend_want_dynbss                0
 #define elf_backend_want_got_plt       1
+#define elf_backend_dtrel_excludes_plt 1
 
 #define elf_info_to_howto                   elf_xtensa_info_to_howto_rela
 
@@ -10813,7 +11513,6 @@ static const struct bfd_elf_special_section elf_xtensa_special_sections[] =
 #define elf_backend_finish_dynamic_sections  elf_xtensa_finish_dynamic_sections
 #define elf_backend_finish_dynamic_symbol    elf_xtensa_finish_dynamic_symbol
 #define elf_backend_gc_mark_hook            elf_xtensa_gc_mark_hook
-#define elf_backend_gc_sweep_hook           elf_xtensa_gc_sweep_hook
 #define elf_backend_grok_prstatus           elf_xtensa_grok_prstatus
 #define elf_backend_grok_psinfo                     elf_xtensa_grok_psinfo
 #define elf_backend_hide_symbol                     elf_xtensa_hide_symbol
@@ -10822,8 +11521,7 @@ static const struct bfd_elf_special_section elf_xtensa_special_sections[] =
 #define elf_backend_relocate_section        elf_xtensa_relocate_section
 #define elf_backend_size_dynamic_sections    elf_xtensa_size_dynamic_sections
 #define elf_backend_always_size_sections     elf_xtensa_always_size_sections
-#define elf_backend_omit_section_dynsym \
-  ((bfd_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true)
+#define elf_backend_omit_section_dynsym      _bfd_elf_omit_section_dynsym_all
 #define elf_backend_special_sections        elf_xtensa_special_sections
 #define elf_backend_action_discarded        elf_xtensa_action_discarded
 #define elf_backend_copy_indirect_symbol     elf_xtensa_copy_indirect_symbol
This page took 0.088876 seconds and 4 git commands to generate.