]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - bfd/elf32-xtensa.c
Automatic date update in version.in
[thirdparty/binutils-gdb.git] / bfd / elf32-xtensa.c
index ebfaf64841caab0cf4db9459ba6e1bcc1c9c5979..05c4f8430a2046a9c48546c815f0eb2ebdb2f728 100644 (file)
@@ -1,11 +1,11 @@
 /* Xtensa-specific support for 32-bit ELF.
-   Copyright 2003, 2004, 2005 Free Software Foundation, Inc.
+   Copyright (C) 2003-2020 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of the
+   published by the Free Software Foundation; either version 3 of the
    License, or (at your option) any later version.
 
    This program is distributed in the hope that it will be useful, but
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
    02110-1301, USA.  */
 
-#include "bfd.h"
 #include "sysdep.h"
+#include "bfd.h"
 
-#ifdef ANSI_PROTOTYPES
 #include <stdarg.h>
-#else
-#include <varargs.h>
-#endif
 #include <strings.h>
 
 #include "bfdlink.h"
 #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.  */
 
-static bfd_boolean add_extra_plt_sections (bfd *, int);
-static char *build_encoding_error_message (xtensa_opcode, bfd_vma);
+static bfd_boolean add_extra_plt_sections (struct bfd_link_info *, int);
+static char *vsprint_msg (const char *, const char *, int, ...) ATTRIBUTE_PRINTF(2,4);
 static bfd_reloc_status_type bfd_elf_xtensa_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_boolean do_fix_for_relocatable_link
@@ -66,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
@@ -98,8 +100,8 @@ static Elf_Internal_Sym *retrieve_local_syms (bfd *);
 
 /* Miscellaneous utility functions.  */
 
-static asection *elf_xtensa_get_plt_section (bfd *, int);
-static asection *elf_xtensa_get_gotplt_section (bfd *, int);
+static asection *elf_xtensa_get_plt_section (struct bfd_link_info *, int);
+static asection *elf_xtensa_get_gotplt_section (struct bfd_link_info *, int);
 static asection *get_elf_r_symndx_section (bfd *, unsigned long);
 static struct elf_link_hash_entry *get_elf_r_symndx_hash_entry
   (bfd *, unsigned long);
@@ -107,10 +109,12 @@ static bfd_vma get_elf_r_symndx_offset (bfd *, unsigned long);
 static bfd_boolean is_reloc_sym_weak (bfd *, Elf_Internal_Rela *);
 static bfd_boolean pcrel_reloc_fits (xtensa_opcode, int, bfd_vma, bfd_vma);
 static bfd_boolean xtensa_is_property_section (asection *);
+static bfd_boolean xtensa_is_insntable_section (asection *);
 static bfd_boolean xtensa_is_littable_section (asection *);
+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 *);
-extern char *xtensa_get_property_section_name (asection *, const char *);
+static asection *xtensa_get_property_section (asection *, const char *);
 static flagword xtensa_get_property_predef_flags (asection *);
 
 /* Other functions called directly by the linker.  */
@@ -136,16 +140,6 @@ int elf32xtensa_size_opt;
 typedef struct xtensa_relax_info_struct xtensa_relax_info;
 
 
-/* Total count of PLT relocations seen during check_relocs.
-   The actual PLT code must be split into multiple sections and all
-   the sections have to be created before size_dynamic_sections,
-   where we figure out the exact number of PLT entries that will be
-   needed.  It is OK if this count is an overestimate, e.g., some
-   relocations may be removed by GC.  */
-
-static int plt_reloc_count = 0;
-
-
 /* The GNU tools do not easily allow extending interfaces to pass around
    the pointer to the Xtensa ISA information, so instead we add a global
    variable here (in BFD) that can be used by any of the tools that need
@@ -165,168 +159,186 @@ 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.  */
+#define reloc_done sec_flg0
 \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, 0x00000000, 0x00000000, FALSE),
+        FALSE, 00, FALSE),
   HOWTO (R_XTENSA_32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
         bfd_elf_xtensa_reloc, "R_XTENSA_32",
         TRUE, 0xffffffff, 0xffffffff, FALSE),
+
   /* Replace a 32-bit value with a value from the runtime linker (only
      used by linker-generated stub functions).  The r_addend value is
      special: 1 means to substitute a pointer to the runtime linker's
      dynamic resolver function; 2 means to substitute the link map for
      the shared object.  */
   HOWTO (R_XTENSA_RTLD, 0, 2, 32, FALSE, 0, complain_overflow_dont,
-        NULL, "R_XTENSA_RTLD",
-        FALSE, 0x00000000, 0x00000000, FALSE),
+        NULL, "R_XTENSA_RTLD", FALSE, 0, 0, FALSE),
+
   HOWTO (R_XTENSA_GLOB_DAT, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
         bfd_elf_generic_reloc, "R_XTENSA_GLOB_DAT",
-        FALSE, 0xffffffff, 0xffffffff, FALSE),
+        FALSE, 0, 0xffffffff, FALSE),
   HOWTO (R_XTENSA_JMP_SLOT, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
         bfd_elf_generic_reloc, "R_XTENSA_JMP_SLOT",
-        FALSE, 0xffffffff, 0xffffffff, FALSE),
+        FALSE, 0, 0xffffffff, FALSE),
   HOWTO (R_XTENSA_RELATIVE, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
         bfd_elf_generic_reloc, "R_XTENSA_RELATIVE",
-        FALSE, 0xffffffff, 0xffffffff, FALSE),
+        FALSE, 0, 0xffffffff, FALSE),
   HOWTO (R_XTENSA_PLT, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
         bfd_elf_xtensa_reloc, "R_XTENSA_PLT",
-        FALSE, 0xffffffff, 0xffffffff, FALSE),
+        FALSE, 0, 0xffffffff, FALSE),
+
   EMPTY_HOWTO (7),
+
+  /* Old relocations for backward compatibility.  */
   HOWTO (R_XTENSA_OP0, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_OP0",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_OP0", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_OP1, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_OP1",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_OP1", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_OP2, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_OP2",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_OP2", FALSE, 0, 0, TRUE),
+
   /* Assembly auto-expansion.  */
   HOWTO (R_XTENSA_ASM_EXPAND, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_ASM_EXPAND",
-        FALSE, 0x00000000, 0x00000000, FALSE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_ASM_EXPAND", FALSE, 0, 0, TRUE),
   /* Relax assembly auto-expansion.  */
   HOWTO (R_XTENSA_ASM_SIMPLIFY, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_ASM_SIMPLIFY",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_ASM_SIMPLIFY", FALSE, 0, 0, TRUE),
+
   EMPTY_HOWTO (13),
-  EMPTY_HOWTO (14),
+
+  HOWTO (R_XTENSA_32_PCREL, 0, 2, 32, TRUE, 0, complain_overflow_bitfield,
+        bfd_elf_xtensa_reloc, "R_XTENSA_32_PCREL",
+        FALSE, 0, 0xffffffff, TRUE),
+
   /* 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",
-        FALSE, 0x00000000, 0x00000000, FALSE),
+        NULL, "R_XTENSA_GNU_VTINHERIT",
+        FALSE, 00, 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",
-        FALSE, 0x00000000, 0x00000000, FALSE),
+        _bfd_elf_rel_vtable_reloc_fn, "R_XTENSA_GNU_VTENTRY",
+        FALSE, 00, FALSE),
 
   /* Relocations for supporting difference of symbols.  */
-  HOWTO (R_XTENSA_DIFF8, 0, 0, 8, FALSE, 0, complain_overflow_bitfield,
-        bfd_elf_xtensa_reloc, "R_XTENSA_DIFF8",
-        FALSE, 0xffffffff, 0xffffffff, FALSE),
-  HOWTO (R_XTENSA_DIFF16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield,
-        bfd_elf_xtensa_reloc, "R_XTENSA_DIFF16",
-        FALSE, 0xffffffff, 0xffffffff, FALSE),
-  HOWTO (R_XTENSA_DIFF32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
-        bfd_elf_xtensa_reloc, "R_XTENSA_DIFF32",
-        FALSE, 0xffffffff, 0xffffffff, FALSE),
+  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_signed,
+        bfd_elf_xtensa_reloc, "R_XTENSA_DIFF16", FALSE, 0, 0xffff, FALSE),
+  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.  */
   HOWTO (R_XTENSA_SLOT0_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT0_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT0_OP", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT1_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT1_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT1_OP", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT2_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT2_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT2_OP", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT3_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT3_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT3_OP", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT4_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT4_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT4_OP", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT5_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT5_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT5_OP", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT6_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT6_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT6_OP", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT7_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT7_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT7_OP", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT8_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT8_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT8_OP", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT9_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT9_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT9_OP", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT10_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT10_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT10_OP", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT11_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT11_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT11_OP", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT12_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT12_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT12_OP", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT13_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT13_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT13_OP", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT14_OP, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT14_OP",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT14_OP", FALSE, 0, 0, TRUE),
 
   /* "Alternate" relocations.  The meaning of these is opcode-specific.  */
   HOWTO (R_XTENSA_SLOT0_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT0_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT0_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT1_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT1_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT1_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT2_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT2_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT2_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT3_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT3_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT3_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT4_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT4_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT4_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT5_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT5_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT5_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT6_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT6_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT6_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT7_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT7_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT7_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT8_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT8_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT8_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT9_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT9_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT9_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT10_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT10_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT10_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT11_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT11_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT11_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT12_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT12_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT12_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT13_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT13_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE),
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT13_ALT", FALSE, 0, 0, TRUE),
   HOWTO (R_XTENSA_SLOT14_ALT, 0, 0, 0, TRUE, 0, complain_overflow_dont,
-        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT14_ALT",
-        FALSE, 0x00000000, 0x00000000, TRUE)
+        bfd_elf_xtensa_reloc, "R_XTENSA_SLOT14_ALT", FALSE, 0, 0, TRUE),
+
+  /* TLS relocations.  */
+  HOWTO (R_XTENSA_TLSDESC_FN, 0, 2, 32, FALSE, 0, complain_overflow_dont,
+        bfd_elf_xtensa_reloc, "R_XTENSA_TLSDESC_FN",
+        FALSE, 0, 0xffffffff, FALSE),
+  HOWTO (R_XTENSA_TLSDESC_ARG, 0, 2, 32, FALSE, 0, complain_overflow_dont,
+        bfd_elf_xtensa_reloc, "R_XTENSA_TLSDESC_ARG",
+        FALSE, 0, 0xffffffff, FALSE),
+  HOWTO (R_XTENSA_TLS_DTPOFF, 0, 2, 32, FALSE, 0, complain_overflow_dont,
+        bfd_elf_xtensa_reloc, "R_XTENSA_TLS_DTPOFF",
+        FALSE, 0, 0xffffffff, FALSE),
+  HOWTO (R_XTENSA_TLS_TPOFF, 0, 2, 32, FALSE, 0, complain_overflow_dont,
+        bfd_elf_xtensa_reloc, "R_XTENSA_TLS_TPOFF",
+        FALSE, 0, 0xffffffff, FALSE),
+  HOWTO (R_XTENSA_TLS_FUNC, 0, 0, 0, FALSE, 0, complain_overflow_dont,
+        bfd_elf_xtensa_reloc, "R_XTENSA_TLS_FUNC",
+        FALSE, 0, 0, FALSE),
+  HOWTO (R_XTENSA_TLS_ARG, 0, 0, 0, FALSE, 0, complain_overflow_dont,
+        bfd_elf_xtensa_reloc, "R_XTENSA_TLS_ARG",
+        FALSE, 0, 0, FALSE),
+  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
@@ -350,6 +362,10 @@ elf_xtensa_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
       TRACE ("BFD_RELOC_32");
       return &elf_howto_table[(unsigned) R_XTENSA_32 ];
 
+    case BFD_RELOC_32_PCREL:
+      TRACE ("BFD_RELOC_32_PCREL");
+      return &elf_howto_table[(unsigned) R_XTENSA_32_PCREL ];
+
     case BFD_RELOC_XTENSA_DIFF8:
       TRACE ("BFD_RELOC_XTENSA_DIFF8");
       return &elf_howto_table[(unsigned) R_XTENSA_DIFF8 ];
@@ -362,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 ];
@@ -410,6 +450,34 @@ elf_xtensa_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
       TRACE ("BFD_RELOC_VTABLE_ENTRY");
       return &elf_howto_table[(unsigned) R_XTENSA_GNU_VTENTRY ];
 
+    case BFD_RELOC_XTENSA_TLSDESC_FN:
+      TRACE ("BFD_RELOC_XTENSA_TLSDESC_FN");
+      return &elf_howto_table[(unsigned) R_XTENSA_TLSDESC_FN ];
+
+    case BFD_RELOC_XTENSA_TLSDESC_ARG:
+      TRACE ("BFD_RELOC_XTENSA_TLSDESC_ARG");
+      return &elf_howto_table[(unsigned) R_XTENSA_TLSDESC_ARG ];
+
+    case BFD_RELOC_XTENSA_TLS_DTPOFF:
+      TRACE ("BFD_RELOC_XTENSA_TLS_DTPOFF");
+      return &elf_howto_table[(unsigned) R_XTENSA_TLS_DTPOFF ];
+
+    case BFD_RELOC_XTENSA_TLS_TPOFF:
+      TRACE ("BFD_RELOC_XTENSA_TLS_TPOFF");
+      return &elf_howto_table[(unsigned) R_XTENSA_TLS_TPOFF ];
+
+    case BFD_RELOC_XTENSA_TLS_FUNC:
+      TRACE ("BFD_RELOC_XTENSA_TLS_FUNC");
+      return &elf_howto_table[(unsigned) R_XTENSA_TLS_FUNC ];
+
+    case BFD_RELOC_XTENSA_TLS_ARG:
+      TRACE ("BFD_RELOC_XTENSA_TLS_ARG");
+      return &elf_howto_table[(unsigned) R_XTENSA_TLS_ARG ];
+
+    case BFD_RELOC_XTENSA_TLS_CALL:
+      TRACE ("BFD_RELOC_XTENSA_TLS_CALL");
+      return &elf_howto_table[(unsigned) R_XTENSA_TLS_CALL ];
+
     default:
       if (code >= BFD_RELOC_XTENSA_SLOT0_OP
          && code <= BFD_RELOC_XTENSA_SLOT14_OP)
@@ -430,23 +498,48 @@ 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;
 }
 
+static reloc_howto_type *
+elf_xtensa_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+                             const char *r_name)
+{
+  unsigned int i;
+
+  for (i = 0; i < sizeof (elf_howto_table) / sizeof (elf_howto_table[0]); i++)
+    if (elf_howto_table[i].name != NULL
+       && strcasecmp (elf_howto_table[i].name, r_name) == 0)
+      return &elf_howto_table[i];
+
+  return NULL;
+}
+
 
 /* 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
@@ -480,29 +573,214 @@ 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 */
+    },
+    {
+      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] =
+{
+    {
+      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.  */
+#define TCB_SIZE       8
+
+struct elf_xtensa_link_hash_entry
+{
+  struct elf_link_hash_entry elf;
+
+  bfd_signed_vma tlsfunc_refcount;
+
+#define GOT_UNKNOWN    0
+#define GOT_NORMAL     1
+#define GOT_TLS_GD     2       /* global or local dynamic */
+#define GOT_TLS_IE     4       /* initial or local exec */
+#define GOT_TLS_ANY    (GOT_TLS_GD | GOT_TLS_IE)
+  unsigned char tls_type;
+};
+
+#define elf_xtensa_hash_entry(ent) ((struct elf_xtensa_link_hash_entry *)(ent))
+
+struct elf_xtensa_obj_tdata
 {
-  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 */
+  struct elf_obj_tdata root;
+
+  /* tls_type for each local got entry.  */
+  char *local_got_tls_type;
+
+  bfd_signed_vma *local_tlsfunc_refcounts;
 };
 
-static const bfd_byte elf_xtensa_le_plt_entry[PLT_ENTRY_SIZE] =
+#define elf_xtensa_tdata(abfd) \
+  ((struct elf_xtensa_obj_tdata *) (abfd)->tdata.any)
+
+#define elf_xtensa_local_got_tls_type(abfd) \
+  (elf_xtensa_tdata (abfd)->local_got_tls_type)
+
+#define elf_xtensa_local_tlsfunc_refcounts(abfd) \
+  (elf_xtensa_tdata (abfd)->local_tlsfunc_refcounts)
+
+#define is_xtensa_elf(bfd) \
+  (bfd_get_flavour (bfd) == bfd_target_elf_flavour \
+   && elf_tdata (bfd) != NULL \
+   && elf_object_id (bfd) == XTENSA_ELF_DATA)
+
+static bfd_boolean
+elf_xtensa_mkobject (bfd *abfd)
+{
+  return bfd_elf_allocate_object (abfd, sizeof (struct elf_xtensa_obj_tdata),
+                                 XTENSA_ELF_DATA);
+}
+
+/* Xtensa ELF linker hash table.  */
+
+struct elf_xtensa_link_hash_table
 {
-  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 */
+  struct elf_link_hash_table elf;
+
+  /* Short-cuts to get to dynamic linker sections.  */
+  asection *sgotloc;
+  asection *spltlittbl;
+
+  /* Total count of PLT relocations seen during check_relocs.
+     The actual PLT code must be split into multiple sections and all
+     the sections have to be created before size_dynamic_sections,
+     where we figure out the exact number of PLT entries that will be
+     needed.  It is OK if this count is an overestimate, e.g., some
+     relocations may be removed by GC.  */
+  int plt_reloc_count;
+
+  struct elf_xtensa_link_hash_entry *tlsbase;
 };
 
+/* Get the Xtensa ELF linker hash table from a link_info structure.  */
+
+#define elf_xtensa_hash_table(p) \
+  (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \
+  == XTENSA_ELF_DATA ? ((struct elf_xtensa_link_hash_table *) ((p)->hash)) : NULL)
+
+/* Create an entry in an Xtensa ELF linker hash table.  */
+
+static struct bfd_hash_entry *
+elf_xtensa_link_hash_newfunc (struct bfd_hash_entry *entry,
+                             struct bfd_hash_table *table,
+                             const char *string)
+{
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (entry == NULL)
+    {
+      entry = bfd_hash_allocate (table,
+                                sizeof (struct elf_xtensa_link_hash_entry));
+      if (entry == NULL)
+       return entry;
+    }
+
+  /* Call the allocation method of the superclass.  */
+  entry = _bfd_elf_link_hash_newfunc (entry, table, string);
+  if (entry != NULL)
+    {
+      struct elf_xtensa_link_hash_entry *eh = elf_xtensa_hash_entry (entry);
+      eh->tlsfunc_refcount = 0;
+      eh->tls_type = GOT_UNKNOWN;
+    }
+
+  return entry;
+}
+
+/* Create an Xtensa ELF linker hash table.  */
+
+static struct bfd_link_hash_table *
+elf_xtensa_link_hash_table_create (bfd *abfd)
+{
+  struct elf_link_hash_entry *tlsbase;
+  struct elf_xtensa_link_hash_table *ret;
+  size_t amt = sizeof (struct elf_xtensa_link_hash_table);
+
+  ret = bfd_zmalloc (amt);
+  if (ret == NULL)
+    return NULL;
+
+  if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd,
+                                     elf_xtensa_link_hash_newfunc,
+                                     sizeof (struct elf_xtensa_link_hash_entry),
+                                     XTENSA_ELF_DATA))
+    {
+      free (ret);
+      return NULL;
+    }
+
+  /* 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_",
+                                 TRUE, FALSE, FALSE);
+  tlsbase->root.type = bfd_link_hash_new;
+  tlsbase->root.u.undef.abfd = NULL;
+  tlsbase->non_elf = 0;
+  ret->tlsbase = elf_xtensa_hash_entry (tlsbase);
+  ret->tlsbase->tls_type = GOT_UNKNOWN;
+
+  return &ret->elf.root;
+}
+
+/* Copy the extra info we tack onto an elf_link_hash_entry.  */
+
+static void
+elf_xtensa_copy_indirect_symbol (struct bfd_link_info *info,
+                                struct elf_link_hash_entry *dir,
+                                struct elf_link_hash_entry *ind)
+{
+  struct elf_xtensa_link_hash_entry *edir, *eind;
+
+  edir = elf_xtensa_hash_entry (dir);
+  eind = elf_xtensa_hash_entry (ind);
+
+  if (ind->root.type == bfd_link_hash_indirect)
+    {
+      edir->tlsfunc_refcount += eind->tlsfunc_refcount;
+      eind->tlsfunc_refcount = 0;
+
+      if (dir->got.refcount <= 0)
+       {
+         edir->tls_type = eind->tls_type;
+         eind->tls_type = GOT_UNKNOWN;
+       }
+    }
+
+  _bfd_elf_link_hash_copy_indirect (info, dir, ind);
+}
 
 static inline bfd_boolean
-xtensa_elf_dynamic_symbol_p (struct elf_link_hash_entry *h,
+elf_xtensa_dynamic_symbol_p (struct elf_link_hash_entry *h,
                             struct bfd_link_info *info)
 {
   /* Check if we should do dynamic things to this symbol.  The
@@ -535,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)
@@ -567,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,
@@ -575,16 +853,15 @@ xtensa_read_table_entries (bfd *abfd,
                           bfd_boolean output_addr)
 {
   asection *table_section;
-  char *table_section_name;
   bfd_size_type table_size = 0;
   bfd_byte *table_data;
   property_table_entry *blocks;
   int blk, block_count;
   bfd_size_type num_records;
-  Elf_Internal_Rela *internal_relocs;
-  bfd_vma section_addr;
+  Elf_Internal_Rela *internal_relocs, *irel, *rel_end;
+  bfd_vma section_addr, off;
   flagword predef_flags;
-  bfd_size_type table_entry_size;
+  bfd_size_type table_entry_size, section_limit;
 
   if (!section
       || !(section->flags & SEC_ALLOC)
@@ -594,13 +871,11 @@ xtensa_read_table_entries (bfd *abfd,
       return 0;
     }
 
-  table_section_name = xtensa_get_property_section_name (section, sec_name);
-  table_section = bfd_get_section_by_name (abfd, table_section_name);
-  free (table_section_name);
+  table_section = xtensa_get_property_section (section, sec_name);
   if (table_section)
     table_size = table_section->size;
 
-  if (table_size == 0) 
+  if (table_size == 0)
     {
       *table_p = NULL;
       return 0;
@@ -622,68 +897,63 @@ xtensa_read_table_entries (bfd *abfd,
   else
     section_addr = section->vma;
 
-  /* If the file has not yet been relocated, process the relocations
-     and sort out the table entries that apply to the specified section.  */
   internal_relocs = retrieve_internal_relocs (abfd, table_section, TRUE);
   if (internal_relocs && !table_section->reloc_done)
     {
-      unsigned i;
-
-      for (i = 0; i < table_section->reloc_count; i++)
-       {
-         Elf_Internal_Rela *rel = &internal_relocs[i];
-         unsigned long r_symndx;
+      qsort (internal_relocs, table_section->reloc_count,
+            sizeof (Elf_Internal_Rela), internal_reloc_compare);
+      irel = internal_relocs;
+    }
+  else
+    irel = NULL;
 
-         if (ELF32_R_TYPE (rel->r_info) == R_XTENSA_NONE)
-           continue;
+  section_limit = bfd_get_section_limit (abfd, section);
+  rel_end = internal_relocs + table_section->reloc_count;
 
-         BFD_ASSERT (ELF32_R_TYPE (rel->r_info) == R_XTENSA_32);
-         r_symndx = ELF32_R_SYM (rel->r_info);
+  for (off = 0; off < table_size; off += table_entry_size)
+    {
+      bfd_vma address = bfd_get_32 (abfd, table_data + off);
 
-         if (get_elf_r_symndx_section (abfd, r_symndx) == section)
-           {
-             bfd_vma sym_off = get_elf_r_symndx_offset (abfd, r_symndx);
-             BFD_ASSERT (sym_off == 0);
-             BFD_ASSERT (rel->r_addend == 0);
-             blocks[block_count].address =
-               (section_addr + sym_off + rel->r_addend
-                + bfd_get_32 (abfd, table_data + rel->r_offset));
-             blocks[block_count].size =
-               bfd_get_32 (abfd, table_data + rel->r_offset + 4);
-             if (predef_flags)
-               blocks[block_count].flags = predef_flags;
-             else
-               blocks[block_count].flags =
-                 bfd_get_32 (abfd, table_data + rel->r_offset + 8);
-             block_count++;
-           }
+      /* Skip any relocations before the current offset.  This should help
+        avoid confusion caused by unexpected relocations for the preceding
+        table entry.  */
+      while (irel &&
+            (irel->r_offset < off
+             || (irel->r_offset == off
+                 && ELF32_R_TYPE (irel->r_info) == R_XTENSA_NONE)))
+       {
+         irel += 1;
+         if (irel >= rel_end)
+           irel = 0;
        }
-    }
-  else
-    {
-      /* The file has already been relocated and the addresses are
-        already in the table.  */
-      bfd_vma off;
-      bfd_size_type section_limit = bfd_get_section_limit (abfd, section);
 
-      for (off = 0; off < table_size; off += table_entry_size) 
+      if (irel && irel->r_offset == off)
        {
-         bfd_vma address = bfd_get_32 (abfd, table_data + off);
+         bfd_vma sym_off;
+         unsigned long r_symndx = ELF32_R_SYM (irel->r_info);
+         BFD_ASSERT (ELF32_R_TYPE (irel->r_info) == R_XTENSA_32);
 
-         if (address >= section_addr
-             && address < section_addr + section_limit)
-           {
-             blocks[block_count].address = address;
-             blocks[block_count].size =
-               bfd_get_32 (abfd, table_data + off + 4);
-             if (predef_flags)
-               blocks[block_count].flags = predef_flags;
-             else
-               blocks[block_count].flags =
-                 bfd_get_32 (abfd, table_data + off + 8);
-             block_count++;
-           }
+         if (get_elf_r_symndx_section (abfd, r_symndx) != section)
+           continue;
+
+         sym_off = get_elf_r_symndx_offset (abfd, r_symndx);
+         BFD_ASSERT (sym_off == 0);
+         address += (section_addr + sym_off + irel->r_addend);
+       }
+      else
+       {
+         if (address < section_addr
+             || address >= section_addr + section_limit)
+           continue;
        }
+
+      blocks[block_count].address = address;
+      blocks[block_count].size = bfd_get_32 (abfd, table_data + off + 4);
+      if (predef_flags)
+       blocks[block_count].flags = predef_flags;
+      else
+       blocks[block_count].flags = bfd_get_32 (abfd, table_data + off + 8);
+      block_count++;
     }
 
   release_contents (table_section, table_data);
@@ -696,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
@@ -706,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;
@@ -762,14 +1033,21 @@ elf_xtensa_check_relocs (bfd *abfd,
                         asection *sec,
                         const Elf_Internal_Rela *relocs)
 {
+  struct elf_xtensa_link_hash_table *htab;
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
 
-  if (info->relocatable)
+  if (bfd_link_relocatable (info) || (sec->flags & SEC_ALLOC) == 0)
     return TRUE;
 
+  BFD_ASSERT (is_xtensa_elf (abfd));
+
+  htab = elf_xtensa_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
+
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
 
@@ -777,51 +1055,108 @@ elf_xtensa_check_relocs (bfd *abfd,
   for (rel = relocs; rel < rel_end; rel++)
     {
       unsigned int r_type;
-      unsigned long r_symndx;
-      struct elf_link_hash_entry *h;
+      unsigned r_symndx;
+      struct elf_link_hash_entry *h = NULL;
+      struct elf_xtensa_link_hash_entry *eh;
+      int tls_type, old_tls_type;
+      bfd_boolean is_got = FALSE;
+      bfd_boolean is_plt = FALSE;
+      bfd_boolean is_tlsfunc = FALSE;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
       r_type = ELF32_R_TYPE (rel->r_info);
 
       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;
        }
 
-      if (r_symndx < symtab_hdr->sh_info)
-       h = NULL;
-      else
+      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);
 
       switch (r_type)
        {
-       case R_XTENSA_32:
-         if (h == NULL)
-           goto local_literal;
+       case R_XTENSA_TLSDESC_FN:
+         if (bfd_link_pic (info))
+           {
+             tls_type = GOT_TLS_GD;
+             is_got = TRUE;
+             is_tlsfunc = TRUE;
+           }
+         else
+           tls_type = GOT_TLS_IE;
+         break;
 
-         if ((sec->flags & SEC_ALLOC) != 0)
+       case R_XTENSA_TLSDESC_ARG:
+         if (bfd_link_pic (info))
            {
-             if (h->got.refcount <= 0)
-               h->got.refcount = 1;
-             else
-               h->got.refcount += 1;
+             tls_type = GOT_TLS_GD;
+             is_got = TRUE;
+           }
+         else
+           {
+             tls_type = GOT_TLS_IE;
+             if (h && elf_xtensa_hash_entry (h) != htab->tlsbase)
+               is_got = TRUE;
            }
          break;
 
+       case R_XTENSA_TLS_DTPOFF:
+         if (bfd_link_pic (info))
+           tls_type = GOT_TLS_GD;
+         else
+           tls_type = GOT_TLS_IE;
+         break;
+
+       case R_XTENSA_TLS_TPOFF:
+         tls_type = GOT_TLS_IE;
+         if (bfd_link_pic (info))
+           info->flags |= DF_STATIC_TLS;
+         if (bfd_link_pic (info) || h)
+           is_got = TRUE;
+         break;
+
+       case R_XTENSA_32:
+         tls_type = GOT_NORMAL;
+         is_got = TRUE;
+         break;
+
        case R_XTENSA_PLT:
-         /* If this relocation is against a local symbol, then it's
-            exactly the same as a normal local GOT entry.  */
-         if (h == NULL)
-           goto local_literal;
+         tls_type = GOT_NORMAL;
+         is_plt = TRUE;
+         break;
+
+       case R_XTENSA_GNU_VTINHERIT:
+         /* This relocation describes the C++ object vtable hierarchy.
+            Reconstruct it for later use during GC.  */
+         if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
+           return FALSE;
+         continue;
+
+       case R_XTENSA_GNU_VTENTRY:
+         /* This relocation describes which C++ vtable entries are actually
+            used.  Record for later use during GC.  */
+         if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+           return FALSE;
+         continue;
+
+       default:
+         /* Nothing to do for any other relocations.  */
+         continue;
+       }
 
-         if ((sec->flags & SEC_ALLOC) != 0)
+      if (h)
+       {
+         if (is_plt)
            {
              if (h->plt.refcount <= 0)
                {
@@ -834,97 +1169,91 @@ elf_xtensa_check_relocs (bfd *abfd,
              /* Keep track of the total PLT relocation count even if we
                 don't yet know whether the dynamic sections will be
                 created.  */
-             plt_reloc_count += 1;
+             htab->plt_reloc_count += 1;
 
              if (elf_hash_table (info)->dynamic_sections_created)
                {
-                 if (!add_extra_plt_sections (elf_hash_table (info)->dynobj,
-                                              plt_reloc_count))
+                 if (! add_extra_plt_sections (info, htab->plt_reloc_count))
                    return FALSE;
                }
            }
-         break;
+         else if (is_got)
+           {
+             if (h->got.refcount <= 0)
+               h->got.refcount = 1;
+             else
+               h->got.refcount += 1;
+           }
 
-       local_literal:
-         if ((sec->flags & SEC_ALLOC) != 0)
+         if (is_tlsfunc)
+           eh->tlsfunc_refcount += 1;
+
+         old_tls_type = eh->tls_type;
+       }
+      else
+       {
+         /* Allocate storage the first time.  */
+         if (elf_local_got_refcounts (abfd) == NULL)
            {
-             bfd_signed_vma *local_got_refcounts;
+             bfd_size_type size = symtab_hdr->sh_info;
+             void *mem;
 
-             /* This is a global offset table entry for a local symbol.  */
-             local_got_refcounts = elf_local_got_refcounts (abfd);
-             if (local_got_refcounts == NULL)
-               {
-                 bfd_size_type size;
+             mem = bfd_zalloc (abfd, size * sizeof (bfd_signed_vma));
+             if (mem == NULL)
+               return FALSE;
+             elf_local_got_refcounts (abfd) = (bfd_signed_vma *) mem;
 
-                 size = symtab_hdr->sh_info;
-                 size *= sizeof (bfd_signed_vma);
-                 local_got_refcounts =
-                   (bfd_signed_vma *) bfd_zalloc (abfd, size);
-                 if (local_got_refcounts == NULL)
-                   return FALSE;
-                 elf_local_got_refcounts (abfd) = local_got_refcounts;
-               }
-             local_got_refcounts[r_symndx] += 1;
+             mem = bfd_zalloc (abfd, size);
+             if (mem == NULL)
+               return FALSE;
+             elf_xtensa_local_got_tls_type (abfd) = (char *) mem;
+
+             mem = bfd_zalloc (abfd, size * sizeof (bfd_signed_vma));
+             if (mem == NULL)
+               return FALSE;
+             elf_xtensa_local_tlsfunc_refcounts (abfd)
+               = (bfd_signed_vma *) mem;
            }
-         break;
 
-       case R_XTENSA_OP0:
-       case R_XTENSA_OP1:
-       case R_XTENSA_OP2:
-       case R_XTENSA_SLOT0_OP:
-       case R_XTENSA_SLOT1_OP:
-       case R_XTENSA_SLOT2_OP:
-       case R_XTENSA_SLOT3_OP:
-       case R_XTENSA_SLOT4_OP:
-       case R_XTENSA_SLOT5_OP:
-       case R_XTENSA_SLOT6_OP:
-       case R_XTENSA_SLOT7_OP:
-       case R_XTENSA_SLOT8_OP:
-       case R_XTENSA_SLOT9_OP:
-       case R_XTENSA_SLOT10_OP:
-       case R_XTENSA_SLOT11_OP:
-       case R_XTENSA_SLOT12_OP:
-       case R_XTENSA_SLOT13_OP:
-       case R_XTENSA_SLOT14_OP:
-       case R_XTENSA_SLOT0_ALT:
-       case R_XTENSA_SLOT1_ALT:
-       case R_XTENSA_SLOT2_ALT:
-       case R_XTENSA_SLOT3_ALT:
-       case R_XTENSA_SLOT4_ALT:
-       case R_XTENSA_SLOT5_ALT:
-       case R_XTENSA_SLOT6_ALT:
-       case R_XTENSA_SLOT7_ALT:
-       case R_XTENSA_SLOT8_ALT:
-       case R_XTENSA_SLOT9_ALT:
-       case R_XTENSA_SLOT10_ALT:
-       case R_XTENSA_SLOT11_ALT:
-       case R_XTENSA_SLOT12_ALT:
-       case R_XTENSA_SLOT13_ALT:
-       case R_XTENSA_SLOT14_ALT:
-       case R_XTENSA_ASM_EXPAND:
-       case R_XTENSA_ASM_SIMPLIFY:
-       case R_XTENSA_DIFF8:
-       case R_XTENSA_DIFF16:
-       case R_XTENSA_DIFF32:
-         /* Nothing to do for these.  */
-         break;
+         /* This is a global offset table entry for a local symbol.  */
+         if (is_got || is_plt)
+           elf_local_got_refcounts (abfd) [r_symndx] += 1;
 
-       case R_XTENSA_GNU_VTINHERIT:
-         /* This relocation describes the C++ object vtable hierarchy.
-            Reconstruct it for later use during GC.  */
-         if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
-           return FALSE;
-         break;
+         if (is_tlsfunc)
+           elf_xtensa_local_tlsfunc_refcounts (abfd) [r_symndx] += 1;
 
-       case R_XTENSA_GNU_VTENTRY:
-         /* This relocation describes which C++ vtable entries are actually
-            used.  Record for later use during GC.  */
-         if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
-           return FALSE;
-         break;
+         old_tls_type = elf_xtensa_local_got_tls_type (abfd) [r_symndx];
+       }
 
-       default:
-         break;
+      if ((old_tls_type & GOT_TLS_IE) && (tls_type & GOT_TLS_IE))
+       tls_type |= old_tls_type;
+      /* If a TLS symbol is accessed using IE at least once,
+        there is no point to use a dynamic model for it.  */
+      else if (old_tls_type != tls_type && old_tls_type != GOT_UNKNOWN
+              && ((old_tls_type & GOT_TLS_GD) == 0
+                  || (tls_type & GOT_TLS_IE) == 0))
+       {
+         if ((old_tls_type & GOT_TLS_IE) && (tls_type & GOT_TLS_GD))
+           tls_type = old_tls_type;
+         else if ((old_tls_type & GOT_TLS_GD) && (tls_type & GOT_TLS_GD))
+           tls_type |= old_tls_type;
+         else
+           {
+             _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;
+           }
+       }
+
+      if (old_tls_type != tls_type)
+       {
+         if (eh)
+           eh->tls_type = tls_type;
+         else
+           elf_xtensa_local_got_tls_type (abfd) [r_symndx] = tls_type;
        }
     }
 
@@ -936,11 +1265,12 @@ static void
 elf_xtensa_make_sym_local (struct bfd_link_info *info,
                           struct elf_link_hash_entry *h)
 {
-  if (info->shared)
+  if (bfd_link_pic (info))
     {
       if (h->plt.refcount > 0)
        {
-         /* Will use RELATIVE relocs instead of JMP_SLOT relocs.  */
+         /* 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;
@@ -974,107 +1304,34 @@ elf_xtensa_hide_symbol (struct bfd_link_info *info,
 
 static asection *
 elf_xtensa_gc_mark_hook (asection *sec,
-                        struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                        struct bfd_link_info *info,
                         Elf_Internal_Rela *rel,
                         struct elf_link_hash_entry *h,
                         Elf_Internal_Sym *sym)
 {
-  if (h)
-    {
-      switch (ELF32_R_TYPE (rel->r_info))
-       {
-       case R_XTENSA_GNU_VTINHERIT:
-       case R_XTENSA_GNU_VTENTRY:
-         break;
+  /* Property sections are marked "KEEP" in the linker scripts, but they
+     should not cause other sections to be marked.  (This approach relies
+     on elf_xtensa_discard_info to remove property table entries that
+     describe discarded sections.  Alternatively, it might be more
+     efficient to avoid using "KEEP" in the linker scripts and instead use
+     the gc_mark_extra_sections hook to mark only the property sections
+     that describe marked sections.  That alternative does not work well
+     with the current property table sections, which do not correspond
+     one-to-one with the sections they describe, but that should be fixed
+     someday.) */
+  if (xtensa_is_property_section (sec))
+    return NULL;
 
-       default:
-         switch (h->root.type)
-           {
-           case bfd_link_hash_defined:
-           case bfd_link_hash_defweak:
-             return h->root.u.def.section;
+  if (h != NULL)
+    switch (ELF32_R_TYPE (rel->r_info))
+      {
+      case R_XTENSA_GNU_VTINHERIT:
+      case R_XTENSA_GNU_VTENTRY:
+       return NULL;
+      }
 
-           case bfd_link_hash_common:
-             return h->root.u.c.p->section;
-
-           default:
-             break;
-           }
-       }
-    }
-  else
-    return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
-
-  return NULL;
-}
-
-
-/* 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 ATTRIBUTE_UNUSED,
-                         asection *sec,
-                         const Elf_Internal_Rela *relocs)
-{
-  Elf_Internal_Shdr *symtab_hdr;
-  struct elf_link_hash_entry **sym_hashes;
-  bfd_signed_vma *local_got_refcounts;
-  const Elf_Internal_Rela *rel, *relend;
-
-  if ((sec->flags & SEC_ALLOC) == 0)
-    return TRUE;
-
-  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-  sym_hashes = elf_sym_hashes (abfd);
-  local_got_refcounts = elf_local_got_refcounts (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;
-
-      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;
-       }
-
-      r_type = ELF32_R_TYPE (rel->r_info);
-      switch (r_type)
-       {
-       case R_XTENSA_32:
-         if (h == NULL)
-           goto local_literal;
-         if (h->got.refcount > 0)
-           h->got.refcount--;
-         break;
-
-       case R_XTENSA_PLT:
-         if (h == NULL)
-           goto local_literal;
-         if (h->plt.refcount > 0)
-           h->plt.refcount--;
-         break;
-
-       local_literal:
-         if (local_got_refcounts[r_symndx] > 0)
-           local_got_refcounts[r_symndx] -= 1;
-         break;
-
-       default:
-         break;
-       }
-    }
-
-  return TRUE;
-}
+  return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
+}
 
 
 /* Create all the dynamic sections.  */
@@ -1082,8 +1339,12 @@ elf_xtensa_gc_sweep_hook (bfd *abfd,
 static bfd_boolean
 elf_xtensa_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
 {
+  struct elf_xtensa_link_hash_table *htab;
   flagword flags, noalloc_flags;
-  asection *s;
+
+  htab = elf_xtensa_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
 
   /* First do all the standard stuff.  */
   if (! _bfd_elf_create_dynamic_sections (dynobj, info))
@@ -1091,7 +1352,7 @@ elf_xtensa_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
 
   /* Create any extra PLT sections in case check_relocs has already
      been called on all the non-dynamic input files.  */
-  if (!add_extra_plt_sections (dynobj, plt_reloc_count))
+  if (! add_extra_plt_sections (info, htab->plt_reloc_count))
     return FALSE;
 
   noalloc_flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY
@@ -1099,28 +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.  */
-  s = bfd_get_section_by_name (dynobj, ".got.plt");
-  if (s == NULL
-      || ! bfd_set_section_flags (dynobj, s, flags))
-    return FALSE;
-
-  /* Create ".rela.got".  */
-  s = bfd_make_section_with_flags (dynobj, ".rela.got", flags);
-  if (s == NULL
-      || ! bfd_set_section_alignment (dynobj, s, 2))
+  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).  */
-  s = bfd_make_section_with_flags (dynobj, ".got.loc", flags);
-  if (s == NULL
-      || ! bfd_set_section_alignment (dynobj, s, 2))
+  htab->sgotloc = bfd_make_section_anyway_with_flags (dynobj, ".got.loc",
+                                                     flags);
+  if (htab->sgotloc == NULL
+      || !bfd_set_section_alignment (htab->sgotloc, 2))
     return FALSE;
 
   /* Create ".xt.lit.plt" (literal table for ".got.plt*").  */
-  s = bfd_make_section_with_flags (dynobj, ".xt.lit.plt",
-                                  noalloc_flags);
-  if (s == NULL
-      || ! bfd_set_section_alignment (dynobj, s, 2))
+  htab->spltlittbl = bfd_make_section_anyway_with_flags (dynobj, ".xt.lit.plt",
+                                                        noalloc_flags);
+  if (htab->spltlittbl == NULL
+      || !bfd_set_section_alignment (htab->spltlittbl, 2))
     return FALSE;
 
   return TRUE;
@@ -1128,8 +1383,9 @@ elf_xtensa_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
 
 
 static bfd_boolean
-add_extra_plt_sections (bfd *dynobj, int count)
+add_extra_plt_sections (struct bfd_link_info *info, int count)
 {
+  bfd *dynobj = elf_hash_table (info)->dynobj;
   int chunk;
 
   /* Iterate over all chunks except 0 which uses the standard ".plt" and
@@ -1141,7 +1397,7 @@ add_extra_plt_sections (bfd *dynobj, int count)
       asection *s;
 
       /* Stop when we find a section has already been created.  */
-      if (elf_xtensa_get_plt_section (dynobj, chunk))
+      if (elf_xtensa_get_plt_section (info, chunk))
        break;
 
       flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
@@ -1149,17 +1405,16 @@ add_extra_plt_sections (bfd *dynobj, 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;
     }
 
@@ -1180,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;
     }
 
@@ -1198,57 +1453,56 @@ elf_xtensa_adjust_dynamic_symbol (struct bfd_link_info *info ATTRIBUTE_UNUSED,
 
 
 static bfd_boolean
-elf_xtensa_fix_refcounts (struct elf_link_hash_entry *h, void *arg)
+elf_xtensa_allocate_dynrelocs (struct elf_link_hash_entry *h, void *arg)
 {
-  struct bfd_link_info *info = (struct bfd_link_info *) arg;
+  struct bfd_link_info *info;
+  struct elf_xtensa_link_hash_table *htab;
+  struct elf_xtensa_link_hash_entry *eh = elf_xtensa_hash_entry (h);
 
-  if (h->root.type == bfd_link_hash_warning)
-    h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
-  if (! xtensa_elf_dynamic_symbol_p (h, info))
-    elf_xtensa_make_sym_local (info, h);
+  if (h->root.type == bfd_link_hash_indirect)
+    return TRUE;
 
-  return TRUE;
-}
+  info = (struct bfd_link_info *) arg;
+  htab = elf_xtensa_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
 
+  /* If we saw any use of an IE model for this symbol, we can then optimize
+     away GOT entries for any TLSDESC_FN relocs.  */
+  if ((eh->tls_type & GOT_TLS_IE) != 0)
+    {
+      BFD_ASSERT (h->got.refcount >= eh->tlsfunc_refcount);
+      h->got.refcount -= eh->tlsfunc_refcount;
+    }
 
-static bfd_boolean
-elf_xtensa_allocate_plt_size (struct elf_link_hash_entry *h, void *arg)
-{
-  asection *srelplt = (asection *) arg;
+  if (! elf_xtensa_dynamic_symbol_p (h, info))
+    elf_xtensa_make_sym_local (info, h);
 
-  if (h->root.type == bfd_link_hash_warning)
-    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+  if (! elf_xtensa_dynamic_symbol_p (h, info)
+      && h->root.type == bfd_link_hash_undefweak)
+    return TRUE;
 
   if (h->plt.refcount > 0)
-    srelplt->size += (h->plt.refcount * sizeof (Elf32_External_Rela));
-
-  return TRUE;
-}
-
-
-static bfd_boolean
-elf_xtensa_allocate_got_size (struct elf_link_hash_entry *h, void *arg)
-{
-  asection *srelgot = (asection *) arg;
-
-  if (h->root.type == bfd_link_hash_warning)
-    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+    htab->elf.srelplt->size += (h->plt.refcount * sizeof (Elf32_External_Rela));
 
   if (h->got.refcount > 0)
-    srelgot->size += (h->got.refcount * sizeof (Elf32_External_Rela));
+    htab->elf.srelgot->size += (h->got.refcount * sizeof (Elf32_External_Rela));
 
   return TRUE;
 }
 
 
 static void
-elf_xtensa_allocate_local_got_size (struct bfd_link_info *info,
-                                   asection *srelgot)
+elf_xtensa_allocate_local_got_size (struct bfd_link_info *info)
 {
+  struct elf_xtensa_link_hash_table *htab;
   bfd *i;
 
-  for (i = info->input_bfds; i; i = i->link_next)
+  htab = elf_xtensa_hash_table (info);
+  if (htab == NULL)
+    return;
+
+  for (i = info->input_bfds; i; i = i->link.next)
     {
       bfd_signed_vma *local_got_refcounts;
       bfd_size_type j, cnt;
@@ -1263,9 +1517,19 @@ elf_xtensa_allocate_local_got_size (struct bfd_link_info *info,
 
       for (j = 0; j < cnt; ++j)
        {
+         /* If we saw any use of an IE model for this symbol, we can
+            then optimize away GOT entries for any TLSDESC_FN relocs.  */
+         if ((elf_xtensa_local_got_tls_type (i) [j] & GOT_TLS_IE) != 0)
+           {
+             bfd_signed_vma *tlsfunc_refcount
+               = &elf_xtensa_local_tlsfunc_refcounts (i) [j];
+             BFD_ASSERT (local_got_refcounts[j] >= *tlsfunc_refcount);
+             local_got_refcounts[j] -= *tlsfunc_refcount;
+           }
+
          if (local_got_refcounts[j] > 0)
-           srelgot->size += (local_got_refcounts[j]
-                             * sizeof (Elf32_External_Rela));
+           htab->elf.srelgot->size += (local_got_refcounts[j]
+                                       * sizeof (Elf32_External_Rela));
        }
     }
 }
@@ -1277,6 +1541,7 @@ static bfd_boolean
 elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
                                  struct bfd_link_info *info)
 {
+  struct elf_xtensa_link_hash_table *htab;
   bfd *dynobj, *abfd;
   asection *s, *srelplt, *splt, *sgotplt, *srelgot, *spltlittbl, *sgotloc;
   bfd_boolean relplt, relgot;
@@ -1284,18 +1549,29 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 
   plt_entries = 0;
   plt_chunks = 0;
-  srelgot = 0;
+
+  htab = elf_xtensa_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
 
   dynobj = elf_hash_table (info)->dynobj;
   if (dynobj == NULL)
     abort ();
+  srelgot = htab->elf.srelgot;
+  srelplt = htab->elf.srelplt;
 
   if (elf_hash_table (info)->dynamic_sections_created)
     {
+      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;
@@ -1303,48 +1579,27 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
        }
 
       /* Allocate room for one word in ".got".  */
-      s = bfd_get_section_by_name (dynobj, ".got");
-      if (s == NULL)
-       abort ();
-      s->size = 4;
+      htab->elf.sgot->size = 4;
 
-      /* Adjust refcounts for symbols that we now know are not "dynamic".  */
+      /* Allocate space in ".rela.got" for literals that reference global
+        symbols and space in ".rela.plt" for literals that have PLT
+        entries.  */
       elf_link_hash_traverse (elf_hash_table (info),
-                             elf_xtensa_fix_refcounts,
+                             elf_xtensa_allocate_dynrelocs,
                              (void *) info);
 
-      /* Allocate space in ".rela.got" for literals that reference
-        global symbols.  */
-      srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
-      if (srelgot == NULL)
-       abort ();
-      elf_link_hash_traverse (elf_hash_table (info),
-                             elf_xtensa_allocate_got_size,
-                             (void *) srelgot);
-
       /* 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)
-       elf_xtensa_allocate_local_got_size (info, srelgot);
-
-      /* Allocate space in ".rela.plt" for literals that have PLT entries.  */
-      srelplt = bfd_get_section_by_name (dynobj, ".rela.plt");
-      if (srelplt == NULL)
-       abort ();
-      elf_link_hash_traverse (elf_hash_table (info),
-                             elf_xtensa_allocate_plt_size,
-                             (void *) srelplt);
+      if (bfd_link_pic (info))
+       elf_xtensa_allocate_local_got_size (info);
 
       /* Allocate space in ".plt" to match the size of ".rela.plt".  For
         each PLT entry, we need the PLT code plus a 4-byte literal.
         For each chunk of ".plt", we also need two more 4-byte
         literals, two corresponding entries in ".rela.got", and an
         8-byte entry in ".xt.lit.plt".  */
-      spltlittbl = bfd_get_section_by_name (dynobj, ".xt.lit.plt");
-      if (spltlittbl == NULL)
-       abort ();
-
+      spltlittbl = htab->spltlittbl;
       plt_entries = srelplt->size / sizeof (Elf32_External_Rela);
       plt_chunks =
        (plt_entries + PLT_ENTRIES_PER_CHUNK - 1) / PLT_ENTRIES_PER_CHUNK;
@@ -1353,14 +1608,13 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
         created earlier because the initial count of PLT relocations
         was an overestimate.  */
       for (chunk = 0;
-          (splt = elf_xtensa_get_plt_section (dynobj, chunk)) != NULL;
+          (splt = elf_xtensa_get_plt_section (info, chunk)) != NULL;
           chunk++)
        {
          int chunk_entries;
 
-         sgotplt = elf_xtensa_get_gotplt_section (dynobj, chunk);
-         if (sgotplt == NULL)
-           abort ();
+         sgotplt = elf_xtensa_get_gotplt_section (info, chunk);
+         BFD_ASSERT (sgotplt != NULL);
 
          if (chunk < plt_chunks - 1)
            chunk_entries = PLT_ENTRIES_PER_CHUNK;
@@ -1385,17 +1639,15 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 
       /* Allocate space in ".got.loc" to match the total size of all the
         literal tables.  */
-      sgotloc = bfd_get_section_by_name (dynobj, ".got.loc");
-      if (sgotloc == NULL)
-       abort ();
+      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;
@@ -1409,46 +1661,31 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   for (s = dynobj->sections; s != NULL; s = s->next)
     {
       const char *name;
-      bfd_boolean strip;
 
       if ((s->flags & SEC_LINKER_CREATED) == 0)
        continue;
 
       /* 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);
-
-      strip = FALSE;
+      name = bfd_section_name (s);
 
-      if (strncmp (name, ".rela", 5) == 0)
-       {
-         if (strcmp (name, ".rela.plt") == 0)
-           relplt = TRUE;
-         else if (strcmp (name, ".rela.got") == 0)
-           relgot = TRUE;
-
-         /* We use the reloc_count field as a counter if we need
-            to copy relocs into the output file.  */
-         s->reloc_count = 0;
-       }
-      else if (strncmp (name, ".plt.", 5) == 0
-              || strncmp (name, ".got.plt.", 9) == 0)
+      if (CONST_STRNEQ (name, ".rela"))
        {
-         if (s->size == 0)
+         if (s->size != 0)
            {
-             /* If we don't need this section, strip it from the output
-                file.  We must create the ".plt*" and ".got.plt*"
-                sections in create_dynamic_sections and/or check_relocs
-                based on a conservative estimate of the PLT relocation
-                count, because the sections must be created before the
-                linker maps input sections to output sections.  The
-                linker does that before size_dynamic_sections, where we
-                compute the exact size of the PLT, so there may be more
-                of these sections than are actually needed.  */
-             strip = TRUE;
+             if (strcmp (name, ".rela.plt") == 0)
+               relplt = TRUE;
+             else if (strcmp (name, ".rela.got") == 0)
+               relgot = TRUE;
+
+             /* We use the reloc_count field as a counter if we need
+                to copy relocs into the output file.  */
+             s->reloc_count = 0;
            }
        }
-      else if (strcmp (name, ".got") != 0
+      else if (! CONST_STRNEQ (name, ".plt.")
+              && ! CONST_STRNEQ (name, ".got.plt.")
+              && strcmp (name, ".got") != 0
               && strcmp (name, ".plt") != 0
               && strcmp (name, ".got.plt") != 0
               && strcmp (name, ".xt.lit.plt") != 0
@@ -1458,13 +1695,24 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
          continue;
        }
 
-      if (strip)
-       s->flags |= SEC_EXCLUDE;
-      else
+      if (s->size == 0)
+       {
+         /* If we don't need this section, strip it from the output
+            file.  We must create the ".plt*" and ".got.plt*"
+            sections in create_dynamic_sections and/or check_relocs
+            based on a conservative estimate of the PLT relocation
+            count, because the sections must be created before the
+            linker maps input sections to output sections.  The
+            linker does that before size_dynamic_sections, where we
+            compute the exact size of the PLT, so there may be more
+            of these sections than are actually needed.  */
+         s->flags |= SEC_EXCLUDE;
+       }
+      else if ((s->flags & SEC_HAS_CONTENTS) != 0)
        {
          /* Allocate memory for the section contents.  */
          s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size);
-         if (s->contents == NULL && s->size != 0)
+         if (s->contents == NULL)
            return FALSE;
        }
     }
@@ -1474,8 +1722,6 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       /* Add the special XTENSA_RTLD relocations now.  The offsets won't be
         known until finish_dynamic_sections, but we need to get the relocs
         in place before they are sorted.  */
-      if (srelgot == NULL)
-       abort ();
       for (chunk = 0; chunk < plt_chunks; chunk++)
        {
          Elf_Internal_Rela irela;
@@ -1501,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->shared)
+      if (bfd_link_executable (info))
        {
          if (!add_dynamic_entry (DT_DEBUG, 0))
            return FALSE;
@@ -1509,8 +1755,7 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 
       if (relplt)
        {
-         if (!add_dynamic_entry (DT_PLTGOT, 0)
-             || !add_dynamic_entry (DT_PLTRELSZ, 0)
+         if (!add_dynamic_entry (DT_PLTRELSZ, 0)
              || !add_dynamic_entry (DT_PLTREL, DT_RELA)
              || !add_dynamic_entry (DT_JMPREL, 0))
            return FALSE;
@@ -1524,7 +1769,8 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
            return FALSE;
        }
 
-      if (!add_dynamic_entry (DT_XTENSA_GOT_LOC_OFF, 0)
+      if (!add_dynamic_entry (DT_PLTGOT, 0)
+         || !add_dynamic_entry (DT_XTENSA_GOT_LOC_OFF, 0)
          || !add_dynamic_entry (DT_XTENSA_GOT_LOC_SZ, 0))
        return FALSE;
     }
@@ -1533,32 +1779,69 @@ elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   return TRUE;
 }
 
-\f
-/* Remove any PT_LOAD segments with no allocated sections.  Prior to
-   binutils 2.13, this function used to remove the non-SEC_ALLOC
-   sections from PT_LOAD segments, but that task has now been moved
-   into elf.c.  We still need this function to remove any empty
-   segments that result, but there's nothing Xtensa-specific about
-   this and it probably ought to be moved into elf.c as well.  */
-
 static bfd_boolean
-elf_xtensa_modify_segment_map (bfd *abfd,
-                              struct bfd_link_info *info ATTRIBUTE_UNUSED)
+elf_xtensa_always_size_sections (bfd *output_bfd,
+                                struct bfd_link_info *info)
 {
-  struct elf_segment_map **m_p;
+  struct elf_xtensa_link_hash_table *htab;
+  asection *tls_sec;
+
+  htab = elf_xtensa_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
 
-  m_p = &elf_tdata (abfd)->segment_map;
-  while (*m_p)
+  tls_sec = htab->elf.tls_sec;
+
+  if (tls_sec && (htab->tlsbase->tls_type & GOT_TLS_ANY) != 0)
     {
-      if ((*m_p)->p_type == PT_LOAD && (*m_p)->count == 0)
-       *m_p = (*m_p)->next;
-      else
-       m_p = &(*m_p)->next;
+      struct elf_link_hash_entry *tlsbase = &htab->tlsbase->elf;
+      struct bfd_link_hash_entry *bh = &tlsbase->root;
+      const struct elf_backend_data *bed = get_elf_backend_data (output_bfd);
+
+      tlsbase->type = STT_TLS;
+      if (!(_bfd_generic_link_add_one_symbol
+           (info, output_bfd, "_TLS_MODULE_BASE_", BSF_LOCAL,
+            tls_sec, 0, NULL, FALSE,
+            bed->collect, &bh)))
+       return FALSE;
+      tlsbase->def_regular = 1;
+      tlsbase->other = STV_HIDDEN;
+      (*bed->elf_backend_hide_symbol) (info, tlsbase, TRUE);
     }
+
   return TRUE;
 }
 
 \f
+/* Return the base VMA address which should be subtracted from real addresses
+   when resolving @dtpoff relocation.
+   This is PT_TLS segment p_vaddr.  */
+
+static bfd_vma
+dtpoff_base (struct bfd_link_info *info)
+{
+  /* If tls_sec is NULL, we should have signalled an error already.  */
+  if (elf_hash_table (info)->tls_sec == NULL)
+    return 0;
+  return elf_hash_table (info)->tls_sec->vma;
+}
+
+/* Return the relocation value for @tpoff relocation
+   if STT_TLS virtual address is ADDRESS.  */
+
+static bfd_vma
+tpoff (struct bfd_link_info *info, bfd_vma address)
+{
+  struct elf_link_hash_table *htab = elf_hash_table (info);
+  bfd_vma base;
+
+  /* If tls_sec is NULL, we should have signalled an error already.  */
+  if (htab->tls_sec == NULL)
+    return 0;
+  base = align_power ((bfd_vma) TCB_SIZE, htab->tls_sec->alignment_power);
+  return address - htab->tls_sec->vma + base;
+}
+
 /* Perform the specified relocation.  The instruction at (contents + address)
    is modified to set one operand to represent the value in "relocation".  The
    operand position is determined by the relocation type recorded in the
@@ -1582,7 +1865,7 @@ elf_xtensa_do_reloc (reloc_howto_type *howto,
   xtensa_isa isa = xtensa_default_isa;
   static xtensa_insnbuf ibuff = NULL;
   static xtensa_insnbuf sbuff = NULL;
-  bfd_vma self_address = 0;
+  bfd_vma self_address;
   bfd_size_type input_size;
   int opnd, slot;
   uint32 newval;
@@ -1595,28 +1878,38 @@ elf_xtensa_do_reloc (reloc_howto_type *howto,
 
   input_size = bfd_get_section_limit (abfd, input_section);
 
+  /* Calculate the PC address for this instruction.  */
+  self_address = (input_section->output_section->vma
+                 + input_section->output_offset
+                 + address);
+
   switch (howto->type)
     {
     case R_XTENSA_NONE:
     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:
       return bfd_reloc_ok;
 
     case R_XTENSA_ASM_EXPAND:
       if (!is_weak_undef)
        {
          /* Check for windowed CALL across a 1GB boundary.  */
-         xtensa_opcode opcode =
-           get_expanded_call_opcode (contents + address,
-                                     input_size - address, 0);
+         opcode = get_expanded_call_opcode (contents + address,
+                                            input_size - address, 0);
          if (is_windowed_call_opcode (opcode))
            {
-             self_address = (input_section->output_section->vma
-                             + input_section->output_offset
-                             + address);
              if ((self_address >> CALL_SEGMENT_BITS)
-                 != (relocation >> CALL_SEGMENT_BITS)) 
+                 != (relocation >> CALL_SEGMENT_BITS))
                {
                  *error_message = "windowed longcall crosses 1GB boundary; "
                    "return may fail";
@@ -1628,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);
@@ -1637,12 +1930,12 @@ elf_xtensa_do_reloc (reloc_howto_type *howto,
 
        /* The CALL needs to be relocated.  Continue below for that part.  */
        address += 3;
+       self_address += 3;
        howto = &elf_howto_table[(unsigned) R_XTENSA_SLOT0_OP ];
       }
       break;
 
     case R_XTENSA_32:
-    case R_XTENSA_PLT:
       {
        bfd_vma x;
        x = bfd_get_32 (abfd, contents + address);
@@ -1650,6 +1943,18 @@ elf_xtensa_do_reloc (reloc_howto_type *howto,
        bfd_put_32 (abfd, x, contents + address);
       }
       return bfd_reloc_ok;
+
+    case R_XTENSA_32_PCREL:
+      bfd_put_32 (abfd, relocation - self_address, contents + address);
+      return bfd_reloc_ok;
+
+    case R_XTENSA_PLT:
+    case R_XTENSA_TLSDESC_FN:
+    case R_XTENSA_TLSDESC_ARG:
+    case R_XTENSA_TLS_DTPOFF:
+    case R_XTENSA_TLS_TPOFF:
+      bfd_put_32 (abfd, relocation, contents + address);
+      return bfd_reloc_ok;
     }
 
   /* Only instruction slot-specific relocations handled below.... */
@@ -1699,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
@@ -1735,11 +2041,6 @@ elf_xtensa_do_reloc (reloc_howto_type *howto,
              return bfd_reloc_dangerous;
            }
 
-         /* Calculate the PC address for this instruction.  */
-         self_address = (input_section->output_section->vma
-                         + input_section->output_offset
-                         + address);
-
          newval = relocation;
        }
     }
@@ -1750,7 +2051,30 @@ elf_xtensa_do_reloc (reloc_howto_type *howto,
       || xtensa_operand_set_field (isa, opcode, opnd, fmt, slot,
                                   sbuff, newval))
     {
-      *error_message = build_encoding_error_message (opcode, relocation);
+      const char *opname = xtensa_opcode_name (isa, opcode);
+      const char *msg;
+
+      msg = "cannot encode";
+      if (is_direct_call_opcode (opcode))
+       {
+         if ((relocation & 0x3) != 0)
+           msg = "misaligned call target";
+         else
+           msg = "call target out of range";
+       }
+      else if (opcode == get_l32r_opcode ())
+       {
+         if ((relocation & 0x3) != 0)
+           msg = "misaligned literal target";
+         else if (is_alt_relocation (howto->type))
+           msg = "literal target out of range (too many literals)";
+         else if (self_address > relocation)
+           msg = "literal target out of range (try using text-section-literals)";
+         else
+           msg = "literal placed after use";
+       }
+
+      *error_message = vsprint_msg (opname, ": %s", strlen (msg) + 2, msg);
       return bfd_reloc_dangerous;
     }
 
@@ -1759,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";
@@ -1784,50 +2108,27 @@ 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;
   if (len > alloc_size)
     {
-      message = (char *) bfd_realloc (message, len);
+      message = (char *) bfd_realloc_or_free (message, len);
       alloc_size = len;
     }
-  if (!is_append)
-    memcpy (message, origmsg, orig_len);
-  vsprintf (message + orig_len, fmt, ap);
-  VA_CLOSE (ap);
-  return message;
-}
-
-
-static char *
-build_encoding_error_message (xtensa_opcode opcode, bfd_vma target_address)
-{
-  const char *opname = xtensa_opcode_name (xtensa_default_isa, opcode);
-  const char *msg;
-
-  msg = "cannot encode";
-  if (is_direct_call_opcode (opcode))
-    {
-      if ((target_address & 0x3) != 0)
-       msg = "misaligned call target";
-      else
-       msg = "call target out of range";
-    }
-  else if (opcode == get_l32r_opcode ())
+  if (message != NULL)
     {
-      if ((target_address & 0x3) != 0)
-       msg = "misaligned literal target";
-      else
-       msg = "literal target out of range";
+      if (!is_append)
+       memcpy (message, origmsg, orig_len);
+      vsprintf (message + orig_len, fmt, ap);
     }
-
-  return vsprint_msg (opname, ": %s", strlen (msg) + 2, msg);
+  va_end (ap);
+  return message;
 }
 
 
@@ -1849,12 +2150,16 @@ 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;
   bfd_boolean is_weak_undef;
 
+  if (!xtensa_default_isa)
+    xtensa_default_isa = xtensa_isa_init (0, 0);
+
   /* ELF relocs are against symbols.  If we are producing relocatable
      output, and the reloc is against an external symbol, the resulting
      reloc will also be against the same symbol.  In such a case, we
@@ -1935,7 +2240,8 @@ bfd_elf_xtensa_reloc (bfd *abfd,
        *error_message = "";
       *error_message = vsprint_msg (*error_message, ": (%s + 0x%lx)",
                                    strlen (symbol->name) + 17,
-                                   symbol->name, reloc_entry->addend);
+                                   symbol->name,
+                                   (unsigned long) reloc_entry->addend);
     }
 
   return flag;
@@ -1945,18 +2251,18 @@ bfd_elf_xtensa_reloc (bfd *abfd,
 /* Set up an entry in the procedure linkage table.  */
 
 static bfd_vma
-elf_xtensa_create_plt_entry (bfd *dynobj,
+elf_xtensa_create_plt_entry (struct bfd_link_info *info,
                             bfd *output_bfd,
                             unsigned reloc_index)
 {
   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;
-  splt = elf_xtensa_get_plt_section (dynobj, chunk);
-  sgotplt = elf_xtensa_get_gotplt_section (dynobj, chunk);
+  splt = elf_xtensa_get_plt_section (info, chunk);
+  sgotplt = elf_xtensa_get_gotplt_section (info, chunk);
   BFD_ASSERT (splt != NULL && sgotplt != NULL);
 
   plt_base = splt->output_section->vma + splt->output_offset;
@@ -1973,23 +2279,206 @@ elf_xtensa_create_plt_entry (bfd *dynobj,
   /* 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;
 }
 
 
+static bfd_boolean get_indirect_call_dest_reg (xtensa_opcode, unsigned *);
+
+static bfd_boolean
+replace_tls_insn (Elf_Internal_Rela *rel,
+                 bfd *abfd,
+                 asection *input_section,
+                 bfd_byte *contents,
+                 bfd_boolean is_ld_model,
+                 char **error_message)
+{
+  static xtensa_insnbuf ibuff = NULL;
+  static xtensa_insnbuf sbuff = NULL;
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_format fmt;
+  xtensa_opcode old_op, new_op;
+  bfd_size_type input_size;
+  int r_type;
+  unsigned dest_reg, src_reg;
+
+  if (ibuff == NULL)
+    {
+      ibuff = xtensa_insnbuf_alloc (isa);
+      sbuff = xtensa_insnbuf_alloc (isa);
+    }
+
+  input_size = bfd_get_section_limit (abfd, input_section);
+
+  /* Read the instruction into a buffer and decode the opcode.  */
+  xtensa_insnbuf_from_chars (isa, ibuff, contents + rel->r_offset,
+                            input_size - rel->r_offset);
+  fmt = xtensa_format_decode (isa, ibuff);
+  if (fmt == XTENSA_UNDEFINED)
+    {
+      *error_message = "cannot decode instruction format";
+      return FALSE;
+    }
+
+  BFD_ASSERT (xtensa_format_num_slots (isa, fmt) == 1);
+  xtensa_format_get_slot (isa, fmt, 0, ibuff, sbuff);
+
+  old_op = xtensa_opcode_decode (isa, fmt, 0, sbuff);
+  if (old_op == XTENSA_UNDEFINED)
+    {
+      *error_message = "cannot decode instruction opcode";
+      return FALSE;
+    }
+
+  r_type = ELF32_R_TYPE (rel->r_info);
+  switch (r_type)
+    {
+    case R_XTENSA_TLS_FUNC:
+    case R_XTENSA_TLS_ARG:
+      if (old_op != get_l32r_opcode ()
+         || xtensa_operand_get_field (isa, old_op, 0, fmt, 0,
+                                      sbuff, &dest_reg) != 0)
+       {
+         *error_message = "cannot extract L32R destination for TLS access";
+         return FALSE;
+       }
+      break;
+
+    case R_XTENSA_TLS_CALL:
+      if (! get_indirect_call_dest_reg (old_op, &dest_reg)
+         || xtensa_operand_get_field (isa, old_op, 0, fmt, 0,
+                                      sbuff, &src_reg) != 0)
+       {
+         *error_message = "cannot extract CALLXn operands for TLS access";
+         return FALSE;
+       }
+      break;
+
+    default:
+      abort ();
+    }
+
+  if (is_ld_model)
+    {
+      switch (r_type)
+       {
+       case R_XTENSA_TLS_FUNC:
+       case R_XTENSA_TLS_ARG:
+         /* Change the instruction to a NOP (or "OR a1, a1, a1" for older
+            versions of Xtensa).  */
+         new_op = xtensa_opcode_lookup (isa, "nop");
+         if (new_op == XTENSA_UNDEFINED)
+           {
+             new_op = xtensa_opcode_lookup (isa, "or");
+             if (new_op == XTENSA_UNDEFINED
+                 || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
+                 || xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
+                                              sbuff, 1) != 0
+                 || xtensa_operand_set_field (isa, new_op, 1, fmt, 0,
+                                              sbuff, 1) != 0
+                 || xtensa_operand_set_field (isa, new_op, 2, fmt, 0,
+                                              sbuff, 1) != 0)
+               {
+                 *error_message = "cannot encode OR for TLS access";
+                 return FALSE;
+               }
+           }
+         else
+           {
+             if (xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0)
+               {
+                 *error_message = "cannot encode NOP for TLS access";
+                 return FALSE;
+               }
+           }
+         break;
+
+       case R_XTENSA_TLS_CALL:
+         /* Read THREADPTR into the CALLX's return value register.  */
+         new_op = xtensa_opcode_lookup (isa, "rur.threadptr");
+         if (new_op == XTENSA_UNDEFINED
+             || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
+             || xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
+                                          sbuff, dest_reg + 2) != 0)
+           {
+             *error_message = "cannot encode RUR.THREADPTR for TLS access";
+             return FALSE;
+           }
+         break;
+       }
+    }
+  else
+    {
+      switch (r_type)
+       {
+       case R_XTENSA_TLS_FUNC:
+         new_op = xtensa_opcode_lookup (isa, "rur.threadptr");
+         if (new_op == XTENSA_UNDEFINED
+             || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
+             || xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
+                                          sbuff, dest_reg) != 0)
+           {
+             *error_message = "cannot encode RUR.THREADPTR for TLS access";
+             return FALSE;
+           }
+         break;
+
+       case R_XTENSA_TLS_ARG:
+         /* Nothing to do.  Keep the original L32R instruction.  */
+         return TRUE;
+
+       case R_XTENSA_TLS_CALL:
+         /* Add the CALLX's src register (holding the THREADPTR value)
+            to the first argument register (holding the offset) and put
+            the result in the CALLX's return value register.  */
+         new_op = xtensa_opcode_lookup (isa, "add");
+         if (new_op == XTENSA_UNDEFINED
+             || xtensa_opcode_encode (isa, fmt, 0, sbuff, new_op) != 0
+             || xtensa_operand_set_field (isa, new_op, 0, fmt, 0,
+                                          sbuff, dest_reg + 2) != 0
+             || xtensa_operand_set_field (isa, new_op, 1, fmt, 0,
+                                          sbuff, dest_reg + 2) != 0
+             || xtensa_operand_set_field (isa, new_op, 2, fmt, 0,
+                                          sbuff, src_reg) != 0)
+           {
+             *error_message = "cannot encode ADD for TLS access";
+             return FALSE;
+           }
+         break;
+       }
+    }
+
+  xtensa_format_set_slot (isa, fmt, 0, ibuff, sbuff);
+  xtensa_insnbuf_to_chars (isa, ibuff, contents + rel->r_offset,
+                          input_size - rel->r_offset);
+
+  return TRUE;
+}
+
+
+#define IS_XTENSA_TLS_RELOC(R_TYPE) \
+  ((R_TYPE) == R_XTENSA_TLSDESC_FN \
+   || (R_TYPE) == R_XTENSA_TLSDESC_ARG \
+   || (R_TYPE) == R_XTENSA_TLS_DTPOFF \
+   || (R_TYPE) == R_XTENSA_TLS_TPOFF \
+   || (R_TYPE) == R_XTENSA_TLS_FUNC \
+   || (R_TYPE) == R_XTENSA_TLS_ARG \
+   || (R_TYPE) == R_XTENSA_TLS_CALL)
+
 /* Relocate an Xtensa ELF section.  This is invoked by the linker for
    both relocatable and final links.  */
 
@@ -2003,32 +2492,35 @@ elf_xtensa_relocate_section (bfd *output_bfd,
                             Elf_Internal_Sym *local_syms,
                             asection **local_sections)
 {
+  struct elf_xtensa_link_hash_table *htab;
   Elf_Internal_Shdr *symtab_hdr;
   Elf_Internal_Rela *rel;
   Elf_Internal_Rela *relend;
   struct elf_link_hash_entry **sym_hashes;
-  asection *srelgot, *srelplt;
-  bfd *dynobj;
   property_table_entry *lit_table = 0;
   int ltblsize = 0;
+  char *local_got_tls_types;
   char *error_message = NULL;
   bfd_size_type input_size;
+  int tls_type;
 
   if (!xtensa_default_isa)
     xtensa_default_isa = xtensa_isa_init (0, 0);
 
-  dynobj = elf_hash_table (info)->dynobj;
-  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
-  sym_hashes = elf_sym_hashes (input_bfd);
-
-  srelgot = NULL;
-  srelplt = NULL;
-  if (dynobj)
+  if (!is_xtensa_elf (input_bfd))
     {
-      srelgot = bfd_get_section_by_name (dynobj, ".rela.got");;
-      srelplt = bfd_get_section_by_name (dynobj, ".rela.plt");
+      bfd_set_error (bfd_error_wrong_format);
+      return FALSE;
     }
 
+  htab = elf_xtensa_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
+
+  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+  sym_hashes = elf_sym_hashes (input_bfd);
+  local_got_tls_types = elf_xtensa_local_got_tls_type (input_bfd);
+
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       ltblsize = xtensa_read_table_entries (input_bfd, input_section,
@@ -2049,29 +2541,80 @@ elf_xtensa_relocate_section (bfd *output_bfd,
       unsigned long r_symndx;
       struct elf_link_hash_entry *h;
       Elf_Internal_Sym *sym;
+      char sym_type;
+      const char *name;
       asection *sec;
       bfd_vma relocation;
       bfd_reloc_status_type r;
       bfd_boolean is_weak_undef;
       bfd_boolean unresolved_reloc;
       bfd_boolean warned;
+      bfd_boolean dynamic_symbol;
 
       r_type = ELF32_R_TYPE (rel->r_info);
       if (r_type == (int) R_XTENSA_GNU_VTINHERIT
          || r_type == (int) R_XTENSA_GNU_VTENTRY)
        continue;
 
-      if (r_type < 0 || r_type >= (int) R_XTENSA_max)
-       {
-         bfd_set_error (bfd_error_bad_value);
-         return FALSE;
+      if (r_type < 0 || r_type >= (int) R_XTENSA_max)
+       {
+         bfd_set_error (bfd_error_bad_value);
+         return FALSE;
+       }
+      howto = &elf_howto_table[r_type];
+
+      r_symndx = ELF32_R_SYM (rel->r_info);
+
+      h = NULL;
+      sym = NULL;
+      sec = NULL;
+      is_weak_undef = FALSE;
+      unresolved_reloc = FALSE;
+      warned = FALSE;
+
+      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
+            an addend stored in the contents.  Take it out of there
+            and move it back into the addend field of the reloc.  */
+         rel->r_addend += bfd_get_32 (input_bfd, contents + rel->r_offset);
+         bfd_put_32 (input_bfd, 0, contents + rel->r_offset);
+       }
+
+      if (r_symndx < symtab_hdr->sh_info)
+       {
+         sym = local_syms + r_symndx;
+         sym_type = ELF32_ST_TYPE (sym->st_info);
+         sec = local_sections[r_symndx];
+         relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+       }
+      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, ignored);
+
+         if (relocation == 0
+             && !unresolved_reloc
+             && h->root.type == bfd_link_hash_undefweak)
+           is_weak_undef = TRUE;
+
+         sym_type = h->type;
        }
-      howto = &elf_howto_table[r_type];
 
-      r_symndx = ELF32_R_SYM (rel->r_info);
+      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);
+
          /* This is a relocatable link.
             1) If the reloc is against a section symbol, adjust
             according to the output section.
@@ -2086,23 +2629,23 @@ elf_xtensa_relocate_section (bfd *output_bfd,
              if (!do_fix_for_relocatable_link (rel, input_bfd, input_section,
                                                contents))
                return FALSE;
-             r_type = ELF32_R_TYPE (rel->r_info);
            }
 
+         dest_addr = sym_sec->output_section->vma + sym_sec->output_offset
+           + get_elf_r_symndx_offset (input_bfd, r_symndx) + rel->r_addend;
+
          if (r_type == R_XTENSA_ASM_SIMPLIFY)
            {
-             char *error_message = NULL;
+             error_message = NULL;
              /* Convert ASM_SIMPLIFY into the simpler relocation
                 so that they never escape a relaxing link.  */
              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);
            }
 
@@ -2125,25 +2668,38 @@ elf_xtensa_relocate_section (bfd *output_bfd,
             to work around problems with DWARF in relocatable links
             with some previous version of BFD.  Now we can't easily get
             rid of the hack without breaking backward compatibility.... */
-         if (rel->r_addend)
+         r = bfd_reloc_ok;
+         howto = &elf_howto_table[r_type];
+         if (howto->partial_inplace && rel->r_addend)
+           {
+             r = elf_xtensa_do_reloc (howto, input_bfd, input_section,
+                                      rel->r_addend, contents,
+                                      rel->r_offset, FALSE,
+                                      &error_message);
+             rel->r_addend = 0;
+           }
+         else
            {
-             howto = &elf_howto_table[r_type];
-             if (howto->partial_inplace)
+             /* Put the correct bits in the target instruction, even
+                though the relocation will still be present in the output
+                file.  This makes disassembly clearer, as well as
+                allowing loadable kernel modules to work without needing
+                relocations on anything other than calls and l32r's.  */
+
+             /* If it is not in the same section, there is nothing we can do.  */
+             if (r_type >= R_XTENSA_SLOT0_OP && r_type <= R_XTENSA_SLOT14_OP &&
+                 sym_sec->output_section == input_section->output_section)
                {
                  r = elf_xtensa_do_reloc (howto, input_bfd, input_section,
-                                          rel->r_addend, contents,
+                                          dest_addr, contents,
                                           rel->r_offset, FALSE,
                                           &error_message);
-                 if (r != bfd_reloc_ok)
-                   {
-                     if (!((*info->callbacks->reloc_dangerous)
-                           (info, error_message, input_bfd, input_section,
-                            rel->r_offset)))
-                       return FALSE;
-                   }
-                 rel->r_addend = 0;
                }
            }
+         if (r != bfd_reloc_ok)
+           (*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;
@@ -2151,94 +2707,81 @@ elf_xtensa_relocate_section (bfd *output_bfd,
 
       /* This is a final link.  */
 
-      h = NULL;
-      sym = NULL;
-      sec = NULL;
-      is_weak_undef = FALSE;
-      unresolved_reloc = FALSE;
-      warned = FALSE;
-
-      if (howto->partial_inplace)
-       {
-         /* Because R_XTENSA_32 was made partial_inplace to fix some
-            problems with DWARF info in partial links, there may be
-            an addend stored in the contents.  Take it out of there
-            and move it back into the addend field of the reloc.  */
-         rel->r_addend += bfd_get_32 (input_bfd, contents + rel->r_offset);
-         bfd_put_32 (input_bfd, 0, contents + rel->r_offset);
-       }
-
-      if (r_symndx < symtab_hdr->sh_info)
-       {
-         sym = local_syms + r_symndx;
-         sec = local_sections[r_symndx];
-         relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
-       }
-      else
-       {
-         RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
-                                  r_symndx, symtab_hdr, sym_hashes,
-                                  h, sec, relocation,
-                                  unresolved_reloc, warned);
-
-         if (relocation == 0
-             && !unresolved_reloc
-             && h->root.type == bfd_link_hash_undefweak)
-           is_weak_undef = TRUE;
-       }
-
       if (relaxing_section)
        {
          /* Check if this references a section in another input file.  */
          do_fix_for_final_link (rel, input_bfd, input_section, contents,
                                 &relocation);
-
-         /* Update some already cached values.  */
-         r_type = ELF32_R_TYPE (rel->r_info);
-         howto = &elf_howto_table[r_type];
        }
 
       /* Sanity check the address.  */
       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;
        }
 
-      /* Generate dynamic relocations.  */
-      if (elf_hash_table (info)->dynamic_sections_created)
+      if (h != NULL)
+       name = h->root.root.string;
+      else
        {
-         bfd_boolean dynamic_symbol = xtensa_elf_dynamic_symbol_p (h, info);
+         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 (sec);
+       }
 
-         if (dynamic_symbol && is_operand_relocation (r_type))
-           {
-             /* This is an error.  The symbol's real value won't be known
-                until runtime and it's likely to be out of range anyway.  */
-             const char *name = h->root.root.string;
-             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;
-           }
-         else if ((r_type == R_XTENSA_32 || r_type == R_XTENSA_PLT)
-                  && (input_section->flags & SEC_ALLOC) != 0
-                  && (dynamic_symbol || info->shared))
+      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
+           ((sym_type == STT_TLS
+             /* 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,
+            (uint64_t) rel->r_offset,
+            howto->name,
+            name);
+       }
+
+      dynamic_symbol = elf_xtensa_dynamic_symbol_p (h, info);
+
+      tls_type = GOT_UNKNOWN;
+      if (h)
+       tls_type = elf_xtensa_hash_entry (h)->tls_type;
+      else if (local_got_tls_types)
+       tls_type = local_got_tls_types [r_symndx];
+
+      switch (r_type)
+       {
+       case R_XTENSA_32:
+       case R_XTENSA_PLT:
+         if (elf_hash_table (info)->dynamic_sections_created
+             && (input_section->flags & SEC_ALLOC) != 0
+             && (dynamic_symbol || bfd_link_pic (info)))
            {
              Elf_Internal_Rela outrel;
              bfd_byte *loc;
              asection *srel;
 
              if (dynamic_symbol && r_type == R_XTENSA_PLT)
-               srel = srelplt;
+               srel = htab->elf.srelplt;
              else
-               srel = srelgot;
+               srel = htab->elf.srelgot;
 
              BFD_ASSERT (srel != NULL);
 
@@ -2261,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)
@@ -2287,17 +2829,21 @@ elf_xtensa_relocate_section (bfd *output_bfd,
                             contents of the literal entry to the address of
                             the PLT entry.  */
                          relocation =
-                           elf_xtensa_create_plt_entry (dynobj, output_bfd,
+                           elf_xtensa_create_plt_entry (info, output_bfd,
                                                         srel->reloc_count);
                        }
                      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
@@ -2306,6 +2852,151 @@ elf_xtensa_relocate_section (bfd *output_bfd,
              BFD_ASSERT (sizeof (Elf32_External_Rela) * srel->reloc_count
                          <= srel->size);
            }
+         else if (r_type == R_XTENSA_ASM_EXPAND && dynamic_symbol)
+           {
+             /* This should only happen for non-PIC code, which is not
+                supposed to be used on systems with dynamic linking.
+                Just ignore these relocations.  */
+             continue;
+           }
+         break;
+
+       case R_XTENSA_TLS_TPOFF:
+         /* Switch to LE model for local symbols in an executable.  */
+         if (! bfd_link_pic (info) && ! dynamic_symbol)
+           {
+             relocation = tpoff (info, relocation);
+             break;
+           }
+         /* fall through */
+
+       case R_XTENSA_TLSDESC_FN:
+       case R_XTENSA_TLSDESC_ARG:
+         {
+           if (r_type == R_XTENSA_TLSDESC_FN)
+             {
+               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 (bfd_link_pic (info))
+                 {
+                   if ((tls_type & GOT_TLS_IE) != 0)
+                     r_type = R_XTENSA_TLS_TPOFF;
+                 }
+               else
+                 {
+                   r_type = R_XTENSA_TLS_TPOFF;
+                   if (! dynamic_symbol)
+                     {
+                       relocation = tpoff (info, relocation);
+                       break;
+                     }
+                 }
+             }
+
+           if (r_type == R_XTENSA_NONE)
+             /* Nothing to do here; skip to the next reloc.  */
+             continue;
+
+           if (! elf_hash_table (info)->dynamic_sections_created)
+             {
+               error_message =
+                 _("TLS relocation invalid without dynamic sections");
+               (*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->elf.srelgot;
+               int indx;
+
+               outrel.r_offset = (input_section->output_section->vma
+                                  + input_section->output_offset
+                                  + rel->r_offset);
+
+               /* Complain if the relocation is in a read-only section
+                  and not in a literal pool.  */
+               if ((input_section->flags & SEC_READONLY) != 0
+                   && ! elf_xtensa_in_literal_pool (lit_table, ltblsize,
+                                                    outrel.r_offset))
+                 {
+                   error_message =
+                     _("dynamic relocation in read-only section");
+                   (*info->callbacks->reloc_dangerous)
+                     (info, error_message,
+                      input_bfd, input_section, rel->r_offset);
+                 }
+
+               indx = h && h->dynindx != -1 ? h->dynindx : 0;
+               if (indx == 0)
+                 outrel.r_addend = relocation - dtpoff_base (info);
+               else
+                 outrel.r_addend = 0;
+               rel->r_addend = 0;
+
+               outrel.r_info = ELF32_R_INFO (indx, r_type);
+               relocation = 0;
+               unresolved_reloc = FALSE;
+
+               BFD_ASSERT (srel);
+               loc = (srel->contents
+                      + srel->reloc_count++ * sizeof (Elf32_External_Rela));
+               bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
+               BFD_ASSERT (sizeof (Elf32_External_Rela) * srel->reloc_count
+                           <= srel->size);
+             }
+         }
+         break;
+
+       case R_XTENSA_TLS_DTPOFF:
+         if (! bfd_link_pic (info))
+           /* Switch from LD model to LE model.  */
+           relocation = tpoff (info, relocation);
+         else
+           relocation -= dtpoff_base (info);
+         break;
+
+       case R_XTENSA_TLS_FUNC:
+       case R_XTENSA_TLS_ARG:
+       case R_XTENSA_TLS_CALL:
+         /* Check if optimizing to IE or LE model.  */
+         if ((tls_type & GOT_TLS_IE) != 0)
+           {
+             bfd_boolean is_ld_model =
+               (h && elf_xtensa_hash_entry (h) == htab->tlsbase);
+             if (! replace_tls_insn (rel, input_bfd, input_section, contents,
+                                     is_ld_model, &error_message))
+               (*info->callbacks->reloc_dangerous)
+                 (info, error_message,
+                  input_bfd, input_section, rel->r_offset);
+
+             if (r_type != R_XTENSA_TLS_ARG || is_ld_model)
+               {
+                 /* Skip subsequent relocations on the same instruction.  */
+                 while (rel + 1 < relend && rel[1].r_offset == rel->r_offset)
+                   rel++;
+               }
+           }
+         continue;
+
+       default:
+         if (elf_hash_table (info)->dynamic_sections_created
+             && dynamic_symbol && (is_operand_relocation (r_type)
+                                   || r_type == R_XTENSA_32_PCREL))
+           {
+             error_message =
+               vsprint_msg ("invalid relocation for dynamic symbol", ": %s",
+                            strlen (name) + 2, name);
+             (*info->callbacks->reloc_dangerous)
+               (info, error_message, input_bfd, input_section, rel->r_offset);
+             continue;
+           }
+         break;
        }
 
       /* Dynamic relocs are not propagated for SEC_DEBUGGING sections
@@ -2313,13 +3004,24 @@ 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 relocation against symbol `%s'"),
-          input_bfd,
-          input_section,
-          (long) rel->r_offset,
-          h->root.root.string);
+              && 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,
+            (uint64_t) rel->r_offset,
+            howto->name,
+            name);
+         return FALSE;
+       }
+
+      /* TLS optimizations may have changed r_type; update "howto".  */
+      howto = &elf_howto_table[r_type];
 
       /* There's no point in calling bfd_perform_relocation here.
         Just go directly to our "special function".  */
@@ -2330,41 +3032,23 @@ elf_xtensa_relocate_section (bfd *output_bfd,
 
       if (r != bfd_reloc_ok && !warned)
        {
-         const char *name;
-
          BFD_ASSERT (r == bfd_reloc_dangerous || r == bfd_reloc_other);
          BFD_ASSERT (error_message != NULL);
 
-         if (h)
-           name = h->root.root.string;
+         if (rel->r_addend == 0)
+           error_message = vsprint_msg (error_message, ": %s",
+                                        strlen (name) + 2, name);
          else
-           {
-             name = bfd_elf_string_from_elf_section
-               (input_bfd, symtab_hdr->sh_link, sym->st_name);
-             if (name && *name == '\0')
-               name = bfd_section_name (input_bfd, sec);
-           }
-         if (name)
-           {
-             if (rel->r_addend == 0)
-               error_message = vsprint_msg (error_message, ": %s",
-                                            strlen (name) + 2, name);
-             else
-               error_message = vsprint_msg (error_message, ": (%s+0x%x)",
-                                            strlen (name) + 22,
-                                            name, rel->r_addend);
-           }
+           error_message = vsprint_msg (error_message, ": (%s+0x%x)",
+                                        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;
@@ -2380,17 +3064,22 @@ elf_xtensa_finish_dynamic_symbol (bfd *output_bfd ATTRIBUTE_UNUSED,
                                  struct elf_link_hash_entry *h,
                                  Elf_Internal_Sym *sym)
 {
-  if (h->needs_plt
-      && !h->def_regular)
+  if (h->needs_plt && !h->def_regular)
     {
       /* Mark the symbol as undefined, rather than as defined in
         the .plt section.  Leave the value alone.  */
       sym->st_shndx = SHN_UNDEF;
+      /* If the symbol is weak, we do need to clear the value.
+        Otherwise, the PLT entry would provide a definition for
+        the symbol even if the symbol wasn't defined anywhere,
+        and so the symbol would never be NULL.  */
+      if (!h->ref_regular_nonweak)
+       sym->st_value = 0;
     }
 
   /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute.  */
-  if (strcmp (h->root.root.string, "_DYNAMIC") == 0
-      || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)
+  if (h == elf_hash_table (info)->hdynamic
+      || h == elf_hash_table (info)->hgot)
     sym->st_shndx = SHN_ABS;
 
   return TRUE;
@@ -2423,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;
     }
@@ -2439,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;
     }
@@ -2460,18 +3148,18 @@ elf_xtensa_combine_prop_entries (bfd *output_bfd,
 
   for (n = 0; n < num; n++)
     {
-      bfd_boolean remove = FALSE;
+      bfd_boolean remove_entry = FALSE;
 
       if (table[n].size == 0)
-       remove = TRUE;
-      else if (n > 0 &&
-              (table[n-1].address + table[n-1].size == table[n].address))
+       remove_entry = TRUE;
+      else if (n > 0
+              && (table[n-1].address + table[n-1].size == table[n].address))
        {
          table[n-1].size += table[n].size;
-         remove = TRUE;
+         remove_entry = TRUE;
        }
 
-      if (remove)
+      if (remove_entry)
        {
          for (m = n; m < num - 1; m++)
            {
@@ -2516,21 +3204,26 @@ static bfd_boolean
 elf_xtensa_finish_dynamic_sections (bfd *output_bfd,
                                    struct bfd_link_info *info)
 {
+  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;
+  int num_xtlit_entries = 0;
 
   if (! elf_hash_table (info)->dynamic_sections_created)
     return TRUE;
 
+  htab = elf_xtensa_hash_table (info);
+  if (htab == NULL)
+    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 = bfd_get_section_by_name (dynobj, ".got");
+  sgot = htab->elf.sgot;
   if (sgot)
     {
       BFD_ASSERT (sgot->size == 4);
@@ -2542,20 +3235,18 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd,
                    sgot->contents);
     }
 
-  srelplt = bfd_get_section_by_name (dynobj, ".rela.plt");
+  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 = bfd_get_section_by_name (dynobj, ".rela.got");;
-      BFD_ASSERT (srelgot != NULL);
-
-      spltlittbl = bfd_get_section_by_name (dynobj, ".xt.lit.plt");
-      BFD_ASSERT (spltlittbl != NULL);
+      spltlittbl = htab->spltlittbl;
+      BFD_ASSERT (srelgot != NULL && spltlittbl != NULL);
 
       /* Find the first XTENSA_RTLD relocation.  Presumably the rest
         of them follow immediately after....  */
@@ -2576,7 +3267,7 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd,
        {
          int chunk_entries = 0;
 
-         sgotplt = elf_xtensa_get_gotplt_section (dynobj, chunk);
+         sgotplt = elf_xtensa_get_gotplt_section (info, chunk);
          BFD_ASSERT (sgotplt != NULL);
 
          /* Emit special RTLD relocations for the first two entries in
@@ -2619,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
@@ -2641,23 +3324,32 @@ 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 = bfd_get_section_by_name (dynobj, ".got.loc");
-  BFD_ASSERT (sxtlit && sgotloc);
-  num_xtlit_entries =
-    elf_xtensa_combine_prop_entries (output_bfd, sxtlit, sgotloc);
-  if (num_xtlit_entries < 0)
-    return FALSE;
+  sgotloc = htab->sgotloc;
+  BFD_ASSERT (sgotloc);
+  if (sxtlit)
+    {
+      num_xtlit_entries =
+       elf_xtensa_combine_prop_entries (output_bfd, sxtlit, sgotloc);
+      if (num_xtlit_entries < 0)
+       return FALSE;
+    }
 
   dyncon = (Elf32_External_Dyn *) sdyn->contents;
   dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->size);
   for (; dyncon < dynconend; dyncon++)
     {
       Elf_Internal_Dyn dyn;
-      const char *name;
-      asection *s;
 
       bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn);
 
@@ -2671,35 +3363,22 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd,
          break;
 
        case DT_XTENSA_GOT_LOC_OFF:
-         name = ".got.loc";
-         goto get_vma;
+         dyn.d_un.d_ptr = (htab->sgotloc->output_section->vma
+                           + htab->sgotloc->output_offset);
+         break;
+
        case DT_PLTGOT:
-         name = ".got";
-         goto get_vma;
-       case DT_JMPREL:
-         name = ".rela.plt";
-       get_vma:
-         s = bfd_get_section_by_name (output_bfd, name);
-         BFD_ASSERT (s);
-         dyn.d_un.d_ptr = s->vma;
+         dyn.d_un.d_ptr = (htab->elf.sgot->output_section->vma
+                           + htab->elf.sgot->output_offset);
          break;
 
-       case DT_PLTRELSZ:
-         s = bfd_get_section_by_name (output_bfd, ".rela.plt");
-         BFD_ASSERT (s);
-         dyn.d_un.d_val = s->size;
+       case DT_JMPREL:
+         dyn.d_un.d_ptr = (htab->elf.srelplt->output_section->vma
+                           + htab->elf.srelplt->output_offset);
          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.  */
-         s = bfd_get_section_by_name (output_bfd, ".rela.plt");
-         if (s)
-           dyn.d_un.d_val -= s->size;
+       case DT_PLTRELSZ:
+         dyn.d_un.d_val = htab->elf.srelplt->size;
          break;
        }
 
@@ -2716,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.  */
@@ -2737,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;
@@ -2757,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;
@@ -2828,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))
     {
@@ -2841,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))
     {
@@ -2871,18 +3554,22 @@ elf_xtensa_discard_info_for_section (bfd *abfd,
                                     asection *sec)
 {
   bfd_byte *contents;
-  bfd_vma section_size;
   bfd_vma offset, actual_offset;
-  size_t removed_bytes = 0;
-
-  section_size = sec->size;
-  if (section_size == 0 || section_size % 8 != 0)
-    return FALSE;
+  bfd_size_type removed_bytes = 0;
+  bfd_size_type entry_size;
 
   if (sec->output_section
       && bfd_is_abs_section (sec->output_section))
     return FALSE;
 
+  if (xtensa_is_proptable_section (sec))
+    entry_size = 12;
+  else
+    entry_size = 8;
+
+  if (sec->size == 0 || sec->size % entry_size != 0)
+    return FALSE;
+
   contents = retrieve_contents (abfd, sec, info->keep_memory);
   if (!contents)
     return FALSE;
@@ -2894,10 +3581,15 @@ elf_xtensa_discard_info_for_section (bfd *abfd,
       return FALSE;
     }
 
+  /* Sort the relocations.  They should already be in order when
+     relaxation is enabled, but it might not be.  */
+  qsort (cookie->rels, sec->reloc_count, sizeof (Elf_Internal_Rela),
+        internal_reloc_compare);
+
   cookie->rel = cookie->rels;
   cookie->relend = cookie->rels + sec->reloc_count;
 
-  for (offset = 0; offset < section_size; offset += 8)
+  for (offset = 0; offset < sec->size; offset += entry_size)
     {
       actual_offset = offset - removed_bytes;
 
@@ -2921,11 +3613,11 @@ elf_xtensa_discard_info_for_section (bfd *abfd,
              if (ELF32_R_TYPE (cookie->rel->r_info) != R_XTENSA_NONE)
                {
                  /* Shift the contents up.  */
-                 if (offset + 8 < section_size)
+                 if (offset + entry_size < sec->size)
                    memmove (&contents[actual_offset],
-                            &contents[actual_offset+8],
-                            section_size - offset - 8);
-                 removed_bytes += 8;
+                            &contents[actual_offset + entry_size],
+                            sec->size - offset - entry_size);
+                 removed_bytes += entry_size;
                }
 
              /* Remove this relocation.  */
@@ -2958,24 +3650,21 @@ elf_xtensa_discard_info_for_section (bfd *abfd,
        }
 
       /* Clear the removed bytes.  */
-      memset (&contents[section_size - removed_bytes], 0, removed_bytes);
+      memset (&contents[sec->size - removed_bytes], 0, removed_bytes);
 
       pin_contents (sec, contents);
       pin_internal_relocs (sec, cookie->rels);
 
       /* Shrink size.  */
-      sec->size = section_size - removed_bytes;
+      if (sec->rawsize == 0)
+       sec->rawsize = sec->size;
+      sec->size -= removed_bytes;
 
       if (xtensa_is_littable_section (sec))
        {
-         bfd *dynobj = elf_hash_table (info)->dynobj;
-         if (dynobj)
-           {
-             asection *sgotloc =
-               bfd_get_section_by_name (dynobj, ".got.loc");
-             if (sgotloc)
-               sgotloc->size -= removed_bytes;
-           }
+         asection *sgotloc = elf_xtensa_hash_table (info)->sgotloc;
+         if (sgotloc)
+           sgotloc->size -= removed_bytes;
        }
     }
   else
@@ -3015,6 +3704,19 @@ elf_xtensa_ignore_discarded_relocs (asection *sec)
   return xtensa_is_property_section (sec);
 }
 
+
+static unsigned int
+elf_xtensa_action_discarded (asection *sec)
+{
+  if (strcmp (".xt_except_table", sec->name) == 0)
+    return 0;
+
+  if (strcmp (".xt_except_desc", sec->name) == 0)
+    return 0;
+
+  return _bfd_elf_default_action_discarded (sec);
+}
+
 \f
 /* Support for core dump NOTE sections.  */
 
@@ -3028,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_pid = bfd_get_32 (abfd, note->descdata + 24);
+  elf_tdata (abfd)->core->lwpid = bfd_get_32 (abfd, note->descdata + 24);
 
   /* pr_reg */
   offset = 72;
@@ -3052,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);
     }
 
@@ -3063,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] == ' ')
@@ -3137,6 +3839,29 @@ is_windowed_call_opcode (xtensa_opcode opcode)
 }
 
 
+static bfd_boolean
+get_indirect_call_dest_reg (xtensa_opcode opcode, unsigned *pdst)
+{
+  unsigned dst = (unsigned) -1;
+
+  init_call_opcodes ();
+  if (opcode == callx0_op)
+    dst = 0;
+  else if (opcode == callx4_op)
+    dst = 4;
+  else if (opcode == callx8_op)
+    dst = 8;
+  else if (opcode == callx12_op)
+    dst = 12;
+
+  if (dst == (unsigned) -1)
+    return FALSE;
+
+  *pdst = dst;
+  return TRUE;
+}
+
+
 static xtensa_opcode
 get_const16_opcode (void)
 {
@@ -3179,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)
 {
@@ -3309,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;
@@ -3348,7 +4100,7 @@ is_operand_relocation (int r_type)
   return FALSE;
 }
 
-      
+
 #define MIN_INSN_LENGTH 2
 
 /* Return 0 if it fails to decode.  */
@@ -3379,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.  */
@@ -3440,25 +4214,38 @@ check_loop_aligned (bfd_byte *contents,
                    bfd_vma address)
 {
   bfd_size_type loop_len, insn_len;
-  xtensa_opcode opcode =
-    insn_decode_opcode (contents, content_length, offset, 0);
-  BFD_ASSERT (opcode != XTENSA_UNDEFINED);
-  if (opcode != XTENSA_UNDEFINED)
-    return FALSE;
-  BFD_ASSERT (xtensa_opcode_is_loop (xtensa_default_isa, opcode));
-  if (!xtensa_opcode_is_loop (xtensa_default_isa, opcode))
-    return FALSE;
+  xtensa_opcode opcode;
 
-  loop_len = insn_decode_len (contents, content_length, offset);
-  BFD_ASSERT (loop_len != 0);
-  if (loop_len == 0)
-    return FALSE;
+  opcode = insn_decode_opcode (contents, content_length, offset, 0);
+  if (opcode == XTENSA_UNDEFINED
+      || xtensa_opcode_is_loop (xtensa_default_isa, opcode) != 1)
+    {
+      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);
-  BFD_ASSERT (insn_len != 0);
-  if (insn_len == 0)
-    return FALSE;
+  if (loop_len == 0 || insn_len == 0)
+    {
+      BFD_ASSERT (FALSE);
+      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);
 }
 
@@ -3569,60 +4356,31 @@ struct string_pair widenable[] =
 };
 
 
-/* Attempt to narrow an instruction.  Return true if the narrowing is
-   valid.  If the do_it parameter is non-zero, then perform the action
-   in-place directly into the contents.  Otherwise, do not modify the
-   contents.  The set of valid narrowing are specified by a string table
+/* Check if an instruction can be "narrowed", i.e., changed from a standard
+   3-byte instruction to a 2-byte "density" instruction.  If it is valid,
+   return the instruction buffer holding the narrow instruction.  Otherwise,
+   return 0.  The set of valid narrowing are specified by a string table
    but require some special case operand checks in some cases.  */
 
-static bfd_boolean
-narrow_instruction (bfd_byte *contents,
-                   bfd_size_type content_length,
-                   bfd_size_type offset,
-                   bfd_boolean do_it)
+static xtensa_insnbuf
+can_narrow_instruction (xtensa_insnbuf slotbuf,
+                       xtensa_format fmt,
+                       xtensa_opcode opcode)
 {
-  xtensa_opcode opcode;
-  bfd_size_type insn_len, opi;
-  xtensa_isa isa = xtensa_default_isa;
-  xtensa_format fmt, o_fmt;
-
-  static xtensa_insnbuf insnbuf = NULL;
-  static xtensa_insnbuf slotbuf = NULL;
-  static xtensa_insnbuf o_insnbuf = NULL;
-  static xtensa_insnbuf o_slotbuf = NULL;
-
-  if (insnbuf == NULL)
-    {
-      insnbuf = xtensa_insnbuf_alloc (isa);
-      slotbuf = xtensa_insnbuf_alloc (isa);
-      o_insnbuf = xtensa_insnbuf_alloc (isa);
-      o_slotbuf = xtensa_insnbuf_alloc (isa);
-    }
-
-  BFD_ASSERT (offset < content_length);
-
-  if (content_length < 2)
-    return FALSE;
-
-  /* We will hand-code a few of these for a little while.
-     These have all been specified in the assembler aleady.  */
-  xtensa_insnbuf_from_chars (isa, insnbuf, &contents[offset],
-                            content_length - offset);
-  fmt = xtensa_format_decode (isa, insnbuf);
-  if (xtensa_format_num_slots (isa, fmt) != 1)
-    return FALSE;
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_format o_fmt;
+  unsigned opi;
 
-  if (xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf) != 0)
-    return FALSE;
+  static xtensa_insnbuf o_insnbuf = NULL;
+  static xtensa_insnbuf o_slotbuf = NULL;
 
-  opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
-  if (opcode == XTENSA_UNDEFINED)
-    return FALSE;
-  insn_len = xtensa_format_length (isa, fmt);
-  if (insn_len > content_length)
-    return FALSE;
+  if (o_insnbuf == NULL)
+    {
+      o_insnbuf = xtensa_insnbuf_alloc (isa);
+      o_slotbuf = xtensa_insnbuf_alloc (isa);
+    }
 
-  for (opi = 0; opi < (sizeof (narrowable)/sizeof (struct string_pair)); ++opi)
+  for (opi = 0; opi < (sizeof (narrowable)/sizeof (struct string_pair)); opi++)
     {
       bfd_boolean is_or = (strcmp ("or", narrowable[opi].wide) == 0);
 
@@ -3638,47 +4396,41 @@ narrow_instruction (bfd_byte *contents,
 
          o_opcode = xtensa_opcode_lookup (isa, narrowable[opi].narrow);
          if (o_opcode == XTENSA_UNDEFINED)
-           return FALSE;
+           return 0;
          o_fmt = get_single_format (o_opcode);
          if (o_fmt == XTENSA_UNDEFINED)
-           return FALSE;
+           return 0;
 
          if (xtensa_format_length (isa, fmt) != 3
              || xtensa_format_length (isa, o_fmt) != 2)
-           return FALSE;
+           return 0;
 
          xtensa_format_encode (isa, o_fmt, o_insnbuf);
          operand_count = xtensa_opcode_num_operands (isa, opcode);
          o_operand_count = xtensa_opcode_num_operands (isa, o_opcode);
 
          if (xtensa_opcode_encode (isa, o_fmt, 0, o_slotbuf, o_opcode) != 0)
-           return FALSE;
+           return 0;
 
          if (!is_or)
            {
              if (xtensa_opcode_num_operands (isa, o_opcode) != operand_count)
-               return FALSE;
+               return 0;
            }
          else
            {
              uint32 rawval0, rawval1, rawval2;
 
-             if (o_operand_count + 1 != operand_count)
-               return FALSE;
-             if (xtensa_operand_get_field (isa, opcode, 0,
-                                           fmt, 0, slotbuf, &rawval0) != 0)
-               return FALSE;
-             if (xtensa_operand_get_field (isa, opcode, 1,
-                                           fmt, 0, slotbuf, &rawval1) != 0)
-               return FALSE;
-             if (xtensa_operand_get_field (isa, opcode, 2,
-                                           fmt, 0, slotbuf, &rawval2) != 0)
-               return FALSE;
-
-             if (rawval1 != rawval2)
-               return FALSE;
-             if (rawval0 == rawval1) /* it is a nop */
-               return FALSE;
+             if (o_operand_count + 1 != operand_count
+                 || xtensa_operand_get_field (isa, opcode, 0,
+                                              fmt, 0, slotbuf, &rawval0) != 0
+                 || xtensa_operand_get_field (isa, opcode, 1,
+                                              fmt, 0, slotbuf, &rawval1) != 0
+                 || xtensa_operand_get_field (isa, opcode, 2,
+                                              fmt, 0, slotbuf, &rawval2) != 0
+                 || rawval1 != rawval2
+                 || rawval0 == rawval1 /* it is a nop */)
+               return 0;
            }
 
          for (i = 0; i < o_operand_count; ++i)
@@ -3686,7 +4438,7 @@ narrow_instruction (bfd_byte *contents,
              if (xtensa_operand_get_field (isa, opcode, i, fmt, 0,
                                            slotbuf, &value)
                  || xtensa_operand_decode (isa, opcode, i, &value))
-               return FALSE;
+               return 0;
 
              /* PC-relative branches need adjustment, but
                 the PC-rel operand will always have a relocation.  */
@@ -3696,52 +4448,41 @@ narrow_instruction (bfd_byte *contents,
                  || xtensa_operand_encode (isa, o_opcode, i, &newval)
                  || xtensa_operand_set_field (isa, o_opcode, i, o_fmt, 0,
                                               o_slotbuf, newval))
-               return FALSE;
+               return 0;
            }
 
-         if (xtensa_format_set_slot (isa, o_fmt, 0,
-                                     o_insnbuf, o_slotbuf) != 0)
-           return FALSE;
+         if (xtensa_format_set_slot (isa, o_fmt, 0, o_insnbuf, o_slotbuf))
+           return 0;
 
-         if (do_it)
-           xtensa_insnbuf_to_chars (isa, o_insnbuf, contents + offset,
-                                    content_length - offset);
-         return TRUE;
+         return o_insnbuf;
        }
     }
-  return FALSE;
+  return 0;
 }
 
 
-/* Attempt to widen an instruction.  Return true if the widening is
-   valid.  If the do_it parameter is non-zero, then the action should
-   be performed inplace into the contents.  Otherwise, do not modify
-   the contents.  The set of valid widenings are specified by a string
-   table but require some special case operand checks in some
-   cases.  */
+/* Attempt to narrow an instruction.  If the narrowing 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.  */
 
 static bfd_boolean
-widen_instruction (bfd_byte *contents,
-                  bfd_size_type content_length,
-                  bfd_size_type offset,
-                  bfd_boolean do_it)
+narrow_instruction (bfd_byte *contents,
+                   bfd_size_type content_length,
+                   bfd_size_type offset)
 {
   xtensa_opcode opcode;
-  bfd_size_type insn_len, opi;
+  bfd_size_type insn_len;
   xtensa_isa isa = xtensa_default_isa;
-  xtensa_format fmt, o_fmt;
+  xtensa_format fmt;
+  xtensa_insnbuf o_insnbuf;
 
   static xtensa_insnbuf insnbuf = NULL;
   static xtensa_insnbuf slotbuf = NULL;
-  static xtensa_insnbuf o_insnbuf = NULL;
-  static xtensa_insnbuf o_slotbuf = NULL;
 
   if (insnbuf == NULL)
     {
       insnbuf = xtensa_insnbuf_alloc (isa);
       slotbuf = xtensa_insnbuf_alloc (isa);
-      o_insnbuf = xtensa_insnbuf_alloc (isa);
-      o_slotbuf = xtensa_insnbuf_alloc (isa);
     }
 
   BFD_ASSERT (offset < content_length);
@@ -3749,7 +4490,7 @@ widen_instruction (bfd_byte *contents,
   if (content_length < 2)
     return FALSE;
 
-  /* We will hand code a few of these for a little while.
+  /* We will hand-code a few of these for a little while.
      These have all been specified in the assembler aleady.  */
   xtensa_insnbuf_from_chars (isa, insnbuf, &contents[offset],
                             content_length - offset);
@@ -3767,7 +4508,43 @@ widen_instruction (bfd_byte *contents,
   if (insn_len > content_length)
     return FALSE;
 
-  for (opi = 0; opi < (sizeof (widenable)/sizeof (struct string_pair)); ++opi)
+  o_insnbuf = can_narrow_instruction (slotbuf, fmt, opcode);
+  if (o_insnbuf)
+    {
+      xtensa_insnbuf_to_chars (isa, o_insnbuf, contents + offset,
+                              content_length - offset);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+/* Check if an instruction can be "widened", i.e., changed from a 2-byte
+   "density" instruction to a standard 3-byte instruction.  If it is valid,
+   return the instruction buffer holding the wide instruction.  Otherwise,
+   return 0.  The set of valid widenings are specified by a string table
+   but require some special case operand checks in some cases.  */
+
+static xtensa_insnbuf
+can_widen_instruction (xtensa_insnbuf slotbuf,
+                      xtensa_format fmt,
+                      xtensa_opcode opcode)
+{
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_format o_fmt;
+  unsigned opi;
+
+  static xtensa_insnbuf o_insnbuf = NULL;
+  static xtensa_insnbuf o_slotbuf = NULL;
+
+  if (o_insnbuf == NULL)
+    {
+      o_insnbuf = xtensa_insnbuf_alloc (isa);
+      o_slotbuf = xtensa_insnbuf_alloc (isa);
+    }
+
+  for (opi = 0; opi < (sizeof (widenable)/sizeof (struct string_pair)); opi++)
     {
       bfd_boolean is_or = (strcmp ("or", widenable[opi].wide) == 0);
       bfd_boolean is_branch = (strcmp ("beqz", widenable[opi].wide) == 0
@@ -3785,14 +4562,14 @@ widen_instruction (bfd_byte *contents,
 
          o_opcode = xtensa_opcode_lookup (isa, widenable[opi].wide);
          if (o_opcode == XTENSA_UNDEFINED)
-           return FALSE;
+           return 0;
          o_fmt = get_single_format (o_opcode);
          if (o_fmt == XTENSA_UNDEFINED)
-           return FALSE;
+           return 0;
 
          if (xtensa_format_length (isa, fmt) != 2
              || xtensa_format_length (isa, o_fmt) != 3)
-           return FALSE;
+           return 0;
 
          xtensa_format_encode (isa, o_fmt, o_insnbuf);
          operand_count = xtensa_opcode_num_operands (isa, opcode);
@@ -3800,32 +4577,29 @@ widen_instruction (bfd_byte *contents,
          check_operand_count = o_operand_count;
 
          if (xtensa_opcode_encode (isa, o_fmt, 0, o_slotbuf, o_opcode) != 0)
-           return FALSE;
+           return 0;
 
          if (!is_or)
            {
              if (xtensa_opcode_num_operands (isa, o_opcode) != operand_count)
-               return FALSE;
+               return 0;
            }
          else
            {
              uint32 rawval0, rawval1;
 
-             if (o_operand_count != operand_count + 1)
-               return FALSE;
-             if (xtensa_operand_get_field (isa, opcode, 0,
-                                           fmt, 0, slotbuf, &rawval0) != 0)
-               return FALSE;
-             if (xtensa_operand_get_field (isa, opcode, 1,
-                                           fmt, 0, slotbuf, &rawval1) != 0)
-               return FALSE;
-             if (rawval0 == rawval1) /* it is a nop */
-               return FALSE;
+             if (o_operand_count != operand_count + 1
+                 || xtensa_operand_get_field (isa, opcode, 0,
+                                              fmt, 0, slotbuf, &rawval0) != 0
+                 || xtensa_operand_get_field (isa, opcode, 1,
+                                              fmt, 0, slotbuf, &rawval1) != 0
+                 || rawval0 == rawval1 /* it is a nop */)
+               return 0;
            }
          if (is_branch)
            check_operand_count--;
 
-         for (i = 0; i < check_operand_count; ++i)
+         for (i = 0; i < check_operand_count; i++)
            {
              int new_i = i;
              if (is_or && i == o_operand_count - 1)
@@ -3833,7 +4607,7 @@ widen_instruction (bfd_byte *contents,
              if (xtensa_operand_get_field (isa, opcode, new_i, fmt, 0,
                                            slotbuf, &value)
                  || xtensa_operand_decode (isa, opcode, new_i, &value))
-               return FALSE;
+               return 0;
 
              /* PC-relative branches need adjustment, but
                 the PC-rel operand will always have a relocation.  */
@@ -3843,18 +4617,73 @@ widen_instruction (bfd_byte *contents,
                  || xtensa_operand_encode (isa, o_opcode, i, &newval)
                  || xtensa_operand_set_field (isa, o_opcode, i, o_fmt, 0,
                                               o_slotbuf, newval))
-               return FALSE;
+               return 0;
            }
 
          if (xtensa_format_set_slot (isa, o_fmt, 0, o_insnbuf, o_slotbuf))
-           return FALSE;
+           return 0;
 
-         if (do_it)
-           xtensa_insnbuf_to_chars (isa, o_insnbuf, contents + offset,
-                                    content_length - offset);
-         return TRUE;
+         return o_insnbuf;
        }
     }
+  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.  */
+
+static bfd_boolean
+widen_instruction (bfd_byte *contents,
+                  bfd_size_type content_length,
+                  bfd_size_type offset)
+{
+  xtensa_opcode opcode;
+  bfd_size_type insn_len;
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_format fmt;
+  xtensa_insnbuf o_insnbuf;
+
+  static xtensa_insnbuf insnbuf = NULL;
+  static xtensa_insnbuf slotbuf = NULL;
+
+  if (insnbuf == NULL)
+    {
+      insnbuf = xtensa_insnbuf_alloc (isa);
+      slotbuf = xtensa_insnbuf_alloc (isa);
+    }
+
+  BFD_ASSERT (offset < content_length);
+
+  if (content_length < 2)
+    return FALSE;
+
+  /* We will hand-code a few of these for a little while.
+     These have all been specified in the assembler aleady.  */
+  xtensa_insnbuf_from_chars (isa, insnbuf, &contents[offset],
+                            content_length - offset);
+  fmt = xtensa_format_decode (isa, insnbuf);
+  if (xtensa_format_num_slots (isa, fmt) != 1)
+    return FALSE;
+
+  if (xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf) != 0)
+    return FALSE;
+
+  opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
+  if (opcode == XTENSA_UNDEFINED)
+    return FALSE;
+  insn_len = xtensa_format_length (isa, fmt);
+  if (insn_len > content_length)
+    return FALSE;
+
+  o_insnbuf = can_widen_instruction (slotbuf, fmt, opcode);
+  if (o_insnbuf)
+    {
+      xtensa_insnbuf_to_chars (isa, o_insnbuf, contents + offset,
+                              content_length - offset);
+      return TRUE;
+    }
   return FALSE;
 }
 
@@ -3884,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;
     }
 
@@ -3892,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);
@@ -3968,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;
@@ -4044,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;
 
@@ -4075,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.  */
@@ -4202,7 +5031,7 @@ print_r_reloc (FILE *fp, const r_reloc *r_rel)
       fprintf (fp, " + ");
       fprintf_vma (fp, r_rel->virtual_offset);
     }
-    
+
   fprintf (fp, ")");
 }
 
@@ -4322,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;
 };
@@ -4363,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))
@@ -4375,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);
@@ -4423,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;
@@ -4462,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);
@@ -4528,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;
 }
 
@@ -4538,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"
@@ -4567,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
 };
@@ -4587,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.  */
+};
 
-  text_action *next;
+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;
+
+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;
 }
 
@@ -4663,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)
@@ -4686,26 +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;
-      /* When the action is another fill at the same address,
-        just increase the size.  */
-      if (t->offset == offset && t->action == ta_fill && action == ta_fill)
+      splay_tree_node node = splay_tree_lookup (l->tree, (splay_tree_key)&a);
+
+      if (node)
        {
-         t->removed_bytes += removed;
+         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;
 }
 
 
@@ -4716,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;
@@ -4724,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;
@@ -4740,69 +5621,202 @@ 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;
 }
 
 
-static bfd_vma 
-offset_with_removed_text (text_action_list *action_list, bfd_vma offset)
+/* Find the total offset adjustment for the relaxations specified by
+   text_actions, beginning from a particular starting action.  This is
+   typically used from offset_with_removed_text to search an entire list of
+   actions, but it may also be called directly when adjusting adjacent offsets
+   so that each search may begin where the previous one left off.  */
+
+static int
+removed_by_actions (text_action_list *action_list,
+                   text_action **p_start_action,
+                   bfd_vma offset,
+                   bfd_boolean before_fill)
 {
   text_action *r;
   int removed = 0;
 
-  for (r = action_list->head; r && r->offset <= offset; r = r->next)
+  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
-         || (r->action == ta_fill && r->removed_bytes < 0))
-       removed += r->removed_bytes;
+      if (r->offset > offset)
+       break;
+
+      if (r->offset == offset
+         && (before_fill || r->action != ta_fill || r->removed_bytes >= 0))
+       break;
+
+      removed += r->removed_bytes;
+
+      r = action_next (action_list, r);
     }
 
-  return (offset - removed);
+  *p_start_action = r;
+  return removed;
 }
 
 
 static bfd_vma
-offset_with_removed_text_before_fill (text_action_list *action_list,
-                                     bfd_vma offset)
+offset_with_removed_text (text_action_list *action_list, bfd_vma offset)
 {
-  text_action *r;
-  int removed = 0;
+  text_action *r = action_first (action_list);
 
-  for (r = action_list->head; r && r->offset < offset; r = r->next)
-    removed += r->removed_bytes;
-
-  return (offset - removed);
+  return offset - removed_by_actions (action_list, &r, offset, FALSE);
 }
 
 
-/* The find_insn_action routine will only find non-fill actions.  */
+static unsigned
+action_list_count (text_action_list *action_list)
+{
+  return action_list->count;
+}
 
-static text_action *
-find_insn_action (text_action_list *action_list, bfd_vma offset)
+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)
 {
-  text_action *t;
-  for (t = action_list->head; t; t = t->next)
+  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)
     {
-      if (t->offset == 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)
        {
-         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;
-           }
+         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
+    {
+      return 0;
+    }
+}
+
+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;
+}
+
+
+/* The find_insn_action routine will only find non-fill actions.  */
+
+static text_action *
+find_insn_action (text_action_list *action_list, bfd_vma offset)
+{
+  static const text_action_t action[] =
+    {
+      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;
 }
@@ -4811,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 = "remove_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 */
@@ -4859,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
@@ -4868,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;
 };
 
 
@@ -4893,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;
@@ -4908,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;
        }
@@ -4920,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.  */
@@ -4927,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;
 }
 
 
@@ -4986,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;
 };
@@ -5001,13 +6077,16 @@ struct elf_xtensa_section_data
 static bfd_boolean
 elf_xtensa_new_section_hook (bfd *abfd, asection *sec)
 {
-  struct elf_xtensa_section_data *sdata;
-  bfd_size_type amt = sizeof (*sdata);
+  if (!sec->used_by_bfd)
+    {
+      struct elf_xtensa_section_data *sdata;
+      size_t amt = sizeof (*sdata);
 
-  sdata = (struct elf_xtensa_section_data *) bfd_zalloc (abfd, amt);
-  if (sdata == NULL)
-    return FALSE;
-  sec->used_by_bfd = (void *) sdata;
+      sdata = bfd_zalloc (abfd, amt);
+      if (sdata == NULL)
+       return FALSE;
+      sec->used_by_bfd = sdata;
+    }
 
   return _bfd_elf_new_section_hook (abfd, sec);
 }
@@ -5043,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;
 }
@@ -5069,12 +6151,11 @@ struct reloc_bfd_fix_struct
   asection *src_sec;
   bfd_vma src_offset;
   unsigned src_type;                   /* Relocation type.  */
-  
-  bfd *target_abfd;
+
   asection *target_sec;
   bfd_vma target_offset;
   bfd_boolean translated;
-  
+
   reloc_bfd_fix *next;
 };
 
@@ -5083,7 +6164,6 @@ static reloc_bfd_fix *
 reloc_bfd_fix_init (asection *src_sec,
                    bfd_vma src_offset,
                    unsigned src_type,
-                   bfd *target_abfd,
                    asection *target_sec,
                    bfd_vma target_offset,
                    bfd_boolean translated)
@@ -5094,7 +6174,6 @@ reloc_bfd_fix_init (asection *src_sec,
   fix->src_sec = src_sec;
   fix->src_offset = src_offset;
   fix->src_type = src_type;
-  fix->target_abfd = target_abfd;
   fix->target_sec = target_sec;
   fix->target_offset = target_offset;
   fix->translated = translated;
@@ -5207,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);
     }
 }
 
@@ -5256,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;
@@ -5272,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;
 }
 
@@ -5392,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);
 }
 
 
@@ -5469,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;
@@ -5492,7 +6570,7 @@ extend_ebb_bounds_forward (ebb_t *ebb)
 
       new_entry = &ebb->ptbl[ebb->end_ptbl_idx + 1];
       if (((new_entry->flags & XTENSA_PROP_INSN) == 0)
-         || ((new_entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) != 0)
+         || ((new_entry->flags & XTENSA_PROP_NO_TRANSFORM) != 0)
          || ((the_entry->flags & XTENSA_PROP_ALIGN) != 0))
        break;
 
@@ -5545,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;
@@ -5565,7 +6646,7 @@ extend_ebb_bounds_backward (ebb_t *ebb)
 
       new_entry = &ebb->ptbl[ebb->start_ptbl_idx - 1];
       if ((new_entry->flags & XTENSA_PROP_INSN) == 0
-         || ((new_entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) != 0)
+         || ((new_entry->flags & XTENSA_PROP_NO_TRANSFORM) != 0)
          || ((new_entry->flags & XTENSA_PROP_ALIGN) != 0))
        return TRUE;
       if (new_entry->address + new_entry->size != the_entry->address)
@@ -5621,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;
     }
@@ -5674,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);
 }
 
@@ -5688,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;
@@ -5714,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);
 }
 
@@ -5759,23 +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 
-  (const source_reloc *, int, const source_reloc *, int);
+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,
@@ -5792,7 +6873,7 @@ static bfd_boolean move_shared_literal
 static bfd_boolean relax_section (bfd *, asection *, struct bfd_link_info *);
 static bfd_boolean translate_section_fixes (asection *);
 static bfd_boolean translate_reloc_bfd_fix (reloc_bfd_fix *);
-static void translate_reloc (const r_reloc *, r_reloc *);
+static asection *translate_reloc (const r_reloc *, r_reloc *, asection *);
 static void shrink_dynamic_reloc_sections
   (struct bfd_link_info *, bfd *, asection *, Elf_Internal_Rela *);
 static bfd_boolean move_literal
@@ -5805,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,
@@ -5888,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))
@@ -5907,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;
@@ -5919,10 +7000,12 @@ analyze_relocations (struct bfd_link_info *link_info)
            relax_info->src_relocs = (source_reloc *)
              bfd_malloc (relax_info->src_count * sizeof (source_reloc));
          }
+       else
+         relax_info->src_count = 0;
       }
 
   /* 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))
@@ -5930,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))
@@ -5962,10 +7045,11 @@ find_relaxable_sections (bfd *abfd,
   bfd_boolean ok = TRUE;
   unsigned i;
   xtensa_relax_info *source_relax_info;
+  bfd_boolean is_l32r_reloc;
 
   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);
@@ -5976,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;
@@ -6010,15 +7094,23 @@ 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().  */
-      if (is_operand_relocation (ELF32_R_TYPE (irel->r_info))
-         && (!is_alt_relocation (ELF32_R_TYPE (irel->r_info))
-             || is_l32r_relocation (abfd, sec, contents, irel)))
-       target_relax_info->src_count++;
+      is_l32r_reloc = FALSE;
+      if (is_operand_relocation (ELF32_R_TYPE (irel->r_info)))
+       {
+         xtensa_opcode opcode =
+           get_relocation_opcode (abfd, sec, contents, irel);
+         if (opcode != XTENSA_UNDEFINED)
+           {
+             is_l32r_reloc = (opcode == get_l32r_opcode ());
+             if (!is_alt_relocation (ELF32_R_TYPE (irel->r_info))
+                 || is_l32r_reloc)
+               target_relax_info->src_count++;
+           }
+       }
 
-      if (is_l32r_relocation (abfd, sec, contents, irel)
-         && r_reloc_is_defined (&r_rel))
+      if (is_l32r_reloc && r_reloc_is_defined (&r_rel))
        {
          /* Mark the target section as relaxable.  */
          target_relax_info->is_relaxable_literal_section = TRUE;
@@ -6048,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);
@@ -6062,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;
@@ -6120,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;
@@ -6159,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);
@@ -6205,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;
@@ -6218,83 +7315,399 @@ is_resolvable_asm_expansion (bfd *abfd,
   if (contents == NULL)
     return FALSE;
 
-  if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_ASM_EXPAND) 
-    return FALSE;
+  if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_ASM_EXPAND)
+    return FALSE;
+
+  sec_size = bfd_get_section_limit (abfd, sec);
+  opcode = get_expanded_call_opcode (contents + irel->r_offset,
+                                    sec_size - irel->r_offset, &uses_l32r);
+  /* 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;
+
+  /* Check and see that the target resolves.  */
+  r_reloc_init (&r_rel, abfd, irel, contents, sec_size);
+  if (!r_reloc_is_defined (&r_rel))
+    return FALSE;
+
+  target_sec = r_reloc_get_section (&r_rel);
+  target_offset = r_rel.target_offset;
+
+  /* If the target is in a shared library, then it doesn't reach.  This
+     isn't supposed to come up because the compiler should never generate
+     non-PIC calls on systems that use shared libraries, but the linker
+     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 (bfd_link_relocatable (link_info)
+      && (target_sec->output_section != sec->output_section
+         || is_reloc_sym_weak (abfd, irel)))
+    return FALSE;
+
+  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);
+
+  if ((self_address >> CALL_SEGMENT_BITS) !=
+      (dest_address >> CALL_SEGMENT_BITS))
+    return FALSE;
+
+  return TRUE;
+}
+
+
+static Elf_Internal_Rela *
+find_associated_l32r_irel (bfd *abfd,
+                          asection *sec,
+                          bfd_byte *contents,
+                          Elf_Internal_Rela *other_irel,
+                          Elf_Internal_Rela *internal_relocs)
+{
+  unsigned i;
+
+  for (i = 0; i < sec->reloc_count; i++)
+    {
+      Elf_Internal_Rela *irel = &internal_relocs[i];
+
+      if (irel == other_irel)
+       continue;
+      if (irel->r_offset != other_irel->r_offset)
+       continue;
+      if (is_l32r_relocation (abfd, sec, contents, irel))
+       return irel;
+    }
+
+  return NULL;
+}
+
+
+static xtensa_opcode *
+build_reloc_opcodes (bfd *abfd,
+                    asection *sec,
+                    bfd_byte *contents,
+                    Elf_Internal_Rela *internal_relocs)
+{
+  unsigned i;
+  xtensa_opcode *reloc_opcodes =
+    (xtensa_opcode *) bfd_malloc (sizeof (xtensa_opcode) * sec->reloc_count);
+  for (i = 0; i < sec->reloc_count; i++)
+    {
+      Elf_Internal_Rela *irel = &internal_relocs[i];
+      reloc_opcodes[i] = get_relocation_opcode (abfd, sec, contents, irel);
+    }
+  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);
 
-  sec_size = bfd_get_section_limit (abfd, sec);
-  opcode = get_expanded_call_opcode (contents + irel->r_offset,
-                                    sec_size - irel->r_offset, &uses_l32r);
-  /* 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;
+         if (opcode == XTENSA_UNDEFINED)
+           {
+             list->ok = FALSE;
+             break;
+           }
 
-  /* Check and see that the target resolves.  */
-  r_reloc_init (&r_rel, abfd, irel, contents, sec_size);
-  if (!r_reloc_is_defined (&r_rel))
-    return FALSE;
+         opnum = get_relocation_opnd (opcode, ELF32_R_TYPE (irel->r_info));
+         if (opnum == XTENSA_UNDEFINED)
+           {
+             list->ok = FALSE;
+             break;
+           }
 
-  target_sec = r_reloc_get_section (&r_rel);
-  target_offset = r_rel.target_offset;
+         /* 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 the target is in a shared library, then it doesn't reach.  This
-     isn't supposed to come up because the compiler should never generate
-     non-PIC calls on systems that use shared libraries, but the linker
-     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
-      && (target_sec->output_section != sec->output_section
-         || is_reloc_sym_weak (abfd, irel)))
-    return FALSE;
+  if (list->ok)
+    {
+      ranges = bfd_realloc (ranges, n * sizeof (*ranges));
+      qsort (ranges, n, sizeof (*ranges), reloc_range_compare);
 
-  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);
-      
-  *is_reachable_p = pcrel_reloc_fits (direct_call_opcode, 0,
-                                     self_address, dest_address);
+      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);
+    }
+}
 
-  if ((self_address >> CALL_SEGMENT_BITS) !=
-      (dest_address >> CALL_SEGMENT_BITS))
-    return FALSE;
+static void reloc_range_list_append (reloc_range_list *list,
+                                    unsigned irel_index)
+{
+  reloc_range_list_entry *entry = list->reloc + irel_index;
 
-  return TRUE;
+  entry->prev = list->list_root.prev;
+  entry->next = &list->list_root;
+  entry->prev->next = entry;
+  entry->next->prev = entry;
+  ++list->n_list;
 }
 
-
-static Elf_Internal_Rela *
-find_associated_l32r_irel (bfd *abfd,
-                          asection *sec,
-                          bfd_byte *contents,
-                          Elf_Internal_Rela *other_irel,
-                          Elf_Internal_Rela *internal_relocs)
+static void reloc_range_list_remove (reloc_range_list *list,
+                                    unsigned irel_index)
 {
-  unsigned i;
+  reloc_range_list_entry *entry = list->reloc + irel_index;
 
-  for (i = 0; i < sec->reloc_count; i++) 
-    {
-      Elf_Internal_Rela *irel = &internal_relocs[i];
+  entry->next->prev = entry->prev;
+  entry->prev->next = entry->next;
+  --list->n_list;
+}
 
-      if (irel == other_irel)
-       continue;
-      if (irel->r_offset != other_irel->r_offset)
-       continue;
-      if (is_l32r_relocation (abfd, sec, contents, irel))
-       return irel;
+/* 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__);
     }
 
-  return NULL;
+  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
@@ -6312,6 +7725,7 @@ compute_text_actions (bfd *abfd,
                      asection *sec,
                      struct bfd_link_info *link_info)
 {
+  xtensa_opcode *reloc_opcodes = NULL;
   xtensa_relax_info *relax_info;
   bfd_byte *contents;
   Elf_Internal_Rela *internal_relocs;
@@ -6320,14 +7734,13 @@ compute_text_actions (bfd *abfd,
   property_table_entry *prop_table = 0;
   int ptblsize = 0;
   bfd_size_type sec_size;
-  static bfd_boolean no_insn_move = FALSE;
-
-  if (no_insn_move)
-    return ok;
+  reloc_range_list relevant_relocs;
 
-  /* Do nothing if the section contains no optimized longcalls.  */
   relax_info = get_xtensa_relax_info (sec);
   BFD_ASSERT (relax_info);
+  BFD_ASSERT (relax_info->src_next == relax_info->src_count);
+
+  /* Do nothing if the section contains no optimized longcalls.  */
   if (!relax_info->is_relaxable_asm_section)
     return ok;
 
@@ -6354,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];
@@ -6371,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;
        }
 
@@ -6402,7 +7824,7 @@ compute_text_actions (bfd *abfd,
          the_entry++;
        }
 
-      if (the_entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM)
+      if (the_entry->flags & XTENSA_PROP_NO_TRANSFORM)
          /* NO_REORDER is OK */
        continue;
 
@@ -6421,7 +7843,9 @@ compute_text_actions (bfd *abfd,
          || !compute_ebb_proposed_actions (&ebb_table)
          || !compute_ebb_actions (&ebb_table)
          || !check_section_ebb_pcrels_fit (abfd, sec, contents,
-                                           internal_relocs, &ebb_table)
+                                           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
@@ -6443,21 +7867,40 @@ 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);
+  free (prop_table);
+  free (reloc_opcodes);
 
   return ok;
 }
 
 
+/* Do not widen an instruction if it is preceeded by a
+   loop opcode.  It might cause misalignment.  */
+
+static bfd_boolean
+prev_instr_is_a_loop (bfd_byte *contents,
+                     bfd_size_type content_length,
+                     bfd_size_type offset)
+{
+  xtensa_opcode prev_opcode;
+
+  if (offset < 3)
+    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.  */
 
 bfd_boolean
@@ -6466,13 +7909,24 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table)
   const ebb_t *ebb = &ebb_table->ebb;
   unsigned rel_idx = ebb->start_reloc_idx;
   property_table_entry *entry, *start_entry, *end_entry;
+  bfd_vma offset = 0;
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_format fmt;
+  static xtensa_insnbuf insnbuf = NULL;
+  static xtensa_insnbuf slotbuf = NULL;
+
+  if (insnbuf == NULL)
+    {
+      insnbuf = xtensa_insnbuf_alloc (isa);
+      slotbuf = xtensa_insnbuf_alloc (isa);
+    }
 
   start_entry = &ebb->ptbl[ebb->start_ptbl_idx];
   end_entry = &ebb->ptbl[ebb->end_ptbl_idx];
 
   for (entry = start_entry; entry <= end_entry; entry++)
     {
-      bfd_vma offset, start_offset, end_offset;
+      bfd_vma start_offset, end_offset;
       bfd_size_type insn_len;
 
       start_offset = entry->address - ebb->sec->vma;
@@ -6494,15 +7948,9 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table)
 
          insn_len = insn_decode_len (ebb->contents, ebb->content_length,
                                      offset);
+         if (insn_len == 0)
+           goto decode_error;
 
-         /* Propose no actions for a section with an undecodable offset.  */
-         if (insn_len == 0) 
-           {
-             (*_bfd_error_handler)
-               (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"),
-                ebb->sec->owner, ebb->sec, offset);
-             return FALSE;
-           }
          if (check_branch_target_aligned_address (offset, insn_len))
            align_type = EBB_REQUIRE_TGT_ALIGN;
 
@@ -6529,65 +7977,63 @@ 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)
-               {
-                 (*_bfd_error_handler)
-                   (_("%B(%A+0x%lx): could not decode instruction for XTENSA_ASM_SIMPLIFY relocation; possible configuration mismatch"),
-                    ebb->sec->owner, ebb->sec, offset);
-                 return FALSE;
-               }
+               goto decode_error;
 
              ebb_propose_action (ebb_table, EBB_NO_ALIGN, 0,
                                  ta_convert_longcall, offset, 0, TRUE);
-             
+
              offset += simplify_size;
              continue;
            }
 
-         insn_len = insn_decode_len (ebb->contents, ebb->content_length,
-                                     offset);
-         /* If the instruction is undecodable, then report an error.  */
-         if (insn_len == 0)
+         if (offset + MIN_INSN_LENGTH > ebb->content_length)
+           goto decode_error;
+         xtensa_insnbuf_from_chars (isa, insnbuf, &ebb->contents[offset],
+                                    ebb->content_length - offset);
+         fmt = xtensa_format_decode (isa, insnbuf);
+         if (fmt == XTENSA_UNDEFINED)
+           goto decode_error;
+         insn_len = xtensa_format_length (isa, fmt);
+         if (insn_len == (bfd_size_type) XTENSA_UNDEFINED)
+           goto decode_error;
+
+         if (xtensa_format_num_slots (isa, fmt) != 1)
            {
-             (*_bfd_error_handler)
-               (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"),
-                ebb->sec->owner, ebb->sec, offset);
-             return FALSE;
+             offset += insn_len;
+             continue;
            }
-           
+
+         xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf);
+         opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
+         if (opcode == XTENSA_UNDEFINED)
+           goto decode_error;
+
          if ((entry->flags & XTENSA_PROP_INSN_NO_DENSITY) == 0
-             && (entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) == 0
-             && narrow_instruction (ebb->contents, ebb->content_length,
-                                    offset, FALSE))
+             && (entry->flags & XTENSA_PROP_NO_TRANSFORM) == 0
+             && can_narrow_instruction (slotbuf, fmt, opcode) != 0)
            {
              /* Add an instruction narrow action.  */
              ebb_propose_action (ebb_table, EBB_NO_ALIGN, 0,
                                  ta_narrow_insn, offset, 0, FALSE);
-             offset += insn_len;
-             continue;
            }
-         if ((entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) == 0
-             && widen_instruction (ebb->contents, ebb->content_length,
-                                   offset, FALSE))
+         else if ((entry->flags & XTENSA_PROP_NO_TRANSFORM) == 0
+                  && can_widen_instruction (slotbuf, fmt, opcode) != 0
+                  && ! prev_instr_is_a_loop (ebb->contents,
+                                             ebb->content_length, offset))
            {
              /* Add an instruction widen action.  */
              ebb_propose_action (ebb_table, EBB_NO_ALIGN, 0,
                                  ta_widen_insn, offset, 0, FALSE);
-             offset += insn_len;
-             continue;
            }
-         opcode = insn_decode_opcode (ebb->contents, ebb->content_length,
-                                      offset, 0);
-         if (xtensa_opcode_is_loop (xtensa_default_isa, opcode))
+         else if (xtensa_opcode_is_loop (xtensa_default_isa, opcode) == 1)
            {
              /* Check for branch targets.  */
              ebb_propose_action (ebb_table, EBB_REQUIRE_LOOP_ALIGN, 0,
                                  ta_none, offset, 0, TRUE);
-             offset += insn_len;
-             continue;
            }
 
          offset += insn_len;
@@ -6601,6 +8047,14 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table)
     }
 
   return TRUE;
+
+ decode_error:
+  _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;
 }
 
 
@@ -6813,6 +8267,8 @@ compute_ebb_actions (ebb_constraint *ebb_table)
                      bad_alignment = FALSE;
                      break;
                    }
+                 if (new_action->do_action)
+                   removed_bytes += new_action->removed_bytes;
                }
              if (!bad_alignment)
                {
@@ -6847,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);
 
@@ -6857,6 +8313,179 @@ compute_ebb_actions (ebb_constraint *ebb_table)
 }
 
 
+/* The xlate_map is a sorted array of address mappings designed to
+   answer the offset_with_removed_text() query with a binary search instead
+   of a linear search through the section's action_list.  */
+
+typedef struct xlate_map_entry xlate_map_entry_t;
+typedef struct xlate_map xlate_map_t;
+
+struct xlate_map_entry
+{
+  bfd_vma orig_address;
+  bfd_vma new_address;
+  unsigned size;
+};
+
+struct xlate_map
+{
+  unsigned entry_count;
+  xlate_map_entry_t *entry;
+};
+
+
+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;
+  const xlate_map_entry_t *b = (const xlate_map_entry_t *) b_v;
+  if (a->orig_address < b->orig_address)
+    return -1;
+  if (a->orig_address > (b->orig_address + b->size - 1))
+    return 1;
+  return 0;
+}
+
+
+static bfd_vma
+xlate_offset_with_removed_text (const xlate_map_t *map,
+                               text_action_list *action_list,
+                               bfd_vma offset)
+{
+  void *r;
+  xlate_map_entry_t *e;
+  struct xlate_map_entry se;
+
+  if (map == NULL)
+    return offset_with_removed_text (action_list, offset);
+
+  if (map->entry_count == 0)
+    return offset;
+
+  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.  */
+
+static xlate_map_t *
+build_xlate_map (asection *sec, xtensa_relax_info *relax_info)
+{
+  text_action_list *action_list = &relax_info->action_list;
+  unsigned num_actions = 0;
+  xlate_map_context ctx;
+
+  ctx.map = (xlate_map_t *) bfd_malloc (sizeof (xlate_map_t));
+
+  if (ctx.map == NULL)
+    return NULL;
+
+  num_actions = action_list_count (action_list);
+  ctx.map->entry = (xlate_map_entry_t *)
+    bfd_malloc (sizeof (xlate_map_entry_t) * (num_actions + 1));
+  if (ctx.map->entry == NULL)
+    {
+      free (ctx.map);
+      return NULL;
+    }
+  ctx.map->entry_count = 0;
+
+  ctx.removed = 0;
+  ctx.current_entry = &ctx.map->entry[0];
+
+  ctx.current_entry->orig_address = 0;
+  ctx.current_entry->new_address = 0;
+  ctx.current_entry->size = 0;
+
+  splay_tree_foreach (action_list->tree, xlate_map_fn, &ctx);
+
+  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
+free_xlate_map (xlate_map_t *map)
+{
+  if (map)
+    {
+      free (map->entry);
+      free (map);
+    }
+}
+
+
 /* Use check_section_ebb_pcrels_fit to make sure that all of the
    relocations in a section will fit if a proposed set of actions
    are performed.  */
@@ -6866,15 +8495,61 @@ check_section_ebb_pcrels_fit (bfd *abfd,
                              asection *sec,
                              bfd_byte *contents,
                              Elf_Internal_Rela *internal_relocs,
-                             const ebb_constraint *constraint)
+                             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);
 
-  for (i = 0; i < sec->reloc_count; i++)
+  if (relax_info && sec->reloc_count > 100)
+    {
+      xmap = build_xlate_map (sec, relax_info);
+      /* NULL indicates out of memory, but the slow version
+        can still be used.  */
+    }
+
+  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;
@@ -6883,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];
@@ -6891,7 +8574,8 @@ check_section_ebb_pcrels_fit (bfd *abfd,
         that fit before linking must fit after linking.  Thus we only
         need to deal with relocations to the same section that are
         PC-relative.  */
-      if (ELF32_R_TYPE (irel->r_info) == R_XTENSA_ASM_SIMPLIFY
+      if (r_type == R_XTENSA_ASM_SIMPLIFY
+         || r_type == R_XTENSA_32_PCREL
          || !howto->pc_relative)
        continue;
 
@@ -6909,10 +8593,12 @@ check_section_ebb_pcrels_fit (bfd *abfd,
 
       if (relax_info)
        {
-         self_offset = offset_with_removed_text (&relax_info->action_list,
-                                                 orig_self_offset);
-         target_offset = offset_with_removed_text (&relax_info->action_list,
-                                                   orig_target_offset);
+         self_offset =
+           xlate_offset_with_removed_text (xmap, &relax_info->action_list,
+                                           orig_self_offset);
+         target_offset =
+           xlate_offset_with_removed_text (xmap, &relax_info->action_list,
+                                           orig_target_offset);
        }
 
       self_removed_bytes = 0;
@@ -6946,20 +8632,43 @@ check_section_ebb_pcrels_fit (bfd *abfd,
          xtensa_opcode opcode;
          int opnum;
 
-         opcode = get_relocation_opcode (abfd, sec, contents, irel);
-         if (opcode == XTENSA_UNDEFINED)
-           return FALSE;
+         if (relevant_relocs)
+           {
+             opcode = entry->opcode;
+             opnum = entry->opnum;
+           }
+         else
+           {
+             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)
-           return FALSE;
+             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))
-           return FALSE;
+           {
+             ok = FALSE;
+             break;
+           }
        }
     }
 
-  return TRUE;
+  free_xlate_map (xmap);
+
+  return ok;
 }
 
 
@@ -7018,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;
 
@@ -7051,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,
@@ -7078,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);
@@ -7130,14 +8839,15 @@ 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,
                                    irel, rel, prop_table, ptblsize))
@@ -7150,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))
@@ -7167,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);
@@ -7187,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;
@@ -7221,12 +8931,22 @@ bfd_boolean
 is_removable_literal (const source_reloc *rel,
                      int i,
                      const source_reloc *src_relocs,
-                     int src_count)
+                     int src_count,
+                     asection *sec,
+                     property_table_entry *prop_table,
+                     int ptblsize)
 {
   const source_reloc *curr_rel;
+  property_table_entry *entry;
+
   if (!rel->is_null)
     return FALSE;
-  
+
+  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;
+
   for (++i; i < src_count; ++i)
     {
       curr_rel = &src_relocs[i];
@@ -7243,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,
@@ -7270,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;
@@ -7287,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,
@@ -7314,7 +9034,7 @@ remove_dead_literal (bfd *abfd,
 }
 
 
-bfd_boolean 
+bfd_boolean
 identify_literal_placement (bfd *abfd,
                            asection *sec,
                            bfd_byte *contents,
@@ -7345,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
@@ -7384,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);
@@ -7397,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;
@@ -7463,11 +9183,16 @@ 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;
 
+      /* Absolute literals in the same output section can always be
+        combined.  */
+      if (reloc[i].is_abs_literal)
+       continue;
+
       /* A literal with no PC-relative relocations can be moved anywhere.  */
       if (reloc[i].opnd != -1)
        {
@@ -7492,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,
@@ -7511,7 +9236,7 @@ coalesce_shared_literal (asection *sec,
 
   entry = elf_xtensa_find_property_entry
     (prop_table, ptblsize, sec->vma + rel->r_rel.target_offset);
-  if (entry && (entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM))
+  if (entry && (entry->flags & XTENSA_PROP_NO_TRANSFORM))
     return TRUE;
 
   /* Mark that the literal will be coalesced.  */
@@ -7521,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;
@@ -7557,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,
@@ -7600,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)
@@ -7611,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);
@@ -7623,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,
-                                            &ebb_table);
+                                            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,
@@ -7673,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;
@@ -7707,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.  */
@@ -7737,8 +9472,11 @@ 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 && !action_list_count (&relax_info->action_list))
+    return TRUE;
+
   contents = retrieve_contents (abfd, sec, link_info->keep_memory);
   if (contents == NULL && sec_size != 0)
     {
@@ -7777,6 +9515,8 @@ 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)
            {
+             pin_internal_relocs (sec, internal_relocs);
+
              if (r_type != R_XTENSA_NONE
                  && find_removed_literal (&relax_info->removed_list,
                                           irel->r_offset))
@@ -7785,9 +9525,8 @@ 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);
-                 pin_internal_relocs (sec, internal_relocs);
                  continue;
                }
 
@@ -7813,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
@@ -7823,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;
            }
@@ -7832,23 +9577,75 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
             we may need to change the relocation's target offset.  */
 
          target_sec = r_reloc_get_section (&r_rel);
-         target_relax_info = get_xtensa_relax_info (target_sec);
 
+         /* For a reference to a discarded section from a DWARF section,
+            i.e., where action_discarded is PRETEND, the symbol will
+            eventually be modified to refer to the kept section (at least if
+            the kept and discarded sections are the same size).  Anticipate
+            that here and adjust things accordingly.  */
+         if (! elf_xtensa_ignore_discarded_relocs (sec)
+             && elf_xtensa_action_discarded (sec) == PRETEND
+             && sec->sec_info_type != SEC_INFO_TYPE_STABS
+             && target_sec != NULL
+             && 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
+                fairly expensive check.  Adjusting the relocations to the
+                discarded section is fairly harmless; it will only adjust
+                some addends and difference values.  If it turns out that
+                _bfd_elf_check_kept_section fails later, it won't matter,
+                so just compare the section names to find the right group
+                member.  */
+             asection *kept = target_sec->kept_section;
+             if (kept != NULL)
+               {
+                 if ((kept->flags & SEC_GROUP) != 0)
+                   {
+                     asection *first = elf_next_in_group (kept);
+                     asection *s = first;
+
+                     kept = NULL;
+                     while (s != NULL)
+                       {
+                         if (strcmp (s->name, target_sec->name) == 0)
+                           {
+                             kept = s;
+                             break;
+                           }
+                         s = elf_next_in_group (s);
+                         if (s == first)
+                           break;
+                       }
+                   }
+               }
+             if (kept != NULL
+                 && ((target_sec->rawsize != 0
+                      ? target_sec->rawsize : target_sec->size)
+                     == (kept->rawsize != 0 ? kept->rawsize : kept->size)))
+               target_sec = kept;
+           }
+
+         target_relax_info = get_xtensa_relax_info (target_sec);
          if (target_relax_info
              && (target_relax_info->is_relaxable_literal_section
                  || target_relax_info->is_relaxable_asm_section))
            {
              r_reloc new_reloc;
-             reloc_bfd_fix *fix;
-             bfd_vma addend_displacement;
-
-             translate_reloc (&r_rel, &new_reloc);
+             target_sec = translate_reloc (&r_rel, &new_reloc, target_sec);
 
              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)
                    {
@@ -7861,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;
@@ -7882,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"),
@@ -7910,52 +9749,56 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
                  pin_contents (sec, contents);
                }
 
-             /* FIXME: If the relocation still references a section in
-                the same input file, the relocation should be modified
-                directly instead of adding a "fix" record.  */
-
-             addend_displacement =
-               new_reloc.target_offset + new_reloc.virtual_offset;
-
-             fix = reloc_bfd_fix_init (sec, source_offset, r_type, 0,
-                                       r_reloc_get_section (&new_reloc),
-                                       addend_displacement, TRUE);
-             add_fix (sec, fix);
+             /* If the relocation still references a section in the same
+                input file, modify the relocation directly instead of
+                adding a "fix" record.  */
+             if (target_sec->owner == abfd)
+               {
+                 unsigned r_symndx = ELF32_R_SYM (new_reloc.rela.r_info);
+                 irel->r_info = ELF32_R_INFO (r_symndx, r_type);
+                 irel->r_addend = new_reloc.rela.r_addend;
+                 pin_internal_relocs (sec, internal_relocs);
+               }
+             else
+               {
+                 bfd_vma addend_displacement;
+                 reloc_bfd_fix *fix;
+
+                 addend_displacement =
+                   new_reloc.target_offset + new_reloc.virtual_offset;
+                 fix = reloc_bfd_fix_init (sec, source_offset, r_type,
+                                           target_sec,
+                                           addend_displacement, TRUE);
+                 add_fix (sec, fix);
+               }
            }
-
-         pin_internal_relocs (sec, internal_relocs);
        }
     }
 
   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.  */
 
-      bfd_size_type size = sec->size;
-      int removed = 0;
       bfd_size_type final_size, copy_size, orig_insn_size;
       bfd_byte *scratch = NULL;
       bfd_byte *dup_contents = NULL;
-      bfd_size_type orig_size = size;
+      bfd_size_type orig_size = sec->size;
       bfd_vma orig_dot = 0;
       bfd_vma orig_dot_copied = 0; /* Byte copied already from
                                            orig dot in physical memory.  */
       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);
 
@@ -7964,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)
@@ -8010,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);
            }
@@ -8027,7 +9870,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
              copy_size = 2;
              memmove (scratch, &contents[orig_dot], orig_insn_size);
              BFD_ASSERT (action->removed_bytes == 1);
-             rv = narrow_instruction (scratch, final_size, 0, TRUE);
+             rv = narrow_instruction (scratch, final_size, 0);
              BFD_ASSERT (rv);
              memmove (&dup_contents[dup_dot], scratch, copy_size);
              orig_dot += orig_insn_size;
@@ -8060,7 +9903,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
              copy_size = 3;
              memmove (scratch, &contents[orig_dot], orig_insn_size);
              BFD_ASSERT (action->removed_bytes == -1);
-             rv = widen_instruction (scratch, final_size, 0, TRUE);
+             rv = widen_instruction (scratch, final_size, 0);
              BFD_ASSERT (rv);
              memmove (&dup_contents[dup_dot], scratch, copy_size);
              orig_dot += orig_insn_size;
@@ -8081,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;
@@ -8094,8 +9937,6 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
              break;
            }
 
-         size -= action->removed_bytes;
-         removed += action->removed_bytes;
          BFD_ASSERT (dup_dot <= final_size);
          BFD_ASSERT (orig_dot <= orig_size);
        }
@@ -8135,6 +9976,8 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
       free (scratch);
       pin_contents (sec, contents);
 
+      if (sec->rawsize == 0)
+       sec->rawsize = sec->size;
       sec->size = final_size;
     }
 
@@ -8145,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;
@@ -8166,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;
@@ -8212,7 +10055,7 @@ translate_reloc_bfd_fix (reloc_bfd_fix *fix)
                                      target_offset);
     }
 
-  if (removed) 
+  if (removed)
     {
       asection *new_sec;
 
@@ -8222,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))
            {
@@ -8256,26 +10099,21 @@ translate_reloc_bfd_fix (reloc_bfd_fix *fix)
 
 /* Fix up a relocation to take account of removed literals.  */
 
-static void
-translate_reloc (const r_reloc *orig_rel, r_reloc *new_rel)
+static asection *
+translate_reloc (const r_reloc *orig_rel, r_reloc *new_rel, asection *sec)
 {
-  asection *sec;
   xtensa_relax_info *relax_info;
   removed_literal *removed;
-  bfd_vma new_offset, target_offset, removed_bytes;
+  bfd_vma target_offset, base_offset;
 
   *new_rel = *orig_rel;
 
   if (!r_reloc_is_defined (orig_rel))
-    return;
-  sec = r_reloc_get_section (orig_rel);
+    return sec ;
 
   relax_info = get_xtensa_relax_info (sec);
-  BFD_ASSERT (relax_info);
-
-  if (!relax_info->is_relaxable_literal_section
-      && !relax_info->is_relaxable_asm_section)
-    return;
+  BFD_ASSERT (relax_info && (relax_info->is_relaxable_literal_section
+                            || relax_info->is_relaxable_asm_section));
 
   target_offset = orig_rel->target_offset;
 
@@ -8306,19 +10144,44 @@ translate_reloc (const r_reloc *orig_rel, r_reloc *new_rel)
          if (!relax_info
              || (!relax_info->is_relaxable_literal_section
                  && !relax_info->is_relaxable_asm_section))
-           return;
+           return sec;
        }
       target_offset = new_rel->target_offset;
     }
 
-  /* ...and the target address may have been moved within its section.  */
-  new_offset = offset_with_removed_text (&relax_info->action_list,
-                                        target_offset);
+  /* Find the base offset of the reloc symbol, excluding any addend from the
+     reloc or from the section contents (for a partial_inplace reloc).  Then
+     find the adjusted values of the offsets due to relaxation.  The base
+     offset is needed to determine the change to the reloc's addend; the reloc
+     addend should not be adjusted due to relaxations located before the base
+     offset.  */
+
+  base_offset = r_reloc_get_target_offset (new_rel) - new_rel->rela.r_addend;
+  if (base_offset <= target_offset)
+    {
+      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_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;
+    }
 
-  /* Modify the offset and addend.  */
-  removed_bytes = target_offset - new_offset;
-  new_rel->target_offset = new_offset;
-  new_rel->rela.r_addend -= removed_bytes;
+  return sec;
 }
 
 
@@ -8337,6 +10200,7 @@ shrink_dynamic_reloc_sections (struct bfd_link_info *info,
                               asection *input_section,
                               Elf_Internal_Rela *rel)
 {
+  struct elf_xtensa_link_hash_table *htab;
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
   unsigned long r_symndx;
@@ -8344,6 +10208,10 @@ shrink_dynamic_reloc_sections (struct bfd_link_info *info,
   struct elf_link_hash_entry *h;
   bfd_boolean dynamic_symbol;
 
+  htab = elf_xtensa_hash_table (info);
+  if (htab == NULL)
+    return;
+
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
 
@@ -8355,30 +10223,26 @@ shrink_dynamic_reloc_sections (struct bfd_link_info *info,
   else
     h = sym_hashes[r_symndx - symtab_hdr->sh_info];
 
-  dynamic_symbol = xtensa_elf_dynamic_symbol_p (h, info);
+  dynamic_symbol = elf_xtensa_dynamic_symbol_p (h, 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))))
     {
-      bfd *dynobj;
-      const char *srel_name;
       asection *srel;
       bfd_boolean is_plt = FALSE;
 
-      dynobj = elf_hash_table (info)->dynobj;
-      BFD_ASSERT (dynobj != NULL);
-
       if (dynamic_symbol && r_type == R_XTENSA_PLT)
        {
-         srel_name = ".rela.plt";
+         srel = htab->elf.srelplt;
          is_plt = TRUE;
        }
       else
-       srel_name = ".rela.got";
+       srel = htab->elf.srelgot;
 
       /* Reduce size of the .rela.* section by one reloc.  */
-      srel = bfd_get_section_by_name (dynobj, srel_name);
       BFD_ASSERT (srel != NULL);
       BFD_ASSERT (srel->size >= sizeof (Elf32_External_Rela));
       srel->size -= sizeof (Elf32_External_Rela);
@@ -8397,15 +10261,15 @@ shrink_dynamic_reloc_sections (struct bfd_link_info *info,
          reloc_index = srel->size / sizeof (Elf32_External_Rela);
 
          chunk = reloc_index / PLT_ENTRIES_PER_CHUNK;
-         splt = elf_xtensa_get_plt_section (dynobj, chunk);
-         sgotplt = elf_xtensa_get_gotplt_section (dynobj, chunk);
+         splt = elf_xtensa_get_plt_section (info, chunk);
+         sgotplt = elf_xtensa_get_gotplt_section (info, chunk);
          BFD_ASSERT (splt != NULL && sgotplt != NULL);
 
          /* Check if an entire PLT chunk has just been eliminated.  */
          if (reloc_index % PLT_ENTRIES_PER_CHUNK == 0)
            {
              /* The two magic GOT entries for that chunk can go away.  */
-             srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
+             srelgot = htab->elf.srelgot;
              BFD_ASSERT (srelgot != NULL);
              srelgot->reloc_count -= 2;
              srelgot->size -= 2 * sizeof (Elf32_External_Rela);
@@ -8458,12 +10322,10 @@ move_literal (bfd *abfd,
     {
       int r_type;
       unsigned i;
-      asection *target_sec;
       reloc_bfd_fix *fix;
       unsigned insert_at;
 
       r_type = ELF32_R_TYPE (r_rel->rela.r_info);
-      target_sec = r_reloc_get_section (r_rel);
 
       /* This is the difficult case.  We have to create a fix up.  */
       this_rela.r_offset = offset;
@@ -8473,8 +10335,8 @@ 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);
-      fix = reloc_bfd_fix_init (sec, offset, r_type, r_rel->abfd,
+      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,
                                FALSE);
@@ -8504,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;
@@ -8524,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)
@@ -8574,15 +10436,16 @@ relax_property_section (bfd *abfd,
 {
   Elf_Internal_Rela *internal_relocs;
   bfd_byte *contents;
-  unsigned i, nexti;
+  unsigned i;
   bfd_boolean ok = TRUE;
   bfd_boolean is_full_prop_section;
   size_t last_zfill_target_offset = 0;
   asection *last_zfill_target_sec = NULL;
   bfd_size_type sec_size;
+  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)
@@ -8591,10 +10454,11 @@ relax_property_section (bfd *abfd,
       goto error_return;
     }
 
-  is_full_prop_section =
-    ((strcmp (sec->name, XTENSA_PROP_SEC_NAME) == 0)
-     || (strncmp (sec->name, ".gnu.linkonce.prop.",
-                 sizeof ".gnu.linkonce.prop." - 1) == 0));
+  is_full_prop_section = xtensa_is_proptable_section (sec);
+  if (is_full_prop_section)
+    entry_size = 12;
+  else
+    entry_size = 8;
 
   if (internal_relocs)
     {
@@ -8623,12 +10487,8 @@ relax_property_section (bfd *abfd,
          size_p = &contents[irel->r_offset + 4];
          flags_p = NULL;
          if (is_full_prop_section)
-           {
-             flags_p = &contents[irel->r_offset + 8];
-             BFD_ASSERT (irel->r_offset + 12 <= sec_size);
-           }
-         else
-           BFD_ASSERT (irel->r_offset + 8 <= sec_size);
+           flags_p = &contents[irel->r_offset + 8];
+         BFD_ASSERT (irel->r_offset + entry_size <= sec_size);
 
          target_sec = r_reloc_get_section (&val.r_rel);
          target_relax_info = get_xtensa_relax_info (target_sec);
@@ -8638,14 +10498,17 @@ relax_property_section (bfd *abfd,
                  || target_relax_info->is_relaxable_asm_section ))
            {
              /* Translate the relocation's destination.  */
-             bfd_vma new_offset, new_end_offset;
+             bfd_vma old_offset = val.r_rel.target_offset;
+             bfd_vma new_offset;
              long old_size, new_size;
-
-             new_offset = offset_with_removed_text
-               (&target_relax_info->action_list, val.r_rel.target_offset);
+             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);
+             new_size = old_size;
 
              if (old_size == 0)
                {
@@ -8657,49 +10520,49 @@ relax_property_section (bfd *abfd,
                     offset before or after the fill address depending
                     on whether the expanding unreachable entry
                     preceeds it.  */
-                 if (last_zfill_target_sec
-                     && last_zfill_target_sec == target_sec
-                     && last_zfill_target_offset == val.r_rel.target_offset)
-                   new_end_offset = new_offset;
-                 else
+                 if (last_zfill_target_sec == 0
+                     || last_zfill_target_sec != target_sec
+                     || last_zfill_target_offset != old_offset)
                    {
-                     new_end_offset = new_offset;
-                     new_offset = offset_with_removed_text_before_fill
-                       (&target_relax_info->action_list,
-                        val.r_rel.target_offset);
+                     bfd_vma new_end_offset = new_offset;
+
+                     /* Recompute the new_offset, but this time don't
+                        include any fill inserted by relaxation.  */
+                     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
                         before the fill address.  */
-                     if (!flags_p
-                         || (bfd_get_32 (abfd, flags_p)
-                             & XTENSA_PROP_UNREACHABLE) == 0)
-                       new_end_offset = new_offset;
-                     else
+                     if (flags_p && (bfd_get_32 (abfd, flags_p)
+                                     & XTENSA_PROP_UNREACHABLE) != 0)
                        {
+                         new_size = new_end_offset - new_offset;
+
                          last_zfill_target_sec = target_sec;
-                         last_zfill_target_offset = val.r_rel.target_offset;
+                         last_zfill_target_offset = old_offset;
                        }
                    }
                }
              else
                {
-                 new_end_offset = offset_with_removed_text_before_fill
-                   (&target_relax_info->action_list,
-                    val.r_rel.target_offset + old_size);
+                 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;
                }
 
-             new_size = new_end_offset - new_offset;
-
              if (new_size != old_size)
                {
                  bfd_put_32 (abfd, new_size, size_p);
                  pin_contents (sec, contents);
                }
 
-             if (new_offset != val.r_rel.target_offset)
+             if (new_offset != old_offset)
                {
-                 bfd_vma diff = new_offset - val.r_rel.target_offset;
+                 bfd_vma diff = new_offset - old_offset;
                  irel->r_addend += diff;
                  pin_internal_relocs (sec, internal_relocs);
                }
@@ -8711,197 +10574,196 @@ 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
-                         || strcmp (sec->name, XTENSA_LIT_SEC_NAME) == 0))
+  if (internal_relocs && (!bfd_link_relocatable (link_info)
+                         || xtensa_is_littable_section (sec)))
     {
       Elf_Internal_Rela *last_irel = NULL;
+      Elf_Internal_Rela *irel, *next_rel, *rel_end;
       int removed_bytes = 0;
-      bfd_vma offset, last_irel_offset;
-      bfd_vma section_size;
-      bfd_size_type entry_size;
+      bfd_vma offset;
       flagword predef_flags;
 
-      if (is_full_prop_section)
-       entry_size = 12;
-      else
-       entry_size = 8;
-
       predef_flags = xtensa_get_property_predef_flags (sec);
 
-      /* Walk over memory and irels at the same time.
-         This REQUIRES that the internal_relocs be sorted by offset.  */
+      /* Walk over memory and relocations at the same time.
+        This REQUIRES that the internal_relocs be sorted by offset.  */
       qsort (internal_relocs, sec->reloc_count, sizeof (Elf_Internal_Rela),
             internal_reloc_compare);
-      nexti = 0; /* Index into internal_relocs.  */
 
       pin_internal_relocs (sec, internal_relocs);
       pin_contents (sec, contents);
 
-      last_irel_offset = (bfd_vma) -1;
-      section_size = sec->size;
-      BFD_ASSERT (section_size % entry_size == 0);
+      next_rel = internal_relocs;
+      rel_end = internal_relocs + sec->reloc_count;
 
-      for (offset = 0; offset < section_size; offset += entry_size)
+      BFD_ASSERT (sec->size % entry_size == 0);
+
+      for (offset = 0; offset < sec->size; offset += entry_size)
        {
-         Elf_Internal_Rela *irel, *next_irel;
+         Elf_Internal_Rela *offset_rel, *extra_rel;
          bfd_vma bytes_to_remove, size, actual_offset;
-         bfd_boolean remove_this_irel;
+         bfd_boolean remove_this_rel;
          flagword flags;
 
-         irel = NULL;
-         next_irel = NULL;
-
-         /* Find the next two relocations (if there are that many left),
-            skipping over any R_XTENSA_NONE relocs.  On entry, "nexti" is
-            the starting reloc index.  After these two loops, "i"
-            is the index of the first non-NONE reloc past that starting
-            index, and "nexti" is the index for the next non-NONE reloc
-            after "i".  */
+         /* Find the first relocation for the entry at the current offset.
+            Adjust the offsets of any extra relocations for the previous
+            entry.  */
+         offset_rel = NULL;
+         if (next_rel)
+           {
+             for (irel = next_rel; irel < rel_end; irel++)
+               {
+                 if ((irel->r_offset == offset
+                      && ELF32_R_TYPE (irel->r_info) != R_XTENSA_NONE)
+                     || irel->r_offset > offset)
+                   {
+                     offset_rel = irel;
+                     break;
+                   }
+                 irel->r_offset -= removed_bytes;
+               }
+           }
 
-         for (i = nexti; i < sec->reloc_count; i++)
+         /* Find the next relocation (if there are any left).  */
+         extra_rel = NULL;
+         if (offset_rel)
            {
-             if (ELF32_R_TYPE (internal_relocs[i].r_info) != R_XTENSA_NONE)
+             for (irel = offset_rel + 1; irel < rel_end; irel++)
                {
-                 irel = &internal_relocs[i];
-                 break;
+                 if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_NONE)
+                   {
+                     extra_rel = irel;
+                     break;
+                   }
                }
-             internal_relocs[i].r_offset -= removed_bytes;
            }
 
-         for (nexti = i + 1; nexti < sec->reloc_count; nexti++)
+         /* Check if there are relocations on the current entry.  There
+            should usually be a relocation on the offset field.  If there
+            are relocations on the size or flags, then we can't optimize
+            this entry.  Also, find the next relocation to examine on the
+            next iteration.  */
+         if (offset_rel)
            {
-             if (ELF32_R_TYPE (internal_relocs[nexti].r_info)
-                 != R_XTENSA_NONE)
+             if (offset_rel->r_offset >= offset + entry_size)
                {
-                 next_irel = &internal_relocs[nexti];
-                 break;
+                 next_rel = offset_rel;
+                 /* There are no relocations on the current entry, but we
+                    might still be able to remove it if the size is zero.  */
+                 offset_rel = NULL;
+               }
+             else if (offset_rel->r_offset > offset
+                      || (extra_rel
+                          && extra_rel->r_offset < offset + entry_size))
+               {
+                 /* There is a relocation on the size or flags, so we can't
+                    do anything with this entry.  Continue with the next.  */
+                 next_rel = offset_rel;
+                 continue;
+               }
+             else
+               {
+                 BFD_ASSERT (offset_rel->r_offset == offset);
+                 offset_rel->r_offset -= removed_bytes;
+                 next_rel = offset_rel + 1;
                }
-             internal_relocs[nexti].r_offset -= removed_bytes;
            }
+         else
+           next_rel = NULL;
 
-         remove_this_irel = FALSE;
+         remove_this_rel = FALSE;
          bytes_to_remove = 0;
          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;
 
-         /* Check that the irels are sorted by offset,
-            with only one per address.  */
-         BFD_ASSERT (!irel || (int) irel->r_offset > (int) last_irel_offset); 
-         BFD_ASSERT (!next_irel || next_irel->r_offset > irel->r_offset);
-
-         /* Make sure there aren't relocs on the size or flag fields.  */
-         if ((irel && irel->r_offset == offset + 4)
-             || (is_full_prop_section 
-                 && irel && irel->r_offset == offset + 8))
-           {
-             irel->r_offset -= removed_bytes;
-             last_irel_offset = irel->r_offset;
-           }
-         else if (next_irel && (next_irel->r_offset == offset + 4
-                                || (is_full_prop_section 
-                                    && next_irel->r_offset == offset + 8)))
-           {
-             nexti += 1;
-             irel->r_offset -= removed_bytes;
-             next_irel->r_offset -= removed_bytes;
-             last_irel_offset = next_irel->r_offset;
-           }
-         else if (size == 0 && (flags & XTENSA_PROP_ALIGN) == 0
-                  && (flags & XTENSA_PROP_UNREACHABLE) == 0)
+         if (size == 0
+             && (flags & XTENSA_PROP_ALIGN) == 0
+             && (flags & XTENSA_PROP_UNREACHABLE) == 0)
            {
              /* Always remove entries with zero size and no alignment.  */
              bytes_to_remove = entry_size;
-             if (irel && irel->r_offset == offset)
-               {
-                 remove_this_irel = TRUE;
-
-                 irel->r_offset -= removed_bytes;
-                 last_irel_offset = irel->r_offset;
-               }
+             if (offset_rel)
+               remove_this_rel = TRUE;
            }
-         else if (irel && irel->r_offset == offset)
+         else if (offset_rel
+                  && ELF32_R_TYPE (offset_rel->r_info) == R_XTENSA_32)
            {
-             if (ELF32_R_TYPE (irel->r_info) == R_XTENSA_32)
+             if (last_irel)
                {
-                 if (last_irel)
+                 flagword old_flags;
+                 bfd_vma old_size =
+                   bfd_get_32 (abfd, &contents[last_irel->r_offset + 4]);
+                 bfd_vma old_address =
+                   (last_irel->r_addend
+                    + bfd_get_32 (abfd, &contents[last_irel->r_offset]));
+                 bfd_vma new_address =
+                   (offset_rel->r_addend
+                    + bfd_get_32 (abfd, &contents[actual_offset]));
+                 if (is_full_prop_section)
+                   old_flags = bfd_get_32
+                     (abfd, &contents[last_irel->r_offset + 8]);
+                 else
+                   old_flags = predef_flags;
+
+                 if ((ELF32_R_SYM (offset_rel->r_info)
+                      == ELF32_R_SYM (last_irel->r_info))
+                     && old_address + old_size == new_address
+                     && old_flags == flags
+                     && (old_flags & XTENSA_PROP_INSN_BRANCH_TARGET) == 0
+                     && (old_flags & XTENSA_PROP_INSN_LOOP_TARGET) == 0)
                    {
-                     flagword old_flags;
-                     bfd_vma old_size =
-                       bfd_get_32 (abfd, &contents[last_irel->r_offset + 4]);
-                     bfd_vma old_address =
-                       (last_irel->r_addend
-                        + bfd_get_32 (abfd, &contents[last_irel->r_offset]));
-                     bfd_vma new_address =
-                       (irel->r_addend
-                        + bfd_get_32 (abfd, &contents[actual_offset]));
-                     if (is_full_prop_section) 
-                       old_flags = bfd_get_32
-                         (abfd, &contents[last_irel->r_offset + 8]);
-                     else
-                       old_flags = predef_flags;
-
-                     if ((ELF32_R_SYM (irel->r_info)
-                          == ELF32_R_SYM (last_irel->r_info))
-                         && old_address + old_size == new_address
-                         && old_flags == flags
-                         && (old_flags & XTENSA_PROP_INSN_BRANCH_TARGET) == 0
-                         && (old_flags & XTENSA_PROP_INSN_LOOP_TARGET) == 0)
-                       {
-                         /* Fix the old size.  */
-                         bfd_put_32 (abfd, old_size + size,
-                                     &contents[last_irel->r_offset + 4]);
-                         bytes_to_remove = entry_size;
-                         remove_this_irel = TRUE;
-                       }
-                     else
-                       last_irel = irel;
+                     /* Fix the old size.  */
+                     bfd_put_32 (abfd, old_size + size,
+                                 &contents[last_irel->r_offset + 4]);
+                     bytes_to_remove = entry_size;
+                     remove_this_rel = TRUE;
                    }
                  else
-                   last_irel = irel;
+                   last_irel = offset_rel;
                }
-
-             irel->r_offset -= removed_bytes;
-             last_irel_offset = irel->r_offset;
+             else
+               last_irel = offset_rel;
            }
 
-         if (remove_this_irel)
+         if (remove_this_rel)
            {
-             irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE);
-             irel->r_offset -= bytes_to_remove;
+             offset_rel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE);
+             offset_rel->r_offset = 0;
            }
 
          if (bytes_to_remove != 0)
            {
              removed_bytes += bytes_to_remove;
-             if (offset + bytes_to_remove < section_size)
+             if (offset + bytes_to_remove < sec->size)
                memmove (&contents[actual_offset],
                         &contents[actual_offset + bytes_to_remove],
-                        section_size - offset - bytes_to_remove);
+                        sec->size - offset - bytes_to_remove);
            }
        }
 
       if (removed_bytes)
        {
+         /* Fix up any extra relocations on the last entry.  */
+         for (irel = next_rel; irel < rel_end; irel++)
+           irel->r_offset -= removed_bytes;
+
          /* Clear the removed bytes.  */
-         memset (&contents[section_size - removed_bytes], 0, removed_bytes);
+         memset (&contents[sec->size - removed_bytes], 0, removed_bytes);
 
-         sec->size = section_size - removed_bytes;
+         if (sec->rawsize == 0)
+           sec->rawsize = sec->size;
+         sec->size -= removed_bytes;
 
          if (xtensa_is_littable_section (sec))
            {
-             bfd *dynobj = elf_hash_table (link_info)->dynobj;
-             if (dynobj)
-               {
-                 asection *sgotloc =
-                   bfd_get_section_by_name (dynobj, ".got.loc");
-                 if (sgotloc)
-                   sgotloc->size -= removed_bytes;
-               }
+             asection *sgotloc = elf_xtensa_hash_table (link_info)->sgotloc;
+             if (sgotloc)
+               sgotloc->size -= removed_bytes;
            }
        }
     }
@@ -8948,19 +10810,16 @@ relax_section_symbols (bfd *abfd, asection *sec)
 
       if (isym->st_shndx == sec_shndx)
        {
-         bfd_vma new_address = offset_with_removed_text
-           (&relax_info->action_list, isym->st_value);
-         bfd_vma new_size = isym->st_size;
+         bfd_vma orig_addr = isym->st_value;
+         int removed = removed_by_actions_map (&relax_info->action_list,
+                                               orig_addr, FALSE);
 
+         isym->st_value -= removed;
          if (ELF32_ST_TYPE (isym->st_info) == STT_FUNC)
-           {
-             bfd_vma new_end = offset_with_removed_text
-               (&relax_info->action_list, isym->st_value + isym->st_size);
-             new_size = new_end - new_address;
-           }
-
-         isym->st_value = new_address;
-         isym->st_size = new_size;
+           isym->st_size -=
+             removed_by_actions_map (&relax_info->action_list,
+                                     orig_addr + isym->st_size, FALSE) -
+             removed;
        }
     }
 
@@ -8978,20 +10837,17 @@ relax_section_symbols (bfd *abfd, asection *sec)
           || sym_hash->root.type == bfd_link_hash_defweak)
          && sym_hash->root.u.def.section == sec)
        {
-         bfd_vma new_address = offset_with_removed_text
-           (&relax_info->action_list, sym_hash->root.u.def.value);
-         bfd_vma new_size = sym_hash->size;
+         bfd_vma orig_addr = sym_hash->root.u.def.value;
+         int removed = removed_by_actions_map (&relax_info->action_list,
+                                               orig_addr, FALSE);
 
-         if (sym_hash->type == STT_FUNC)
-           {
-             bfd_vma new_end = offset_with_removed_text
-               (&relax_info->action_list,
-                sym_hash->root.u.def.value + sym_hash->size);
-             new_size = new_end - new_address;
-           }
+         sym_hash->root.u.def.value -= removed;
 
-         sym_hash->root.u.def.value = new_address;
-         sym_hash->size = new_size;
+         if (sym_hash->type == STT_FUNC)
+           sym_hash->size -=
+             removed_by_actions_map (&relax_info->action_list,
+                                     orig_addr + sym_hash->size, FALSE) -
+             removed;
        }
     }
 
@@ -9029,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;
        }
@@ -9087,28 +10944,32 @@ do_fix_for_final_link (Elf_Internal_Rela *rel,
 /* Miscellaneous utility functions....  */
 
 static asection *
-elf_xtensa_get_plt_section (bfd *dynobj, int chunk)
+elf_xtensa_get_plt_section (struct bfd_link_info *info, int chunk)
 {
-  char plt_name[10];
+  bfd *dynobj;
+  char plt_name[17];
 
   if (chunk == 0)
-    return bfd_get_section_by_name (dynobj, ".plt");
+    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 (bfd *dynobj, int chunk)
+elf_xtensa_get_gotplt_section (struct bfd_link_info *info, int chunk)
 {
-  char got_name[14];
+  bfd *dynobj;
+  char got_name[21];
 
   if (chunk == 0)
-    return bfd_get_section_by_name (dynobj, ".got.plt");
+    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);
 }
 
 
@@ -9135,15 +10996,12 @@ get_elf_r_symndx_section (bfd *abfd, unsigned long r_symndx)
 
       if (section_index == SHN_UNDEF)
        target_sec = bfd_und_section_ptr;
-      else if (section_index > 0 && section_index < SHN_LORESERVE)
-       target_sec = bfd_section_from_elf_index (abfd, section_index);
       else if (section_index == SHN_ABS)
        target_sec = bfd_abs_section_ptr;
       else if (section_index == SHN_COMMON)
        target_sec = bfd_com_section_ptr;
       else
-       /* Who knows?  */
-       target_sec = NULL;
+       target_sec = bfd_section_from_elf_index (abfd, section_index);
     }
   else
     {
@@ -9151,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)
        {
@@ -9216,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;
@@ -9254,39 +11112,45 @@ pcrel_reloc_fits (xtensa_opcode opc,
 }
 
 
-static int linkonce_len = sizeof (".gnu.linkonce.") - 1;
-static int insn_sec_len = sizeof (XTENSA_INSN_SEC_NAME) - 1;
-static int lit_sec_len = sizeof (XTENSA_LIT_SEC_NAME) - 1;
-static int prop_sec_len = sizeof (XTENSA_PROP_SEC_NAME) - 1;
-
-
-static bfd_boolean 
+static bfd_boolean
 xtensa_is_property_section (asection *sec)
 {
-  if (strncmp (XTENSA_INSN_SEC_NAME, sec->name, insn_sec_len) == 0
-      || strncmp (XTENSA_LIT_SEC_NAME, sec->name, lit_sec_len) == 0
-      || strncmp (XTENSA_PROP_SEC_NAME, sec->name, prop_sec_len) == 0)
+  if (xtensa_is_insntable_section (sec)
+      || xtensa_is_littable_section (sec)
+      || xtensa_is_proptable_section (sec))
     return TRUE;
 
-  if (strncmp (".gnu.linkonce.", sec->name, linkonce_len) == 0
-      && (strncmp (&sec->name[linkonce_len], "x.", 2) == 0
-         || strncmp (&sec->name[linkonce_len], "p.", 2) == 0
-         || strncmp (&sec->name[linkonce_len], "prop.", 5) == 0))
+  return FALSE;
+}
+
+
+static bfd_boolean
+xtensa_is_insntable_section (asection *sec)
+{
+  if (CONST_STRNEQ (sec->name, XTENSA_INSN_SEC_NAME)
+      || CONST_STRNEQ (sec->name, ".gnu.linkonce.x."))
     return TRUE;
 
   return FALSE;
 }
 
 
-static bfd_boolean 
+static bfd_boolean
 xtensa_is_littable_section (asection *sec)
 {
-  if (strncmp (XTENSA_LIT_SEC_NAME, sec->name, lit_sec_len) == 0)
+  if (CONST_STRNEQ (sec->name, XTENSA_LIT_SEC_NAME)
+      || CONST_STRNEQ (sec->name, ".gnu.linkonce.p."))
     return TRUE;
 
-  if (strncmp (".gnu.linkonce.", sec->name, linkonce_len) == 0
-      && sec->name[linkonce_len] == 'p'
-      && sec->name[linkonce_len + 1] == '.')
+  return FALSE;
+}
+
+
+static bfd_boolean
+xtensa_is_proptable_section (asection *sec)
+{
+  if (CONST_STRNEQ (sec->name, XTENSA_PROP_SEC_NAME)
+      || CONST_STRNEQ (sec->name, ".gnu.linkonce.prop."))
     return TRUE;
 
   return FALSE;
@@ -9328,19 +11192,65 @@ internal_reloc_matches (const void *ap, const void *bp)
 }
 
 
-char *
-xtensa_get_property_section_name (asection *sec, const char *base_name)
+/* Predicate function used to look up a section in a particular group.  */
+
+static bfd_boolean
+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
+             && strcmp (group_name, gname) == 0));
+}
+
+
+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,
+                             bfd_boolean separate_sections)
 {
-  if (strncmp (sec->name, ".gnu.linkonce.", linkonce_len) == 0)
+  const char *suffix, *group_name;
+  char *prop_sec_name;
+
+  group_name = elf_group_name (sec);
+  if (group_name)
+    {
+      suffix = strrchr (sec->name, '.');
+      if (suffix == sec->name)
+       suffix = 0;
+      prop_sec_name = xtensa_add_names (base_name, suffix);
+    }
+  else if (strncmp (sec->name, ".gnu.linkonce.", linkonce_len) == 0)
     {
-      char *prop_sec_name;
-      const char *suffix;
       char *linkonce_kind = 0;
 
-      if (strcmp (base_name, XTENSA_INSN_SEC_NAME) == 0) 
-       linkonce_kind = "x";
-      else if (strcmp (base_name, XTENSA_LIT_SEC_NAME) == 0) 
-       linkonce_kind = "p";
+      if (strcmp (base_name, XTENSA_INSN_SEC_NAME) == 0)
+       linkonce_kind = "x.";
+      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.";
       else
@@ -9353,31 +11263,96 @@ xtensa_get_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).  */
-      if (strncmp (suffix, "t.", 2) == 0 && linkonce_kind[1] == '.')
-        suffix += 2;
+        the new linkonce_kind (but not for "prop" sections).  */
+      if (CONST_STRNEQ (suffix, "t.") && linkonce_kind[1] == '.')
+       suffix += 2;
       strcat (prop_sec_name + linkonce_len, suffix);
+    }
+  else
+    {
+      prop_sec_name = xtensa_add_names (base_name,
+                                       separate_sections ? sec->name : NULL);
+    }
+
+  return prop_sec_name;
+}
 
-      return prop_sec_name;
+
+static asection *
+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,
+                                               separate_section);
+  prop_sec = bfd_get_section_by_name_if (sec->owner, prop_sec_name,
+                                        match_section_group,
+                                        (void *) elf_group_name (sec));
+  free (prop_sec_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)
+{
+  char *prop_sec_name;
+  asection *prop_sec;
+
+  /* Check if the section already exists.  */
+  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));
+  /* If not, create it.  */
+  if (! prop_sec)
+    {
+      flagword flags = (SEC_RELOC | SEC_HAS_CONTENTS | SEC_READONLY);
+      flags |= (bfd_section_flags (sec)
+               & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES));
+
+      prop_sec = bfd_make_section_anyway_with_flags
+       (sec->owner, strdup (prop_sec_name), flags);
+      if (! prop_sec)
+       return 0;
+
+      elf_group_name (prop_sec) = elf_group_name (sec);
     }
 
-  return strdup (base_name);
+  free (prop_sec_name);
+  return prop_sec;
 }
 
 
 flagword
 xtensa_get_property_predef_flags (asection *sec)
 {
-  if (strcmp (sec->name, XTENSA_INSN_SEC_NAME) == 0
-      || strncmp (sec->name, ".gnu.linkonce.x.",
-                 sizeof ".gnu.linkonce.x." - 1) == 0)
+  if (xtensa_is_insntable_section (sec))
     return (XTENSA_PROP_INSN
-           | XTENSA_PROP_INSN_NO_TRANSFORM
+           | XTENSA_PROP_NO_TRANSFORM
            | XTENSA_PROP_INSN_NO_REORDER);
 
   if (xtensa_is_littable_section (sec))
     return (XTENSA_PROP_LITERAL
-           | XTENSA_PROP_INSN_NO_TRANSFORM
+           | XTENSA_PROP_NO_TRANSFORM
            | XTENSA_PROP_INSN_NO_REORDER);
 
   return 0;
@@ -9404,13 +11379,13 @@ xtensa_callback_required_dependence (bfd *abfd,
   /* ".plt*" sections have no explicit relocations but they contain L32R
      instructions that reference the corresponding ".got.plt*" sections.  */
   if ((sec->flags & SEC_LINKER_CREATED) != 0
-      && strncmp (sec->name, ".plt", 4) == 0)
+      && CONST_STRNEQ (sec->name, ".plt"))
     {
       asection *sgotplt;
 
       /* 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];
@@ -9420,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);
 
@@ -9430,7 +11405,12 @@ xtensa_callback_required_dependence (bfd *abfd,
       (*callback) (sec, sec_size, sgotplt, 0, closure);
     }
 
-  internal_relocs = retrieve_internal_relocs (abfd, sec, 
+  /* Only ELF files are supported for Xtensa.  Check here to avoid a segfault
+     when building uclibc, which runs "ld -b binary /dev/null".  */
+  if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+    return ok;
+
+  internal_relocs = retrieve_internal_relocs (abfd, sec,
                                              link_info->keep_memory);
   if (internal_relocs == NULL
       || sec->reloc_count == 0)
@@ -9479,80 +11459,27 @@ xtensa_callback_required_dependence (bfd *abfd,
 /* The default literal sections should always be marked as "code" (i.e.,
    SHF_EXECINSTR).  This is particularly important for the Linux kernel
    module loader so that the literals are not placed after the text.  */
-static struct bfd_elf_special_section const
-  xtensa_special_sections_f[]=
-{
-  { ".fini.literal", 13, 0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { NULL,             0, 0, 0,            0 }
-};
-
-static struct bfd_elf_special_section const
-  xtensa_special_sections_i[]=
-{
-  { ".init.literal", 13, 0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { NULL,             0, 0, 0,            0 }
-};
-static struct bfd_elf_special_section const
-  xtensa_special_sections_l[]=
+static const struct bfd_elf_special_section elf_xtensa_special_sections[] =
 {
-  { ".literal",       8, 0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { NULL,             0, 0, 0,            0 }
-};
-
-static struct bfd_elf_special_section const *
-  elf_xtensa_special_sections[27] =
-{
-  NULL,                                /* 'a' */
-  NULL,                                /* 'b' */
-  NULL,                                /* 'c' */
-  NULL,                                /* 'd' */
-  NULL,                                /* 'e' */
-  xtensa_special_sections_f,   /* 'f' */
-  NULL,                                /* 'g' */
-  NULL,                                /* 'h' */
-  xtensa_special_sections_i,   /* 'i' */
-  NULL,                                /* 'j' */
-  NULL,                                /* 'k' */
-  xtensa_special_sections_l,   /* 'l' */
-  NULL,                                /* 'm' */
-  NULL,                                /* 'n' */
-  NULL,                                /* 'o' */
-  NULL,                                /* 'p' */
-  NULL,                                /* 'q' */
-  NULL,                                /* 'r' */
-  NULL,                                /* 's' */
-  NULL,                                /* 't' */
-  NULL,                                /* 'u' */
-  NULL,                                /* 'v' */
-  NULL,                                /* 'w' */
-  NULL,                                /* 'x' */
-  NULL,                                /* 'y' */
-  NULL,                                /* 'z' */
-  NULL                         /* other */
+  { 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 }
 };
-
 \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
 
-/* The new EM_XTENSA value will be recognized beginning in the Xtensa T1040
-   release. However, we still have to generate files with the EM_XTENSA_OLD
-   value so that pre-T1040 tools can read the files.  As soon as we stop
-   caring about pre-T1040 tools, the following two values should be
-   swapped. At the same time, any other code that uses EM_XTENSA_OLD
-   should be changed to use EM_XTENSA.  */
-#define ELF_MACHINE_CODE               EM_XTENSA_OLD
-#define ELF_MACHINE_ALT1               EM_XTENSA
-
-#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_MACHINE_CODE               EM_XTENSA
+#define ELF_MACHINE_ALT1               EM_XTENSA_OLD
+
+#define ELF_MAXPAGESIZE                        0x1000
 #endif /* ELF_ARCH */
 
 #define elf_backend_can_gc_sections    1
@@ -9561,15 +11488,21 @@ static struct bfd_elf_special_section const *
 #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
 
+#define bfd_elf32_mkobject                  elf_xtensa_mkobject
+
 #define bfd_elf32_bfd_merge_private_bfd_data elf_xtensa_merge_private_bfd_data
 #define bfd_elf32_new_section_hook          elf_xtensa_new_section_hook
 #define bfd_elf32_bfd_print_private_bfd_data elf_xtensa_print_private_bfd_data
 #define bfd_elf32_bfd_relax_section         elf_xtensa_relax_section
 #define bfd_elf32_bfd_reloc_type_lookup             elf_xtensa_reloc_type_lookup
+#define bfd_elf32_bfd_reloc_name_lookup \
+  elf_xtensa_reloc_name_lookup
 #define bfd_elf32_bfd_set_private_flags             elf_xtensa_set_private_flags
+#define bfd_elf32_bfd_link_hash_table_create elf_xtensa_link_hash_table_create
 
 #define elf_backend_adjust_dynamic_symbol    elf_xtensa_adjust_dynamic_symbol
 #define elf_backend_check_relocs            elf_xtensa_check_relocs
@@ -9580,15 +11513,17 @@ static struct bfd_elf_special_section const *
 #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
-#define elf_backend_modify_segment_map      elf_xtensa_modify_segment_map
 #define elf_backend_object_p                elf_xtensa_object_p
 #define elf_backend_reloc_type_class        elf_xtensa_reloc_type_class
 #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_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
 
 #include "elf32-target.h"