]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - bfd/elf32-xtensa.c
Update the address and phone number of the FSF organization in the GPL notices
[thirdparty/binutils-gdb.git] / bfd / elf32-xtensa.c
index 495c275027d5d5e91de4548353bd965182b893d4..7b4a3f0761c1080da15ddd5576468157df8a47f5 100644 (file)
@@ -1,5 +1,5 @@
 /* Xtensa-specific support for 32-bit ELF.
-   Copyright 2003, 2004 Free Software Foundation, Inc.
+   Copyright 2003, 2004, 2005 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -15,8 +15,8 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
 
 #include "bfd.h"
 #include "sysdep.h"
 #include "xtensa-isa.h"
 #include "xtensa-config.h"
 
-/* Main interface functions.  */
-static void elf_xtensa_info_to_howto_rela
-  PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
-static reloc_howto_type *elf_xtensa_reloc_type_lookup
-  PARAMS ((bfd *abfd, bfd_reloc_code_real_type code));
-extern int xtensa_read_table_entries
-  PARAMS ((bfd *, asection *, property_table_entry **, const char *));
-static bfd_boolean elf_xtensa_check_relocs
-  PARAMS ((bfd *, struct bfd_link_info *, asection *,
-          const Elf_Internal_Rela *));
-static void elf_xtensa_hide_symbol
-  PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *, bfd_boolean));
-static asection *elf_xtensa_gc_mark_hook
-  PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *,
-          struct elf_link_hash_entry *, Elf_Internal_Sym *));
-static bfd_boolean elf_xtensa_gc_sweep_hook
-  PARAMS ((bfd *, struct bfd_link_info *, asection *,
-          const Elf_Internal_Rela *));
-static bfd_boolean elf_xtensa_create_dynamic_sections
-  PARAMS ((bfd *, struct bfd_link_info *));
-static bfd_boolean elf_xtensa_adjust_dynamic_symbol
-  PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
-static bfd_boolean elf_xtensa_size_dynamic_sections
-  PARAMS ((bfd *, struct bfd_link_info *));
-static bfd_boolean elf_xtensa_modify_segment_map
-  PARAMS ((bfd *, struct bfd_link_info *));
-static bfd_boolean elf_xtensa_relocate_section
-  PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
-          Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
-static bfd_boolean elf_xtensa_relax_section
-  PARAMS ((bfd *, asection *, struct bfd_link_info *, bfd_boolean *again));
-static bfd_boolean elf_xtensa_finish_dynamic_symbol
-  PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *,
-          Elf_Internal_Sym *));
-static bfd_boolean elf_xtensa_finish_dynamic_sections
-  PARAMS ((bfd *, struct bfd_link_info *));
-static bfd_boolean elf_xtensa_merge_private_bfd_data
-  PARAMS ((bfd *, bfd *));
-static bfd_boolean elf_xtensa_set_private_flags
-  PARAMS ((bfd *, flagword));
-extern flagword elf_xtensa_get_private_bfd_flags
-  PARAMS ((bfd *));
-static bfd_boolean elf_xtensa_print_private_bfd_data
-  PARAMS ((bfd *, PTR));
-static bfd_boolean elf_xtensa_object_p
-  PARAMS ((bfd *));
-static void elf_xtensa_final_write_processing
-  PARAMS ((bfd *, bfd_boolean));
-static enum elf_reloc_type_class elf_xtensa_reloc_type_class
-  PARAMS ((const Elf_Internal_Rela *));
-static bfd_boolean elf_xtensa_discard_info
-  PARAMS ((bfd *, struct elf_reloc_cookie *, struct bfd_link_info *));
-static bfd_boolean elf_xtensa_ignore_discarded_relocs
-  PARAMS ((asection *));
-static bfd_boolean elf_xtensa_grok_prstatus
-  PARAMS ((bfd *, Elf_Internal_Note *));
-static bfd_boolean elf_xtensa_grok_psinfo
-  PARAMS ((bfd *, Elf_Internal_Note *));
-static bfd_boolean elf_xtensa_new_section_hook
-  PARAMS ((bfd *, asection *));
-
+#define XTENSA_NO_NOP_REMOVAL 0
 
 /* Local helper functions.  */
 
-static bfd_boolean xtensa_elf_dynamic_symbol_p
-  PARAMS ((struct elf_link_hash_entry *, struct bfd_link_info *));
-static int property_table_compare
-  PARAMS ((const PTR, const PTR));
-static bfd_boolean elf_xtensa_in_literal_pool
-  PARAMS ((property_table_entry *, int, bfd_vma));
-static void elf_xtensa_make_sym_local
-  PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
-static bfd_boolean add_extra_plt_sections
-  PARAMS ((bfd *, int));
-static bfd_boolean elf_xtensa_fix_refcounts
-  PARAMS ((struct elf_link_hash_entry *, PTR));
-static bfd_boolean elf_xtensa_allocate_plt_size
-  PARAMS ((struct elf_link_hash_entry *, PTR));
-static bfd_boolean elf_xtensa_allocate_got_size
-  PARAMS ((struct elf_link_hash_entry *, PTR));
-static void elf_xtensa_allocate_local_got_size
-  PARAMS ((struct bfd_link_info *, asection *));
-static bfd_reloc_status_type elf_xtensa_do_reloc
-  PARAMS ((reloc_howto_type *, bfd *, asection *, bfd_vma, bfd_byte *,
-          bfd_vma, bfd_boolean, char **));
-static char * vsprint_msg
-  VPARAMS ((const char *, const char *, int, ...));
-static char *build_encoding_error_message
-  PARAMS ((xtensa_opcode, xtensa_encode_result));
+static bfd_boolean add_extra_plt_sections (bfd *, int);
+static char *build_encoding_error_message (xtensa_opcode, bfd_vma);
 static bfd_reloc_status_type bfd_elf_xtensa_reloc
-  PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
-static void do_fix_for_relocatable_link
-  PARAMS ((Elf_Internal_Rela *, bfd *, asection *));
+  (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+static bfd_boolean do_fix_for_relocatable_link
+  (Elf_Internal_Rela *, bfd *, asection *, bfd_byte *);
 static void do_fix_for_final_link
-  PARAMS ((Elf_Internal_Rela *, asection *, bfd_vma *));
-static bfd_vma elf_xtensa_create_plt_entry
-  PARAMS ((bfd *, bfd *, unsigned));
-static int elf_xtensa_combine_prop_entries
-  PARAMS ((bfd *, asection *, asection *));
-static bfd_boolean elf_xtensa_discard_info_for_section
-  PARAMS ((bfd *, struct elf_reloc_cookie *, struct bfd_link_info *,
-          asection *));
+  (Elf_Internal_Rela *, bfd *, asection *, bfd_byte *, bfd_vma *);
 
 /* Local functions to handle Xtensa configurability.  */
 
-static void init_call_opcodes
-  PARAMS ((void));
-static bfd_boolean is_indirect_call_opcode
-  PARAMS ((xtensa_opcode));
-static bfd_boolean is_direct_call_opcode
-  PARAMS ((xtensa_opcode));
-static bfd_boolean is_windowed_call_opcode
-  PARAMS ((xtensa_opcode));
-static xtensa_opcode get_l32r_opcode
-  PARAMS ((void));
-static bfd_vma l32r_offset
-  PARAMS ((bfd_vma, bfd_vma));
-static int get_relocation_opnd
-  PARAMS ((Elf_Internal_Rela *));
+static bfd_boolean is_indirect_call_opcode (xtensa_opcode);
+static bfd_boolean is_direct_call_opcode (xtensa_opcode);
+static bfd_boolean is_windowed_call_opcode (xtensa_opcode);
+static xtensa_opcode get_const16_opcode (void);
+static xtensa_opcode get_l32r_opcode (void);
+static bfd_vma l32r_offset (bfd_vma, bfd_vma);
+static int get_relocation_opnd (xtensa_opcode, int);
+static int get_relocation_slot (int);
 static xtensa_opcode get_relocation_opcode
-  PARAMS ((asection *, bfd_byte *, Elf_Internal_Rela *));
+  (bfd *, asection *, bfd_byte *, Elf_Internal_Rela *);
 static bfd_boolean is_l32r_relocation
-  PARAMS ((asection *, bfd_byte *, Elf_Internal_Rela *));
+  (bfd *, asection *, bfd_byte *, Elf_Internal_Rela *);
+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 xtensa_opcode insn_decode_opcode
+  (bfd_byte *, bfd_size_type, bfd_size_type, int);
+static bfd_boolean check_branch_target_aligned
+  (bfd_byte *, bfd_size_type, bfd_vma, bfd_vma);
+static bfd_boolean check_loop_aligned
+  (bfd_byte *, bfd_size_type, bfd_vma, bfd_vma);
+static bfd_boolean check_branch_target_aligned_address (bfd_vma, int);
+static bfd_size_type get_asm_simplify_size
+  (bfd_byte *, bfd_size_type, bfd_size_type);
 
 /* Functions for link-time code simplifications.  */
 
-static bfd_reloc_status_type elf_xtensa_do_asm_simplify 
-  PARAMS ((bfd_byte *, bfd_vma, bfd_vma));
+static bfd_reloc_status_type elf_xtensa_do_asm_simplify
+  (bfd_byte *, bfd_vma, bfd_vma, char **);
 static bfd_reloc_status_type contract_asm_expansion
-  PARAMS ((bfd_byte *, bfd_vma, Elf_Internal_Rela *));
-static xtensa_opcode swap_callx_for_call_opcode
-  PARAMS ((xtensa_opcode));
-static xtensa_opcode get_expanded_call_opcode
-  PARAMS ((bfd_byte *, int));
+  (bfd_byte *, bfd_vma, Elf_Internal_Rela *, char **);
+static xtensa_opcode swap_callx_for_call_opcode (xtensa_opcode);
+static xtensa_opcode get_expanded_call_opcode (bfd_byte *, int, bfd_boolean *);
 
 /* Access to internal relocations, section contents and symbols.  */
 
 static Elf_Internal_Rela *retrieve_internal_relocs
-  PARAMS ((bfd *, asection *, bfd_boolean));
-static void pin_internal_relocs
-  PARAMS ((asection *, Elf_Internal_Rela *));
-static void release_internal_relocs
-  PARAMS ((asection *, Elf_Internal_Rela *));
-static bfd_byte *retrieve_contents
-  PARAMS ((bfd *, asection *, bfd_boolean));
-static void pin_contents
-  PARAMS ((asection *, bfd_byte *));
-static void release_contents
-  PARAMS ((asection *, bfd_byte *));
-static Elf_Internal_Sym *retrieve_local_syms
-  PARAMS ((bfd *));
+  (bfd *, asection *, bfd_boolean);
+static void pin_internal_relocs (asection *, Elf_Internal_Rela *);
+static void release_internal_relocs (asection *, Elf_Internal_Rela *);
+static bfd_byte *retrieve_contents (bfd *, asection *, bfd_boolean);
+static void pin_contents (asection *, bfd_byte *);
+static void release_contents (asection *, bfd_byte *);
+static Elf_Internal_Sym *retrieve_local_syms (bfd *);
 
 /* Miscellaneous utility functions.  */
 
-static asection *elf_xtensa_get_plt_section
-  PARAMS ((bfd *, int));
-static asection *elf_xtensa_get_gotplt_section
-  PARAMS ((bfd *, int));
-static asection *get_elf_r_symndx_section
-  PARAMS ((bfd *, unsigned long));
+static asection *elf_xtensa_get_plt_section (bfd *, int);
+static asection *elf_xtensa_get_gotplt_section (bfd *, int);
+static asection *get_elf_r_symndx_section (bfd *, unsigned long);
 static struct elf_link_hash_entry *get_elf_r_symndx_hash_entry
-  PARAMS ((bfd *, unsigned long));
-static bfd_vma get_elf_r_symndx_offset
-  PARAMS ((bfd *, unsigned long));
-static bfd_boolean pcrel_reloc_fits
-  PARAMS ((xtensa_operand, bfd_vma, bfd_vma));
-static bfd_boolean xtensa_is_property_section
-  PARAMS ((asection *));
-static bfd_boolean xtensa_is_littable_section
-  PARAMS ((asection *));
-static bfd_boolean is_literal_section
-  PARAMS ((asection *));
-static int internal_reloc_compare
-  PARAMS ((const PTR, const PTR));
-extern char *xtensa_get_property_section_name
-  PARAMS ((asection *, const char *));
+  (bfd *, unsigned long);
+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_littable_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 flagword xtensa_get_property_predef_flags (asection *);
 
 /* Other functions called directly by the linker.  */
 
 typedef void (*deps_callback_t)
-  PARAMS ((asection *, bfd_vma, asection *, bfd_vma, PTR));
+  (asection *, bfd_vma, asection *, bfd_vma, void *);
 extern bfd_boolean xtensa_callback_required_dependence
-  PARAMS ((bfd *, asection *, struct bfd_link_info *,
-          deps_callback_t, PTR));
+  (bfd *, asection *, struct bfd_link_info *, deps_callback_t, void *);
+
+
+/* Globally visible flag for choosing size optimization of NOP removal
+   instead of branch-target-aware minimization for NOP removal.
+   When nonzero, narrow all instructions and remove all NOPs possible
+   around longcall expansions.  */
+
+int elf32xtensa_size_opt;
 
 
+/* The "new_section_hook" is used to set up a per-section
+   "xtensa_relax_info" data structure with additional information used
+   during relaxation.  */
+
 typedef struct xtensa_relax_info_struct xtensa_relax_info;
 
 
@@ -234,12 +146,25 @@ typedef struct xtensa_relax_info_struct xtensa_relax_info;
 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
+   this information. */
+
+xtensa_isa xtensa_default_isa;
+
+
 /* When this is true, relocations may have been modified to refer to
    symbols from other input files.  The per-section list of "fix"
    records needs to be checked when resolving relocations.  */
 
 static bfd_boolean relaxing_section = FALSE;
 
+/* When this is true, during final links, literals that cannot be
+   coalesced and their relocations may be moved to other sections.  */
+
+int elf32xtensa_no_literal_movement = 1;
+
 \f
 static reloc_howto_type elf_howto_table[] =
 {
@@ -296,10 +221,115 @@ static reloc_howto_type elf_howto_table[] =
   /* 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)
+        FALSE, 0x00000000, 0x00000000, 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),
+
+  /* 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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+
+  /* "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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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),
+  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)
 };
 
-#ifdef DEBUG_GEN_RELOC
+#if DEBUG_GEN_RELOC
 #define TRACE(str) \
   fprintf (stderr, "Xtensa bfd reloc lookup %d (%s)\n", code, str)
 #else
@@ -307,9 +337,8 @@ static reloc_howto_type elf_howto_table[] =
 #endif
 
 static reloc_howto_type *
-elf_xtensa_reloc_type_lookup (abfd, code)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     bfd_reloc_code_real_type code;
+elf_xtensa_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+                             bfd_reloc_code_real_type code)
 {
   switch (code)
     {
@@ -321,6 +350,18 @@ elf_xtensa_reloc_type_lookup (abfd, code)
       TRACE ("BFD_RELOC_32");
       return &elf_howto_table[(unsigned) R_XTENSA_32 ];
 
+    case BFD_RELOC_XTENSA_DIFF8:
+      TRACE ("BFD_RELOC_XTENSA_DIFF8");
+      return &elf_howto_table[(unsigned) R_XTENSA_DIFF8 ];
+
+    case BFD_RELOC_XTENSA_DIFF16:
+      TRACE ("BFD_RELOC_XTENSA_DIFF16");
+      return &elf_howto_table[(unsigned) R_XTENSA_DIFF16 ];
+
+    case BFD_RELOC_XTENSA_DIFF32:
+      TRACE ("BFD_RELOC_XTENSA_DIFF32");
+      return &elf_howto_table[(unsigned) R_XTENSA_DIFF32 ];
+
     case BFD_RELOC_XTENSA_RTLD:
       TRACE ("BFD_RELOC_XTENSA_RTLD");
       return &elf_howto_table[(unsigned) R_XTENSA_RTLD ];
@@ -370,6 +411,22 @@ elf_xtensa_reloc_type_lookup (abfd, code)
       return &elf_howto_table[(unsigned) R_XTENSA_GNU_VTENTRY ];
 
     default:
+      if (code >= BFD_RELOC_XTENSA_SLOT0_OP
+         && code <= BFD_RELOC_XTENSA_SLOT14_OP)
+       {
+         unsigned n = (R_XTENSA_SLOT0_OP +
+                       (code - BFD_RELOC_XTENSA_SLOT0_OP));
+         return &elf_howto_table[n];
+       }
+
+      if (code >= BFD_RELOC_XTENSA_SLOT0_ALT
+         && code <= BFD_RELOC_XTENSA_SLOT14_ALT)
+       {
+         unsigned n = (R_XTENSA_SLOT0_ALT +
+                       (code - BFD_RELOC_XTENSA_SLOT0_ALT));
+         return &elf_howto_table[n];
+       }
+
       break;
     }
 
@@ -382,10 +439,9 @@ elf_xtensa_reloc_type_lookup (abfd, code)
    it in the BFD internal arelent representation of the relocation.  */
 
 static void
-elf_xtensa_info_to_howto_rela (abfd, cache_ptr, dst)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     arelent *cache_ptr;
-     Elf_Internal_Rela *dst;
+elf_xtensa_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED,
+                              arelent *cache_ptr,
+                              Elf_Internal_Rela *dst)
 {
   unsigned int r_type = ELF32_R_TYPE (dst->r_info);
 
@@ -446,9 +502,8 @@ static const bfd_byte elf_xtensa_le_plt_entry[PLT_ENTRY_SIZE] =
 
 
 static inline bfd_boolean
-xtensa_elf_dynamic_symbol_p (h, info)
-     struct elf_link_hash_entry *h;
-     struct bfd_link_info *info;
+xtensa_elf_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
      "ignore_protected" argument need not be set, because Xtensa code
@@ -461,15 +516,45 @@ xtensa_elf_dynamic_symbol_p (h, info)
 
 \f
 static int
-property_table_compare (ap, bp)
-     const PTR ap;
-     const PTR bp;
+property_table_compare (const void *ap, const void *bp)
 {
   const property_table_entry *a = (const property_table_entry *) ap;
   const property_table_entry *b = (const property_table_entry *) bp;
 
-  /* Check if one entry overlaps with the other; this shouldn't happen
-     except when searching for a match.  */
+  if (a->address == b->address)
+    {
+      if (a->size != b->size)
+       return (a->size - b->size);
+
+      if ((a->flags & XTENSA_PROP_ALIGN) != (b->flags & XTENSA_PROP_ALIGN))
+       return ((b->flags & XTENSA_PROP_ALIGN)
+               - (a->flags & XTENSA_PROP_ALIGN));
+
+      if ((a->flags & XTENSA_PROP_ALIGN)
+         && (GET_XTENSA_PROP_ALIGNMENT (a->flags)
+             != 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)
+               - (a->flags & XTENSA_PROP_UNREACHABLE));
+
+      return (a->flags - b->flags);
+    }
+
+  return (a->address - b->address);
+}
+
+
+static int
+property_table_matches (const void *ap, const void *bp)
+{
+  const property_table_entry *a = (const property_table_entry *) ap;
+  const property_table_entry *b = (const property_table_entry *) bp;
+
+  /* Check if one entry overlaps with the other.  */
   if ((b->address >= a->address && b->address < (a->address + a->size))
       || (a->address >= b->address && a->address < (b->address + b->size)))
     return 0;
@@ -478,47 +563,64 @@ property_table_compare (ap, bp)
 }
 
 
-/* Get the literal table or instruction table entries for the given
-   section.  Sets TABLE_P and returns the number of entries.  On error,
-   returns a negative value.  */
+/* Get the literal table or property table entries for the given
+   section.  Sets TABLE_P and returns the number of entries.  On
+   error, returns a negative value.  */
 
-int
-xtensa_read_table_entries (abfd, section, table_p, sec_name)
-     bfd *abfd;
-     asection *section;
-     property_table_entry **table_p;
-     const char *sec_name;
+static int
+xtensa_read_table_entries (bfd *abfd,
+                          asection *section,
+                          property_table_entry **table_p,
+                          const char *sec_name,
+                          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 block_count;
+  int blk, block_count;
   bfd_size_type num_records;
   Elf_Internal_Rela *internal_relocs;
   bfd_vma section_addr;
+  flagword predef_flags;
+  bfd_size_type table_entry_size;
+
+  if (!section
+      || !(section->flags & SEC_ALLOC)
+      || (section->flags & SEC_DEBUGGING))
+    {
+      *table_p = NULL;
+      return 0;
+    }
 
-  table_section_name = 
-    xtensa_get_property_section_name (section, sec_name);
+  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);
-  if (table_section != NULL)
+  if (table_section)
     table_size = table_section->size;
-  
+
   if (table_size == 0) 
     {
       *table_p = NULL;
       return 0;
     }
 
-  num_records = table_size / 8;
+  predef_flags = xtensa_get_property_predef_flags (table_section);
+  table_entry_size = 12;
+  if (predef_flags)
+    table_entry_size -= 4;
+
+  num_records = table_size / table_entry_size;
   table_data = retrieve_contents (abfd, table_section, TRUE);
   blocks = (property_table_entry *)
     bfd_malloc (num_records * sizeof (property_table_entry));
   block_count = 0;
-  
-  section_addr = section->output_section->vma + section->output_offset;
+
+  if (output_addr)
+    section_addr = section->output_section->vma + section->output_offset;
+  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.  */
@@ -541,11 +643,18 @@ xtensa_read_table_entries (abfd, section, table_p, sec_name)
          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++;
            }
        }
@@ -555,17 +664,23 @@ xtensa_read_table_entries (abfd, section, table_p, sec_name)
       /* 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 += 8
+      for (off = 0; off < table_size; off += table_entry_size
        {
          bfd_vma address = bfd_get_32 (abfd, table_data + off);
 
          if (address >= section_addr
-             && address < section_addr + section->size)
+             && 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++;
            }
        }
@@ -574,34 +689,64 @@ xtensa_read_table_entries (abfd, section, table_p, sec_name)
   release_contents (table_section, table_data);
   release_internal_relocs (table_section, internal_relocs);
 
-  if (block_count > 0) 
+  if (block_count > 0)
     {
       /* Now sort them into address order for easy reference.  */
       qsort (blocks, block_count, sizeof (property_table_entry),
             property_table_compare);
+
+      /* Check that the table contents are valid.  Problems may occur,
+         for example, if an unrelocated object file is stripped.  */
+      for (blk = 1; blk < block_count; blk++)
+       {
+         /* The only circumstance where two entries may legitimately
+            have the same address is when one of them is a zero-size
+            placeholder to mark a place where fill can be inserted.
+            The zero-size entry should come first.  */
+         if (blocks[blk - 1].address == blocks[blk].address &&
+             blocks[blk - 1].size != 0)
+           {
+             (*_bfd_error_handler) (_("%B(%A): invalid property table"),
+                                    abfd, section);
+             bfd_set_error (bfd_error_bad_value);
+             free (blocks);
+             return -1;
+           }
+       }
     }
-    
+
   *table_p = blocks;
   return block_count;
 }
 
 
-static bfd_boolean
-elf_xtensa_in_literal_pool (lit_table, lit_table_size, addr)
-     property_table_entry *lit_table;
-     int lit_table_size;
-     bfd_vma addr;
+static property_table_entry *
+elf_xtensa_find_property_entry (property_table_entry *property_table,
+                               int property_table_size,
+                               bfd_vma addr)
 {
   property_table_entry entry;
+  property_table_entry *rv;
 
-  if (lit_table_size == 0)
-    return FALSE;
+  if (property_table_size == 0)
+    return NULL;
 
   entry.address = addr;
   entry.size = 1;
+  entry.flags = 0;
 
-  if (bsearch (&entry, lit_table, lit_table_size,
-              sizeof (property_table_entry), property_table_compare))
+  rv = bsearch (&entry, property_table, property_table_size,
+               sizeof (property_table_entry), property_table_matches);
+  return rv;
+}
+
+
+static bfd_boolean
+elf_xtensa_in_literal_pool (property_table_entry *lit_table,
+                           int lit_table_size,
+                           bfd_vma addr)
+{
+  if (elf_xtensa_find_property_entry (lit_table, lit_table_size, addr))
     return TRUE;
 
   return FALSE;
@@ -612,11 +757,10 @@ elf_xtensa_in_literal_pool (lit_table, lit_table_size, addr)
    calculate needed space in the dynamic reloc sections.  */
 
 static bfd_boolean
-elf_xtensa_check_relocs (abfd, info, sec, relocs)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     asection *sec;
-     const Elf_Internal_Rela *relocs;
+elf_xtensa_check_relocs (bfd *abfd,
+                        struct bfd_link_info *info,
+                        asection *sec,
+                        const Elf_Internal_Rela *relocs)
 {
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
@@ -641,9 +785,8 @@ elf_xtensa_check_relocs (abfd, info, sec, relocs)
 
       if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
        {
-         (*_bfd_error_handler) (_("%s: bad symbol index: %d"),
-                                bfd_archive_filename (abfd),
-                                r_symndx);
+         (*_bfd_error_handler) (_("%B: bad symbol index: %d"),
+                                abfd, r_symndx);
          return FALSE;
        }
 
@@ -682,7 +825,7 @@ elf_xtensa_check_relocs (abfd, info, sec, relocs)
            {
              if (h->plt.refcount <= 0)
                {
-                 h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+                 h->needs_plt = 1;
                  h->plt.refcount = 1;
                }
              else
@@ -715,8 +858,8 @@ elf_xtensa_check_relocs (abfd, info, sec, relocs)
 
                  size = symtab_hdr->sh_info;
                  size *= sizeof (bfd_signed_vma);
-                 local_got_refcounts = ((bfd_signed_vma *)
-                                        bfd_zalloc (abfd, size));
+                 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;
@@ -728,8 +871,41 @@ elf_xtensa_check_relocs (abfd, info, sec, relocs)
        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;
 
@@ -757,10 +933,33 @@ elf_xtensa_check_relocs (abfd, info, sec, relocs)
 
 
 static void
-elf_xtensa_hide_symbol (info, h, force_local)
-     struct bfd_link_info *info;
-     struct elf_link_hash_entry *h;
-     bfd_boolean force_local;
+elf_xtensa_make_sym_local (struct bfd_link_info *info,
+                          struct elf_link_hash_entry *h)
+{
+  if (info->shared)
+    {
+      if (h->plt.refcount > 0)
+       {
+         /* Will use RELATIVE relocs instead of JMP_SLOT relocs.  */
+         if (h->got.refcount < 0)
+           h->got.refcount = 0;
+         h->got.refcount += h->plt.refcount;
+         h->plt.refcount = 0;
+       }
+    }
+  else
+    {
+      /* Don't need any dynamic relocations at all.  */
+      h->plt.refcount = 0;
+      h->got.refcount = 0;
+    }
+}
+
+
+static void
+elf_xtensa_hide_symbol (struct bfd_link_info *info,
+                       struct elf_link_hash_entry *h,
+                       bfd_boolean force_local)
 {
   /* For a shared link, move the plt refcount to the got refcount to leave
      space for RELATIVE relocs.  */
@@ -774,14 +973,13 @@ elf_xtensa_hide_symbol (info, h, force_local)
    relocation.  */
 
 static asection *
-elf_xtensa_gc_mark_hook (sec, info, rel, h, sym)
-     asection *sec;
-     struct bfd_link_info *info ATTRIBUTE_UNUSED;
-     Elf_Internal_Rela *rel;
-     struct elf_link_hash_entry *h;
-     Elf_Internal_Sym *sym;
+elf_xtensa_gc_mark_hook (asection *sec,
+                        struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                        Elf_Internal_Rela *rel,
+                        struct elf_link_hash_entry *h,
+                        Elf_Internal_Sym *sym)
 {
-  if (h != NULL)
+  if (h)
     {
       switch (ELF32_R_TYPE (rel->r_info))
        {
@@ -810,15 +1008,15 @@ elf_xtensa_gc_mark_hook (sec, info, rel, h, sym)
   return NULL;
 }
 
+
 /* Update the GOT & PLT entry reference counts
    for the section being removed.  */
 
 static bfd_boolean
-elf_xtensa_gc_sweep_hook (abfd, info, sec, relocs)
-     bfd *abfd;
-     struct bfd_link_info *info ATTRIBUTE_UNUSED;
-     asection *sec;
-     const Elf_Internal_Rela *relocs;
+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;
@@ -841,7 +1039,12 @@ elf_xtensa_gc_sweep_hook (abfd, info, sec, relocs)
 
       r_symndx = ELF32_R_SYM (rel->r_info);
       if (r_symndx >= symtab_hdr->sh_info)
-       h = sym_hashes[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)
@@ -877,9 +1080,7 @@ elf_xtensa_gc_sweep_hook (abfd, info, sec, relocs)
 /* Create all the dynamic sections.  */
 
 static bfd_boolean
-elf_xtensa_create_dynamic_sections (dynobj, info)
-     bfd *dynobj;
-     struct bfd_link_info *info;
+elf_xtensa_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
 {
   flagword flags, noalloc_flags;
   asection *s;
@@ -929,9 +1130,7 @@ elf_xtensa_create_dynamic_sections (dynobj, info)
 
 
 static bfd_boolean
-add_extra_plt_sections (dynobj, count)
-     bfd *dynobj;
-     int count;
+add_extra_plt_sections (bfd *dynobj, int count)
 {
   int chunk;
 
@@ -978,19 +1177,18 @@ add_extra_plt_sections (dynobj, count)
    understand.  */
 
 static bfd_boolean
-elf_xtensa_adjust_dynamic_symbol (info, h)
-     struct bfd_link_info *info ATTRIBUTE_UNUSED;
-     struct elf_link_hash_entry *h;
+elf_xtensa_adjust_dynamic_symbol (struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                                 struct elf_link_hash_entry *h)
 {
   /* 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->weakdef != NULL)
+  if (h->u.weakdef)
     {
-      BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined
-                 || h->weakdef->root.type == bfd_link_hash_defweak);
-      h->root.u.def.section = h->weakdef->root.u.def.section;
-      h->root.u.def.value = h->weakdef->root.u.def.value;
+      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;
       return TRUE;
     }
 
@@ -1002,35 +1200,8 @@ elf_xtensa_adjust_dynamic_symbol (info, h)
 }
 
 
-static void
-elf_xtensa_make_sym_local (info, h)
-     struct bfd_link_info *info;
-     struct elf_link_hash_entry *h;
-{
-  if (info->shared)
-    {
-      if (h->plt.refcount > 0)
-       {
-         /* Will use RELATIVE relocs instead of JMP_SLOT relocs.  */
-         if (h->got.refcount < 0)
-           h->got.refcount = 0;
-         h->got.refcount += h->plt.refcount;
-         h->plt.refcount = 0;
-       }
-    }
-  else
-    {
-      /* Don't need any dynamic relocations at all.  */
-      h->plt.refcount = 0;
-      h->got.refcount = 0;
-    }
-}
-
-
 static bfd_boolean
-elf_xtensa_fix_refcounts (h, arg)
-     struct elf_link_hash_entry *h;
-     PTR arg;
+elf_xtensa_fix_refcounts (struct elf_link_hash_entry *h, void *arg)
 {
   struct bfd_link_info *info = (struct bfd_link_info *) arg;
 
@@ -1045,9 +1216,7 @@ elf_xtensa_fix_refcounts (h, arg)
 
 
 static bfd_boolean
-elf_xtensa_allocate_plt_size (h, arg)
-     struct elf_link_hash_entry *h;
-     PTR arg;
+elf_xtensa_allocate_plt_size (struct elf_link_hash_entry *h, void *arg)
 {
   asection *srelplt = (asection *) arg;
 
@@ -1062,9 +1231,7 @@ elf_xtensa_allocate_plt_size (h, arg)
 
 
 static bfd_boolean
-elf_xtensa_allocate_got_size (h, arg)
-     struct elf_link_hash_entry *h;
-     PTR arg;
+elf_xtensa_allocate_got_size (struct elf_link_hash_entry *h, void *arg)
 {
   asection *srelgot = (asection *) arg;
 
@@ -1079,9 +1246,8 @@ elf_xtensa_allocate_got_size (h, arg)
 
 
 static void
-elf_xtensa_allocate_local_got_size (info, srelgot)
-     struct bfd_link_info *info;
-     asection *srelgot;
+elf_xtensa_allocate_local_got_size (struct bfd_link_info *info,
+                                   asection *srelgot)
 {
   bfd *i;
 
@@ -1111,9 +1277,8 @@ elf_xtensa_allocate_local_got_size (info, srelgot)
 /* Set the sizes of the dynamic sections.  */
 
 static bfd_boolean
-elf_xtensa_size_dynamic_sections (output_bfd, info)
-     bfd *output_bfd ATTRIBUTE_UNUSED;
-     struct bfd_link_info *info;
+elf_xtensa_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
+                                 struct bfd_link_info *info)
 {
   bfd *dynobj, *abfd;
   asection *s, *srelplt, *splt, *sgotplt, *srelgot, *spltlittbl, *sgotloc;
@@ -1149,7 +1314,7 @@ elf_xtensa_size_dynamic_sections (output_bfd, info)
       /* Adjust refcounts for symbols that we now know are not "dynamic".  */
       elf_link_hash_traverse (elf_hash_table (info),
                              elf_xtensa_fix_refcounts,
-                             (PTR) info);
+                             (void *) info);
 
       /* Allocate space in ".rela.got" for literals that reference
         global symbols.  */
@@ -1158,7 +1323,7 @@ elf_xtensa_size_dynamic_sections (output_bfd, info)
        abort ();
       elf_link_hash_traverse (elf_hash_table (info),
                              elf_xtensa_allocate_got_size,
-                             (PTR) srelgot);
+                             (void *) srelgot);
 
       /* If we are generating a shared object, we also need space in
         ".rela.got" for R_XTENSA_RELATIVE relocs for literals that
@@ -1172,7 +1337,7 @@ elf_xtensa_size_dynamic_sections (output_bfd, info)
        abort ();
       elf_link_hash_traverse (elf_hash_table (info),
                              elf_xtensa_allocate_plt_size,
-                             (PTR) srelplt);
+                             (void *) srelplt);
 
       /* 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.
@@ -1297,7 +1462,7 @@ elf_xtensa_size_dynamic_sections (output_bfd, info)
        }
 
       if (strip)
-       _bfd_strip_section_from_output (info, s);
+       s->flags |= SEC_EXCLUDE;
       else
        {
          /* Allocate memory for the section contents.  */
@@ -1380,14 +1545,13 @@ elf_xtensa_size_dynamic_sections (output_bfd, info)
    this and it probably ought to be moved into elf.c as well.  */
 
 static bfd_boolean
-elf_xtensa_modify_segment_map (abfd, info)
-     bfd *abfd;
-     struct bfd_link_info *info ATTRIBUTE_UNUSED;
+elf_xtensa_modify_segment_map (bfd *abfd,
+                              struct bfd_link_info *info ATTRIBUTE_UNUSED)
 {
   struct elf_segment_map **m_p;
 
   m_p = &elf_tdata (abfd)->segment_map;
-  while (*m_p != NULL)
+  while (*m_p)
     {
       if ((*m_p)->p_type == PT_LOAD && (*m_p)->count == 0)
        *m_p = (*m_p)->next;
@@ -1404,32 +1568,42 @@ elf_xtensa_modify_segment_map (abfd, info)
    howto.  */
 
 #define CALL_SEGMENT_BITS (30)
-#define CALL_SEGMENT_SIZE (1<<CALL_SEGMENT_BITS)
+#define CALL_SEGMENT_SIZE (1 << CALL_SEGMENT_BITS)
 
 static bfd_reloc_status_type
-elf_xtensa_do_reloc (howto, abfd, input_section, relocation,
-                    contents, address, is_weak_undef, error_message)
-     reloc_howto_type *howto;
-     bfd *abfd;
-     asection *input_section;
-     bfd_vma relocation;
-     bfd_byte *contents;
-     bfd_vma address;
-     bfd_boolean is_weak_undef;
-     char **error_message;
+elf_xtensa_do_reloc (reloc_howto_type *howto,
+                    bfd *abfd,
+                    asection *input_section,
+                    bfd_vma relocation,
+                    bfd_byte *contents,
+                    bfd_vma address,
+                    bfd_boolean is_weak_undef,
+                    char **error_message)
 {
+  xtensa_format fmt;
   xtensa_opcode opcode;
-  xtensa_operand operand;
-  xtensa_encode_result encode_result;
   xtensa_isa isa = xtensa_default_isa;
-  xtensa_insnbuf ibuff;
-  bfd_vma self_address;
-  int opnd;
+  static xtensa_insnbuf ibuff = NULL;
+  static xtensa_insnbuf sbuff = NULL;
+  bfd_vma self_address = 0;
+  bfd_size_type input_size;
+  int opnd, slot;
   uint32 newval;
 
+  if (!ibuff)
+    {
+      ibuff = xtensa_insnbuf_alloc (isa);
+      sbuff = xtensa_insnbuf_alloc (isa);
+    }
+
+  input_size = bfd_get_section_limit (abfd, input_section);
+
   switch (howto->type)
     {
     case R_XTENSA_NONE:
+    case R_XTENSA_DIFF8:
+    case R_XTENSA_DIFF16:
+    case R_XTENSA_DIFF32:
       return bfd_reloc_ok;
 
     case R_XTENSA_ASM_EXPAND:
@@ -1438,14 +1612,14 @@ elf_xtensa_do_reloc (howto, abfd, input_section, relocation,
          /* Check for windowed CALL across a 1GB boundary.  */
          xtensa_opcode opcode =
            get_expanded_call_opcode (contents + address,
-                                     input_section->size - 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)) 
+             if ((self_address >> CALL_SEGMENT_BITS)
+                 != (relocation >> CALL_SEGMENT_BITS)) 
                {
                  *error_message = "windowed longcall crosses 1GB boundary; "
                    "return may fail";
@@ -1456,16 +1630,17 @@ elf_xtensa_do_reloc (howto, abfd, input_section, relocation,
       return bfd_reloc_ok;
 
     case R_XTENSA_ASM_SIMPLIFY:
-      { 
+      {
         /* Convert the L32R/CALLX to CALL.  */
-       bfd_reloc_status_type retval = 
-         elf_xtensa_do_asm_simplify (contents, address, input_section->size);
+       bfd_reloc_status_type retval =
+         elf_xtensa_do_asm_simplify (contents, address, input_size,
+                                     error_message);
        if (retval != bfd_reloc_ok)
-         return retval;
+         return bfd_reloc_dangerous;
 
        /* The CALL needs to be relocated.  Continue below for that part.  */
        address += 3;
-       howto = &elf_howto_table[(unsigned) R_XTENSA_OP0 ];
+       howto = &elf_howto_table[(unsigned) R_XTENSA_SLOT0_OP ];
       }
       break;
 
@@ -1480,73 +1655,131 @@ elf_xtensa_do_reloc (howto, abfd, input_section, relocation,
       return bfd_reloc_ok;
     }
 
-  /* Read the instruction into a buffer and decode the opcode.  */
-  ibuff = xtensa_insnbuf_alloc (isa);
-  xtensa_insnbuf_from_chars (isa, ibuff, contents + address);
-  opcode = xtensa_decode_insn (isa, ibuff);
-
-  /* Determine which operand is being relocated.  */
-  if (opcode == XTENSA_UNDEFINED)
+  /* Only instruction slot-specific relocations handled below.... */
+  slot = get_relocation_slot (howto->type);
+  if (slot == XTENSA_UNDEFINED)
     {
-      *error_message = "cannot decode instruction";
+      *error_message = "unexpected relocation";
       return bfd_reloc_dangerous;
     }
 
-  if (howto->type < R_XTENSA_OP0 || howto->type > R_XTENSA_OP2)
+  /* Read the instruction into a buffer and decode the opcode.  */
+  xtensa_insnbuf_from_chars (isa, ibuff, contents + address,
+                            input_size - address);
+  fmt = xtensa_format_decode (isa, ibuff);
+  if (fmt == XTENSA_UNDEFINED)
     {
-      *error_message = "unexpected relocation";
+      *error_message = "cannot decode instruction format";
       return bfd_reloc_dangerous;
     }
 
-  opnd = howto->type - R_XTENSA_OP0;
+  xtensa_format_get_slot (isa, fmt, slot, ibuff, sbuff);
 
-  /* Calculate the PC address for this instruction.  */
-  if (!howto->pc_relative)
+  opcode = xtensa_opcode_decode (isa, fmt, slot, sbuff);
+  if (opcode == XTENSA_UNDEFINED)
     {
-      *error_message = "expected PC-relative relocation";
+      *error_message = "cannot decode instruction opcode";
       return bfd_reloc_dangerous;
     }
 
-  self_address = (input_section->output_section->vma
-                 + input_section->output_offset
-                 + address);
+  /* Check for opcode-specific "alternate" relocations.  */
+  if (is_alt_relocation (howto->type))
+    {
+      if (opcode == get_l32r_opcode ())
+       {
+         /* Handle the special-case of non-PC-relative L32R instructions.  */
+         bfd *output_bfd = input_section->output_section->owner;
+         asection *lit4_sec = bfd_get_section_by_name (output_bfd, ".lit4");
+         if (!lit4_sec)
+           {
+             *error_message = "relocation references missing .lit4 section";
+             return bfd_reloc_dangerous;
+           }
+         self_address = ((lit4_sec->vma & ~0xfff)
+                         + 0x40000 - 3); /* -3 to compensate for do_reloc */
+         newval = relocation;
+         opnd = 1;
+       }
+      else if (opcode == get_const16_opcode ())
+       {
+         /* ALT used for high 16 bits.  */
+         newval = relocation >> 16;
+         opnd = 1;
+       }
+      else
+       {
+         /* No other "alternate" relocations currently defined.  */
+         *error_message = "unexpected relocation";
+         return bfd_reloc_dangerous;
+       }
+    }
+  else /* Not an "alternate" relocation.... */
+    {
+      if (opcode == get_const16_opcode ())
+       {
+         newval = relocation & 0xffff;
+         opnd = 1;
+       }
+      else
+       {
+         /* ...normal PC-relative relocation.... */
+
+         /* Determine which operand is being relocated.  */
+         opnd = get_relocation_opnd (opcode, howto->type);
+         if (opnd == XTENSA_UNDEFINED)
+           {
+             *error_message = "unexpected relocation";
+             return bfd_reloc_dangerous;
+           }
 
-  /* Apply the relocation.  */
-  operand = xtensa_get_operand (isa, opcode, opnd);
-  newval = xtensa_operand_do_reloc (operand, relocation, self_address);
-  encode_result = xtensa_operand_encode (operand, &newval);
-  xtensa_operand_set_field (operand, ibuff, newval);
+         if (!howto->pc_relative)
+           {
+             *error_message = "expected PC-relative relocation";
+             return bfd_reloc_dangerous;
+           }
 
-  /* Write the modified instruction back out of the buffer.  */
-  xtensa_insnbuf_to_chars (isa, ibuff, contents + address);
-  free (ibuff);
+         /* Calculate the PC address for this instruction.  */
+         self_address = (input_section->output_section->vma
+                         + input_section->output_offset
+                         + address);
 
-  if (encode_result != xtensa_encode_result_ok)
+         newval = relocation;
+       }
+    }
+
+  /* Apply the relocation.  */
+  if (xtensa_operand_do_reloc (isa, opcode, opnd, &newval, self_address)
+      || xtensa_operand_encode (isa, opcode, opnd, &newval)
+      || xtensa_operand_set_field (isa, opcode, opnd, fmt, slot,
+                                  sbuff, newval))
     {
-      char *message = build_encoding_error_message (opcode, encode_result);
-      *error_message = message;
+      *error_message = build_encoding_error_message (opcode, relocation);
       return bfd_reloc_dangerous;
     }
 
-  /* Final check for call.  */
+  /* Check for calls across 1GB boundaries.  */
   if (is_direct_call_opcode (opcode)
       && is_windowed_call_opcode (opcode))
     {
-      if ((self_address >> CALL_SEGMENT_BITS) !=
-         (relocation >> CALL_SEGMENT_BITS)) 
+      if ((self_address >> CALL_SEGMENT_BITS)
+         != (relocation >> CALL_SEGMENT_BITS)) 
        {
-         *error_message = "windowed call crosses 1GB boundary; "
-           "return may fail";
+         *error_message =
+           "windowed call crosses 1GB boundary; return may fail";
          return bfd_reloc_dangerous;
        }
     }
 
+  /* Write the modified instruction back out of the buffer.  */
+  xtensa_format_set_slot (isa, fmt, slot, ibuff, sbuff);
+  xtensa_insnbuf_to_chars (isa, ibuff, contents + address,
+                          input_size - address);
   return bfd_reloc_ok;
 }
 
 
 static char *
-vsprint_msg VPARAMS ((const char *origmsg, const char *fmt, int arglen, ...))
+vsprint_msg (const char *origmsg, const char *fmt, int arglen, ...)
 {
   /* To reduce the size of the memory leak,
      we only use a single message buffer.  */
@@ -1576,55 +1809,27 @@ vsprint_msg VPARAMS ((const char *origmsg, const char *fmt, int arglen, ...))
 
 
 static char *
-build_encoding_error_message (opcode, encode_result)
-     xtensa_opcode opcode;
-     xtensa_encode_result encode_result;
+build_encoding_error_message (xtensa_opcode opcode, bfd_vma target_address)
 {
   const char *opname = xtensa_opcode_name (xtensa_default_isa, opcode);
-  const char *msg = NULL;
+  const char *msg;
 
-  switch (encode_result)
+  msg = "cannot encode";
+  if (is_direct_call_opcode (opcode))
     {
-    case xtensa_encode_result_ok:
-      msg = "unexpected valid encoding";
-      break;
-    case xtensa_encode_result_align:
-      msg = "misaligned encoding";
-      break;
-    case xtensa_encode_result_not_in_table:
-      msg = "encoding not in lookup table";
-      break;
-    case xtensa_encode_result_too_low:
-      msg = "encoding out of range: too low";
-      break;
-    case xtensa_encode_result_too_high:
-      msg = "encoding out of range: too high";
-      break;
-    case xtensa_encode_result_not_ok:
-    default:
-      msg = "could not encode";
-      break;
+      if ((target_address & 0x3) != 0)
+       msg = "misaligned call target";
+      else
+       msg = "call target out of range";
     }
-
-  if (is_direct_call_opcode (opcode)
-      && (encode_result == xtensa_encode_result_too_low
-         || encode_result == xtensa_encode_result_too_high))
-
-    msg = "direct call out of range";
-
-  else if (opcode == get_l32r_opcode ()) 
+  else if (opcode == get_l32r_opcode ())
     {
-      /* L32Rs have the strange interaction with encoding in that they
-         have an unsigned immediate field, so libisa returns "too high"
-         when the absolute value is out of range and never returns "too
-         low", but I leave the "too low" message in case anything
-         changes.  */
-      if (encode_result == xtensa_encode_result_too_low)
-       msg = "literal out of range";
-      else if (encode_result == xtensa_encode_result_too_high)
-       msg = "literal placed after use";
+      if ((target_address & 0x3) != 0)
+       msg = "misaligned literal target";
+      else
+       msg = "literal target out of range";
     }
-  
+
   return vsprint_msg (opname, ": %s", strlen (msg) + 2, msg);
 }
 
@@ -1637,20 +1842,17 @@ build_encoding_error_message (opcode, encode_result)
    stripped-down version of bfd_perform_relocation.  */
 
 static bfd_reloc_status_type
-bfd_elf_xtensa_reloc (abfd, reloc_entry, symbol, data, input_section,
-                     output_bfd, error_message)
-     bfd *abfd;
-     arelent *reloc_entry;
-     asymbol *symbol;
-     PTR data;
-     asection *input_section;
-     bfd *output_bfd;
-     char **error_message;
+bfd_elf_xtensa_reloc (bfd *abfd,
+                     arelent *reloc_entry,
+                     asymbol *symbol,
+                     void *data,
+                     asection *input_section,
+                     bfd *output_bfd,
+                     char **error_message)
 {
   bfd_vma relocation;
   bfd_reloc_status_type flag;
   bfd_size_type octets = reloc_entry->address * bfd_octets_per_byte (abfd);
-  bfd_size_type sz;
   bfd_vma output_base = 0;
   reloc_howto_type *howto = reloc_entry->howto;
   asection *reloc_target_output_section;
@@ -1667,16 +1869,14 @@ bfd_elf_xtensa_reloc (abfd, reloc_entry, symbol, data, input_section,
      relocs to begin with, but that's a long story and there's little we
      can do about it now....)  */
 
-  if (output_bfd != (bfd *) NULL
-      && (symbol->flags & BSF_SECTION_SYM) == 0)
+  if (output_bfd && (symbol->flags & BSF_SECTION_SYM) == 0)
     {
       reloc_entry->address += input_section->output_offset;
       return bfd_reloc_ok;
     }
 
   /* Is the address of the relocation really within the section?  */
-  sz = input_section->rawsize ? input_section->rawsize : input_section->size;
-  if (reloc_entry->address > sz / bfd_octets_per_byte (abfd))
+  if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
     return bfd_reloc_outofrange;
 
   /* Work out which section the relocation is targeted at and the
@@ -1712,7 +1912,7 @@ bfd_elf_xtensa_reloc (abfd, reloc_entry, symbol, data, input_section,
             to the reloc entry rather than the raw data.  Everything except
             relocations against section symbols has already been handled
             above.  */
-         
+
          BFD_ASSERT (symbol->flags & BSF_SECTION_SYM);
          reloc_entry->addend = relocation;
          reloc_entry->address += input_section->output_offset;
@@ -1748,10 +1948,9 @@ bfd_elf_xtensa_reloc (abfd, reloc_entry, symbol, data, input_section,
 /* Set up an entry in the procedure linkage table.  */
 
 static bfd_vma
-elf_xtensa_create_plt_entry (dynobj, output_bfd, reloc_index)
-      bfd *dynobj;
-      bfd *output_bfd;
-      unsigned reloc_index;
+elf_xtensa_create_plt_entry (bfd *dynobj,
+                            bfd *output_bfd,
+                            unsigned reloc_index)
 {
   asection *splt, *sgotplt;
   bfd_vma plt_base, got_base;
@@ -1798,17 +1997,14 @@ elf_xtensa_create_plt_entry (dynobj, output_bfd, reloc_index)
    both relocatable and final links.  */
 
 static bfd_boolean
-elf_xtensa_relocate_section (output_bfd, info, input_bfd,
-                            input_section, contents, relocs,
-                            local_syms, local_sections)
-     bfd *output_bfd;
-     struct bfd_link_info *info;
-     bfd *input_bfd;
-     asection *input_section;
-     bfd_byte *contents;
-     Elf_Internal_Rela *relocs;
-     Elf_Internal_Sym *local_syms;
-     asection **local_sections;
+elf_xtensa_relocate_section (bfd *output_bfd,
+                            struct bfd_link_info *info,
+                            bfd *input_bfd,
+                            asection *input_section,
+                            bfd_byte *contents,
+                            Elf_Internal_Rela *relocs,
+                            Elf_Internal_Sym *local_syms,
+                            asection **local_sections)
 {
   Elf_Internal_Shdr *symtab_hdr;
   Elf_Internal_Rela *rel;
@@ -1819,9 +2015,10 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd,
   property_table_entry *lit_table = 0;
   int ltblsize = 0;
   char *error_message = NULL;
+  bfd_size_type input_size;
 
-  if (xtensa_default_isa == NULL)
-    xtensa_isa_init ();
+  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;
@@ -1829,7 +2026,7 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd,
 
   srelgot = NULL;
   srelplt = NULL;
-  if (dynobj != NULL)
+  if (dynobj)
     {
       srelgot = bfd_get_section_by_name (dynobj, ".rela.got");;
       srelplt = bfd_get_section_by_name (dynobj, ".rela.plt");
@@ -1838,11 +2035,14 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd,
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       ltblsize = xtensa_read_table_entries (input_bfd, input_section,
-                                           &lit_table, XTENSA_LIT_SEC_NAME);
+                                           &lit_table, XTENSA_LIT_SEC_NAME,
+                                           TRUE);
       if (ltblsize < 0)
        return FALSE;
     }
 
+  input_size = bfd_get_section_limit (input_bfd, input_section);
+
   rel = relocs;
   relend = relocs + input_section->reloc_count;
   for (; rel < relend; rel++)
@@ -1875,7 +2075,7 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd,
 
       if (info->relocatable)
        {
-         /* This is a relocatable link. 
+         /* This is a relocatable link.
             1) If the reloc is against a section symbol, adjust
             according to the output section.
             2) If there is a new target for this relocation,
@@ -1886,15 +2086,26 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd,
          if (relaxing_section)
            {
              /* Check if this references a section in another input file.  */
-             do_fix_for_relocatable_link (rel, input_bfd, input_section);
+             if (!do_fix_for_relocatable_link (rel, input_bfd, input_section,
+                                               contents))
+               return FALSE;
              r_type = ELF32_R_TYPE (rel->r_info);
            }
 
-         if (r_type == R_XTENSA_ASM_SIMPLIFY) 
+         if (r_type == R_XTENSA_ASM_SIMPLIFY)
            {
+             char *error_message = NULL;
              /* Convert ASM_SIMPLIFY into the simpler relocation
                 so that they never escape a relaxing link.  */
-             contract_asm_expansion (contents, input_section->size, rel);
+             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;
+               }
              r_type = ELF32_R_TYPE (rel->r_info);
            }
 
@@ -1982,7 +2193,8 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd,
       if (relaxing_section)
        {
          /* Check if this references a section in another input file.  */
-         do_fix_for_final_link (rel, input_section, &relocation);
+         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);
@@ -1990,9 +2202,12 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd,
        }
 
       /* Sanity check the address.  */
-      if (rel->r_offset >= input_section->size
+      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_set_error (bfd_error_bad_value);
          return FALSE;
        }
@@ -2002,9 +2217,7 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd,
        {
          bfd_boolean dynamic_symbol = xtensa_elf_dynamic_symbol_p (h, info);
 
-         if (dynamic_symbol && (r_type == R_XTENSA_OP0
-                                || r_type == R_XTENSA_OP1
-                                || r_type == R_XTENSA_OP2))
+         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.  */
@@ -2076,7 +2289,7 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd,
                          /* Create the PLT entry and set the initial
                             contents of the literal entry to the address of
                             the PLT entry.  */
-                         relocation = 
+                         relocation =
                            elf_xtensa_create_plt_entry (dynobj, output_bfd,
                                                         srel->reloc_count);
                        }
@@ -2103,11 +2316,11 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd,
         not process them.  */
       if (unresolved_reloc
          && !((input_section->flags & SEC_DEBUGGING) != 0
-              && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0))
+              && h->def_dynamic))
        (*_bfd_error_handler)
-         (_("%s(%s+0x%lx): unresolvable relocation against symbol `%s'"),
-          bfd_archive_filename (input_bfd),
-          bfd_get_section_name (input_bfd, input_section),
+         (_("%B(%A+0x%lx): unresolvable relocation against symbol `%s'"),
+          input_bfd,
+          input_section,
           (long) rel->r_offset,
           h->root.root.string);
 
@@ -2117,15 +2330,15 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd,
                               relocation + rel->r_addend,
                               contents, rel->r_offset, is_weak_undef,
                               &error_message);
-      
+
       if (r != bfd_reloc_ok && !warned)
        {
          const char *name;
 
-         BFD_ASSERT (r == bfd_reloc_dangerous);
-         BFD_ASSERT (error_message != (char *) NULL);
+         BFD_ASSERT (r == bfd_reloc_dangerous || r == bfd_reloc_other);
+         BFD_ASSERT (error_message != NULL);
 
-         if (h != NULL)
+         if (h)
            name = h->root.root.string;
          else
            {
@@ -2135,8 +2348,16 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd,
                name = bfd_section_name (input_bfd, sec);
            }
          if (name)
-           error_message = vsprint_msg (error_message, ": %s",
-                                        strlen (name), 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);
+           }
+
          if (!((*info->callbacks->reloc_dangerous)
                (info, error_message, input_bfd, input_section,
                 rel->r_offset)))
@@ -2157,14 +2378,13 @@ elf_xtensa_relocate_section (output_bfd, info, input_bfd,
    the PLT and GOT entries are all set up by relocate_section.  */
 
 static bfd_boolean
-elf_xtensa_finish_dynamic_symbol (output_bfd, info, h, sym)
-     bfd *output_bfd ATTRIBUTE_UNUSED;
-     struct bfd_link_info *info ATTRIBUTE_UNUSED;
-     struct elf_link_hash_entry *h;
-     Elf_Internal_Sym *sym;
+elf_xtensa_finish_dynamic_symbol (bfd *output_bfd ATTRIBUTE_UNUSED,
+                                 struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                                 struct elf_link_hash_entry *h,
+                                 Elf_Internal_Sym *sym)
 {
-  if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0
-      && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+  if (h->needs_plt
+      && !h->def_regular)
     {
       /* Mark the symbol as undefined, rather than as defined in
         the .plt section.  Leave the value alone.  */
@@ -2189,10 +2409,9 @@ elf_xtensa_finish_dynamic_symbol (output_bfd, info, h, sym)
    on error.  */
 
 static int
-elf_xtensa_combine_prop_entries (output_bfd, sxtlit, sgotloc)
-     bfd *output_bfd;
-     asection *sxtlit;
-     asection *sgotloc;
+elf_xtensa_combine_prop_entries (bfd *output_bfd,
+                                asection *sxtlit,
+                                asection *sgotloc)
 {
   bfd_byte *contents;
   property_table_entry *table;
@@ -2208,7 +2427,7 @@ elf_xtensa_combine_prop_entries (output_bfd, sxtlit, sgotloc)
   if (sgotloc_size != section_size)
     {
       (*_bfd_error_handler)
-       ("internal inconsistency in size of .got.loc section");
+       (_("internal inconsistency in size of .got.loc section"));
       return -1;
     }
 
@@ -2279,10 +2498,7 @@ elf_xtensa_combine_prop_entries (output_bfd, sxtlit, sgotloc)
 
   /* Clear the removed bytes.  */
   if ((bfd_size_type) (num * 8) < section_size)
-    {
-      memset (&contents[num * 8], 0, section_size - num * 8);
-      sxtlit->size = num * 8;
-    }
+    memset (&contents[num * 8], 0, section_size - num * 8);
 
   if (! bfd_set_section_contents (output_bfd, sxtlit, contents, 0,
                                  section_size))
@@ -2300,9 +2516,8 @@ elf_xtensa_combine_prop_entries (output_bfd, sxtlit, sgotloc)
 /* Finish up the dynamic sections.  */
 
 static bfd_boolean
-elf_xtensa_finish_dynamic_sections (output_bfd, info)
-     bfd *output_bfd;
-     struct bfd_link_info *info;
+elf_xtensa_finish_dynamic_sections (bfd *output_bfd,
+                                   struct bfd_link_info *info)
 {
   bfd *dynobj;
   asection *sdyn, *srelplt, *sgot, *sxtlit, *sgotloc;
@@ -2323,7 +2538,7 @@ elf_xtensa_finish_dynamic_sections (output_bfd, info)
     {
       BFD_ASSERT (sgot->size == 4);
       if (sdyn == NULL)
-       bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents);
+       bfd_put_32 (output_bfd, 0, sgot->contents);
       else
        bfd_put_32 (output_bfd,
                    sdyn->output_section->vma + sdyn->output_offset,
@@ -2331,7 +2546,7 @@ elf_xtensa_finish_dynamic_sections (output_bfd, info)
     }
 
   srelplt = bfd_get_section_by_name (dynobj, ".rela.plt");
-  if (srelplt != NULL && srelplt->size != 0)
+  if (srelplt && srelplt->size != 0)
     {
       asection *sgotplt, *srelgot, *spltlittbl;
       int chunk, plt_chunks, plt_entries;
@@ -2504,9 +2719,7 @@ elf_xtensa_finish_dynamic_sections (output_bfd, info)
    object file when linking.  */
 
 static bfd_boolean
-elf_xtensa_merge_private_bfd_data (ibfd, obfd)
-     bfd *ibfd;
-     bfd *obfd;
+elf_xtensa_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
 {
   unsigned out_mach, in_mach;
   flagword out_flag, in_flag;
@@ -2525,11 +2738,11 @@ elf_xtensa_merge_private_bfd_data (ibfd, obfd)
 
   out_mach = out_flag & EF_XTENSA_MACH;
   in_mach = in_flag & EF_XTENSA_MACH;
-  if (out_mach != in_mach) 
+  if (out_mach != in_mach)
     {
       (*_bfd_error_handler)
-       ("%s: incompatible machine type. Output is 0x%x. Input is 0x%x",
-        bfd_archive_filename (ibfd), out_mach, in_mach);
+       (_("%B: 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;
     }
@@ -2538,31 +2751,27 @@ elf_xtensa_merge_private_bfd_data (ibfd, obfd)
     {
       elf_flags_init (obfd) = TRUE;
       elf_elfheader (obfd)->e_flags = in_flag;
-      
+
       if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
          && bfd_get_arch_info (obfd)->the_default)
        return bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
                                  bfd_get_mach (ibfd));
-      
+
       return TRUE;
     }
 
-  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_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)) 
-    elf_elfheader(obfd)->e_flags &= (~ 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;
 }
 
 
 static bfd_boolean
-elf_xtensa_set_private_flags (abfd, flags)
-     bfd *abfd;
-     flagword flags;
+elf_xtensa_set_private_flags (bfd *abfd, flagword flags)
 {
   BFD_ASSERT (!elf_flags_init (abfd)
              || elf_elfheader (abfd)->e_flags == flags);
@@ -2574,24 +2783,14 @@ elf_xtensa_set_private_flags (abfd, flags)
 }
 
 
-extern flagword
-elf_xtensa_get_private_bfd_flags (abfd)
-     bfd *abfd;
-{
-  return elf_elfheader (abfd)->e_flags;
-}
-
-
 static bfd_boolean
-elf_xtensa_print_private_bfd_data (abfd, farg)
-     bfd *abfd;
-     PTR farg;
+elf_xtensa_print_private_bfd_data (bfd *abfd, void *farg)
 {
   FILE *f = (FILE *) farg;
   flagword e_flags = elf_elfheader (abfd)->e_flags;
 
   fprintf (f, "\nXtensa header:\n");
-  if ((e_flags & EF_XTENSA_MACH) == E_XTENSA_MACH) 
+  if ((e_flags & EF_XTENSA_MACH) == E_XTENSA_MACH)
     fprintf (f, "\nMachine     = Base\n");
   else
     fprintf (f, "\nMachine Id  = 0x%x\n", e_flags & EF_XTENSA_MACH);
@@ -2609,8 +2808,7 @@ elf_xtensa_print_private_bfd_data (abfd, farg)
 /* Set the right machine number for an Xtensa ELF file.  */
 
 static bfd_boolean
-elf_xtensa_object_p (abfd)
-     bfd *abfd;
+elf_xtensa_object_p (bfd *abfd)
 {
   int mach;
   unsigned long arch = elf_elfheader (abfd)->e_flags & EF_XTENSA_MACH;
@@ -2634,9 +2832,8 @@ elf_xtensa_object_p (abfd)
    number.  */
 
 static void
-elf_xtensa_final_write_processing (abfd, linker)
-     bfd *abfd;
-     bfd_boolean linker ATTRIBUTE_UNUSED;
+elf_xtensa_final_write_processing (bfd *abfd,
+                                  bfd_boolean linker ATTRIBUTE_UNUSED)
 {
   int mach;
   unsigned long val;
@@ -2656,8 +2853,7 @@ elf_xtensa_final_write_processing (abfd, linker)
 
 
 static enum elf_reloc_type_class
-elf_xtensa_reloc_type_class (rela)
-     const Elf_Internal_Rela *rela;
+elf_xtensa_reloc_type_class (const Elf_Internal_Rela *rela)
 {
   switch ((int) ELF32_R_TYPE (rela->r_info))
     {
@@ -2672,11 +2868,10 @@ elf_xtensa_reloc_type_class (rela)
 
 \f
 static bfd_boolean
-elf_xtensa_discard_info_for_section (abfd, cookie, info, sec)
-     bfd *abfd;
-     struct elf_reloc_cookie *cookie;
-     struct bfd_link_info *info;
-     asection *sec;
+elf_xtensa_discard_info_for_section (bfd *abfd,
+                                    struct elf_reloc_cookie *cookie,
+                                    struct bfd_link_info *info,
+                                    asection *sec)
 {
   bfd_byte *contents;
   bfd_vma section_size;
@@ -2797,10 +2992,9 @@ elf_xtensa_discard_info_for_section (abfd, cookie, info, sec)
 
 
 static bfd_boolean
-elf_xtensa_discard_info (abfd, cookie, info)
-     bfd *abfd;
-     struct elf_reloc_cookie *cookie;
-     struct bfd_link_info *info;
+elf_xtensa_discard_info (bfd *abfd,
+                        struct elf_reloc_cookie *cookie,
+                        struct bfd_link_info *info)
 {
   asection *sec;
   bfd_boolean changed = FALSE;
@@ -2819,8 +3013,7 @@ elf_xtensa_discard_info (abfd, cookie, info)
 
 
 static bfd_boolean
-elf_xtensa_ignore_discarded_relocs (sec)
-     asection *sec;
+elf_xtensa_ignore_discarded_relocs (asection *sec)
 {
   return xtensa_is_property_section (sec);
 }
@@ -2829,9 +3022,7 @@ elf_xtensa_ignore_discarded_relocs (sec)
 /* Support for core dump NOTE sections.  */
 
 static bfd_boolean
-elf_xtensa_grok_prstatus (abfd, note)
-     bfd *abfd;
-     Elf_Internal_Note *note;
+elf_xtensa_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
 {
   int offset;
   unsigned int size;
@@ -2856,9 +3047,7 @@ elf_xtensa_grok_prstatus (abfd, note)
 
 
 static bfd_boolean
-elf_xtensa_grok_psinfo (abfd, note)
-     bfd *abfd;
-     Elf_Internal_Note *note;
+elf_xtensa_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
 {
   switch (note->descsz)
     {
@@ -2900,7 +3089,7 @@ static xtensa_opcode call8_op = XTENSA_UNDEFINED;
 static xtensa_opcode call12_op = XTENSA_UNDEFINED;
 
 static void
-init_call_opcodes ()
+init_call_opcodes (void)
 {
   if (callx0_op == XTENSA_UNDEFINED)
     {
@@ -2917,8 +3106,7 @@ init_call_opcodes ()
 
 
 static bfd_boolean
-is_indirect_call_opcode (opcode)
-     xtensa_opcode opcode;
+is_indirect_call_opcode (xtensa_opcode opcode)
 {
   init_call_opcodes ();
   return (opcode == callx0_op
@@ -2929,8 +3117,7 @@ is_indirect_call_opcode (opcode)
 
 
 static bfd_boolean
-is_direct_call_opcode (opcode)
-     xtensa_opcode opcode;
+is_direct_call_opcode (xtensa_opcode opcode)
 {
   init_call_opcodes ();
   return (opcode == call0_op
@@ -2941,8 +3128,7 @@ is_direct_call_opcode (opcode)
 
 
 static bfd_boolean
-is_windowed_call_opcode (opcode)
-     xtensa_opcode opcode;
+is_windowed_call_opcode (xtensa_opcode opcode)
 {
   init_call_opcodes ();
   return (opcode == call4_op
@@ -2954,23 +3140,37 @@ is_windowed_call_opcode (opcode)
 }
 
 
+static xtensa_opcode
+get_const16_opcode (void)
+{
+  static bfd_boolean done_lookup = FALSE;
+  static xtensa_opcode const16_opcode = XTENSA_UNDEFINED;
+  if (!done_lookup)
+    {
+      const16_opcode = xtensa_opcode_lookup (xtensa_default_isa, "const16");
+      done_lookup = TRUE;
+    }
+  return const16_opcode;
+}
+
+
 static xtensa_opcode
 get_l32r_opcode (void)
 {
   static xtensa_opcode l32r_opcode = XTENSA_UNDEFINED;
-  if (l32r_opcode == XTENSA_UNDEFINED)
+  static bfd_boolean done_lookup = FALSE;
+
+  if (!done_lookup)
     {
       l32r_opcode = xtensa_opcode_lookup (xtensa_default_isa, "l32r");
-      BFD_ASSERT (l32r_opcode != XTENSA_UNDEFINED);
+      done_lookup = TRUE;
     }
   return l32r_opcode;
 }
 
 
 static bfd_vma
-l32r_offset (addr, pc)
-     bfd_vma addr;
-     bfd_vma pc;
+l32r_offset (bfd_vma addr, bfd_vma pc)
 {
   bfd_vma offset;
 
@@ -2982,1855 +3182,5090 @@ l32r_offset (addr, pc)
 }
 
 
-/* Get the operand number for a PC-relative relocation.
-   If the relocation is not a PC-relative one, return (-1).  */
-
 static int
-get_relocation_opnd (irel)
-     Elf_Internal_Rela *irel;
-{
-  if (ELF32_R_TYPE (irel->r_info) < R_XTENSA_OP0
-      || ELF32_R_TYPE (irel->r_info) >= R_XTENSA_max)
-    return -1;
-  return ELF32_R_TYPE (irel->r_info) - R_XTENSA_OP0;
-}
-
-
-/* Get the opcode for a relocation.  */
-
-static xtensa_opcode
-get_relocation_opcode (sec, contents, irel)
-     asection *sec;
-     bfd_byte *contents;
-     Elf_Internal_Rela *irel;
+get_relocation_opnd (xtensa_opcode opcode, int r_type)
 {
-  static xtensa_insnbuf ibuff = NULL;
   xtensa_isa isa = xtensa_default_isa;
+  int last_immed, last_opnd, opi;
 
-  if (get_relocation_opnd (irel) == -1)
+  if (opcode == XTENSA_UNDEFINED)
     return XTENSA_UNDEFINED;
 
-  if (contents == NULL)
-    return XTENSA_UNDEFINED;
+  /* Find the last visible PC-relative immediate operand for the opcode.
+     If there are no PC-relative immediates, then choose the last visible
+     immediate; otherwise, fail and return XTENSA_UNDEFINED.  */
+  last_immed = XTENSA_UNDEFINED;
+  last_opnd = xtensa_opcode_num_operands (isa, opcode);
+  for (opi = last_opnd - 1; opi >= 0; opi--)
+    {
+      if (xtensa_operand_is_visible (isa, opcode, opi) == 0)
+       continue;
+      if (xtensa_operand_is_PCrelative (isa, opcode, opi) == 1)
+       {
+         last_immed = opi;
+         break;
+       }
+      if (last_immed == XTENSA_UNDEFINED
+         && xtensa_operand_is_register (isa, opcode, opi) == 0)
+       last_immed = opi;
+    }
+  if (last_immed < 0)
+    return XTENSA_UNDEFINED;
+
+  /* If the operand number was specified in an old-style relocation,
+     check for consistency with the operand computed above.  */
+  if (r_type >= R_XTENSA_OP0 && r_type <= R_XTENSA_OP2)
+    {
+      int reloc_opnd = r_type - R_XTENSA_OP0;
+      if (reloc_opnd != last_immed)
+       return XTENSA_UNDEFINED;
+    }
+
+  return last_immed;
+}
+
+
+int
+get_relocation_slot (int r_type)
+{
+  switch (r_type)
+    {
+    case R_XTENSA_OP0:
+    case R_XTENSA_OP1:
+    case R_XTENSA_OP2:
+      return 0;
+
+    default:
+      if (r_type >= R_XTENSA_SLOT0_OP && r_type <= R_XTENSA_SLOT14_OP)
+       return r_type - R_XTENSA_SLOT0_OP;
+      if (r_type >= R_XTENSA_SLOT0_ALT && r_type <= R_XTENSA_SLOT14_ALT)
+       return r_type - R_XTENSA_SLOT0_ALT;
+      break;
+    }
+
+  return XTENSA_UNDEFINED;
+}
+
+
+/* Get the opcode for a relocation.  */
+
+static xtensa_opcode
+get_relocation_opcode (bfd *abfd,
+                      asection *sec,
+                      bfd_byte *contents,
+                      Elf_Internal_Rela *irel)
+{
+  static xtensa_insnbuf ibuff = NULL;
+  static xtensa_insnbuf sbuff = NULL;
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_format fmt;
+  int slot;
+
+  if (contents == NULL)
+    return XTENSA_UNDEFINED;
 
-  if (sec->size <= irel->r_offset)
+  if (bfd_get_section_limit (abfd, sec) <= irel->r_offset)
     return XTENSA_UNDEFINED;
 
   if (ibuff == NULL)
-    ibuff = xtensa_insnbuf_alloc (isa);
-      
+    {
+      ibuff = xtensa_insnbuf_alloc (isa);
+      sbuff = xtensa_insnbuf_alloc (isa);
+    }
+
   /* Decode the instruction.  */
-  xtensa_insnbuf_from_chars (isa, ibuff, &contents[irel->r_offset]);
-  return xtensa_decode_insn (isa, ibuff);
+  xtensa_insnbuf_from_chars (isa, ibuff, &contents[irel->r_offset],
+                            sec->size - irel->r_offset);
+  fmt = xtensa_format_decode (isa, ibuff);
+  slot = get_relocation_slot (ELF32_R_TYPE (irel->r_info));
+  if (slot == XTENSA_UNDEFINED)
+    return XTENSA_UNDEFINED;
+  xtensa_format_get_slot (isa, fmt, slot, ibuff, sbuff);
+  return xtensa_opcode_decode (isa, fmt, slot, sbuff);
 }
 
 
 bfd_boolean
-is_l32r_relocation (sec, contents, irel)
-     asection *sec;
-     bfd_byte *contents;
-     Elf_Internal_Rela *irel;
+is_l32r_relocation (bfd *abfd,
+                   asection *sec,
+                   bfd_byte *contents,
+                   Elf_Internal_Rela *irel)
 {
   xtensa_opcode opcode;
-
-  if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_OP1)
+  if (!is_operand_relocation (ELF32_R_TYPE (irel->r_info)))
     return FALSE;
-  
-  opcode = get_relocation_opcode (sec, contents, irel);
+  opcode = get_relocation_opcode (abfd, sec, contents, irel);
   return (opcode == get_l32r_opcode ());
 }
 
-\f
-/* Code for transforming CALLs at link-time.  */
 
-static bfd_reloc_status_type
-elf_xtensa_do_asm_simplify (contents, address, content_length)
-     bfd_byte *contents;
-     bfd_vma address;
-     bfd_vma content_length;
+static bfd_size_type
+get_asm_simplify_size (bfd_byte *contents,
+                      bfd_size_type content_len,
+                      bfd_size_type offset)
 {
-  static xtensa_insnbuf insnbuf = NULL;
-  xtensa_opcode opcode;
-  xtensa_operand operand;
-  xtensa_opcode direct_call_opcode;
-  xtensa_isa isa = xtensa_default_isa;
-  bfd_byte *chbuf = contents + address;
-  int opn;
-
-  if (insnbuf == NULL)
-    insnbuf = xtensa_insnbuf_alloc (isa);
+  bfd_size_type insnlen, size = 0;
 
-  if (content_length < address)
-    {
-      (*_bfd_error_handler)
-       ("Attempt to convert L32R/CALLX to CALL failed");
-      return bfd_reloc_other;
-    }
+  /* Decode the size of the next two instructions.  */
+  insnlen = insn_decode_len (contents, content_len, offset);
+  if (insnlen == 0)
+    return 0;
 
-  opcode = get_expanded_call_opcode (chbuf, content_length - address);
-  direct_call_opcode = swap_callx_for_call_opcode (opcode);
-  if (direct_call_opcode == XTENSA_UNDEFINED)
-    {
-      (*_bfd_error_handler)
-       ("Attempt to convert L32R/CALLX to CALL failed");
-      return bfd_reloc_other;
-    }
+  size += insnlen;
   
-  /* Assemble a NOP ("or a1, a1, a1") into the 0 byte offset.  */
-  opcode = xtensa_opcode_lookup (isa, "or");
-  xtensa_encode_insn (isa, opcode, insnbuf);
-  for (opn = 0; opn < 3; opn++) 
-    {
-      operand = xtensa_get_operand (isa, opcode, opn);
-      xtensa_operand_set_field (operand, insnbuf, 1);
-    }
-  xtensa_insnbuf_to_chars (isa, insnbuf, chbuf);
+  insnlen = insn_decode_len (contents, content_len, offset + size);
+  if (insnlen == 0)
+    return 0;
 
-  /* Assemble a CALL ("callN 0") into the 3 byte offset.  */
-  xtensa_encode_insn (isa, direct_call_opcode, insnbuf);
-  operand = xtensa_get_operand (isa, opcode, 0);
-  xtensa_operand_set_field (operand, insnbuf, 0);
-  xtensa_insnbuf_to_chars (isa, insnbuf, chbuf + 3);
+  size += insnlen;
+  return size;
+}
 
-  return bfd_reloc_ok;
+
+bfd_boolean
+is_alt_relocation (int r_type)
+{
+  return (r_type >= R_XTENSA_SLOT0_ALT
+         && r_type <= R_XTENSA_SLOT14_ALT);
 }
 
 
-static bfd_reloc_status_type
-contract_asm_expansion (contents, content_length, irel)
-     bfd_byte *contents;
-     bfd_vma content_length;
-     Elf_Internal_Rela *irel;
+bfd_boolean
+is_operand_relocation (int r_type)
 {
-  bfd_reloc_status_type retval =
-    elf_xtensa_do_asm_simplify (contents, irel->r_offset, content_length);
+  switch (r_type)
+    {
+    case R_XTENSA_OP0:
+    case R_XTENSA_OP1:
+    case R_XTENSA_OP2:
+      return TRUE;
 
-  if (retval != bfd_reloc_ok)
-    return retval;
+    default:
+      if (r_type >= R_XTENSA_SLOT0_OP && r_type <= R_XTENSA_SLOT14_OP)
+       return TRUE;
+      if (r_type >= R_XTENSA_SLOT0_ALT && r_type <= R_XTENSA_SLOT14_ALT)
+       return TRUE;
+      break;
+    }
 
-  /* Update the irel->r_offset field so that the right immediate and
-     the right instruction are modified during the relocation.  */
-  irel->r_offset += 3;
-  irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_XTENSA_OP0);
-  return bfd_reloc_ok;
+  return FALSE;
 }
 
+      
+#define MIN_INSN_LENGTH 2
+
+/* Return 0 if it fails to decode.  */
 
-static xtensa_opcode
-swap_callx_for_call_opcode (opcode)
-     xtensa_opcode opcode;
+bfd_size_type
+insn_decode_len (bfd_byte *contents,
+                bfd_size_type content_len,
+                bfd_size_type offset)
 {
-  init_call_opcodes ();
+  int insn_len;
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_format fmt;
+  static xtensa_insnbuf ibuff = NULL;
 
-  if (opcode == callx0_op) return call0_op;
-  if (opcode == callx4_op) return call4_op;
-  if (opcode == callx8_op) return call8_op;
-  if (opcode == callx12_op) return call12_op;
+  if (offset + MIN_INSN_LENGTH > content_len)
+    return 0;
 
-  /* Return XTENSA_UNDEFINED if the opcode is not an indirect call.  */
-  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 0;
+  insn_len = xtensa_format_length (isa, fmt);
+  if (insn_len ==  XTENSA_UNDEFINED)
+    return 0;
+  return insn_len;
 }
 
 
-/* Check if "buf" is pointing to a "L32R aN; CALLX aN" sequence, and
-   if so, return the CALLX opcode.  If not, return XTENSA_UNDEFINED.  */
-
-#define L32R_TARGET_REG_OPERAND 0
-#define CALLN_SOURCE_OPERAND 0
+/* Decode the opcode for a single slot instruction.
+   Return 0 if it fails to decode or the instruction is multi-slot.  */
 
-static xtensa_opcode 
-get_expanded_call_opcode (buf, bufsize)
-     bfd_byte *buf;
-     int bufsize;
+xtensa_opcode
+insn_decode_opcode (bfd_byte *contents,
+                   bfd_size_type content_len,
+                   bfd_size_type offset,
+                   int slot)
 {
-  static xtensa_insnbuf insnbuf = NULL;
-  xtensa_opcode opcode;
-  xtensa_operand operand;
   xtensa_isa isa = xtensa_default_isa;
-  uint32 regno, call_regno;
-  
-  /* Buffer must be at least 6 bytes.  */
-  if (bufsize < 6)
+  xtensa_format fmt;
+  static xtensa_insnbuf insnbuf = NULL;
+  static xtensa_insnbuf slotbuf = NULL;
+
+  if (offset + MIN_INSN_LENGTH > content_len)
     return XTENSA_UNDEFINED;
 
   if (insnbuf == NULL)
-    insnbuf = xtensa_insnbuf_alloc (isa);
-      
-  xtensa_insnbuf_from_chars (isa, insnbuf, buf);
-  opcode = xtensa_decode_insn (isa, insnbuf);
-  
-  if (opcode != get_l32r_opcode ())
-    return XTENSA_UNDEFINED;
-  
-  operand = xtensa_get_operand (isa, opcode, L32R_TARGET_REG_OPERAND);
-  regno = xtensa_operand_decode
-    (operand, xtensa_operand_get_field (operand, insnbuf));
-  
-  /* Next instruction should be an CALLXn with operand 0 == regno.  */
-  xtensa_insnbuf_from_chars (isa, insnbuf, 
-                            buf + xtensa_insn_length (isa, opcode));
-  opcode = xtensa_decode_insn (isa, insnbuf);
-  
-  if (!is_indirect_call_opcode (opcode))
+    {
+      insnbuf = xtensa_insnbuf_alloc (isa);
+      slotbuf = xtensa_insnbuf_alloc (isa);
+    }
+
+  xtensa_insnbuf_from_chars (isa, insnbuf, &contents[offset],
+                            content_len - offset);
+  fmt = xtensa_format_decode (isa, insnbuf);
+  if (fmt == XTENSA_UNDEFINED)
     return XTENSA_UNDEFINED;
-  
-  operand = xtensa_get_operand (isa, opcode, CALLN_SOURCE_OPERAND);
-  call_regno = xtensa_operand_decode
-    (operand, xtensa_operand_get_field (operand, insnbuf));
-  if (call_regno != regno)
+
+  if (slot >= xtensa_format_num_slots (isa, fmt))
     return XTENSA_UNDEFINED;
-  
-  return opcode;
-}
 
-\f
-/* Data structures used during relaxation.  */
+  xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+  return xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+}
 
-/* r_reloc: relocation values.  */
 
-/* Through the relaxation process, we need to keep track of the values
-   that will result from evaluating relocations.  The standard ELF
-   relocation structure is not sufficient for this purpose because we're
-   operating on multiple input files at once, so we need to know which
-   input file a relocation refers to.  The r_reloc structure thus
-   records both the input file (bfd) and ELF relocation.
+/* The offset is the offset in the contents.
+   The address is the address of that offset.  */
 
-   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.  */
+static bfd_boolean
+check_branch_target_aligned (bfd_byte *contents,
+                            bfd_size_type content_length,
+                            bfd_vma offset,
+                            bfd_vma address)
+{
+  bfd_size_type insn_len = insn_decode_len (contents, content_length, offset);
+  if (insn_len == 0)
+    return FALSE;
+  return check_branch_target_aligned_address (address, insn_len);
+}
 
-typedef struct r_reloc_struct r_reloc;
 
-struct r_reloc_struct
+static bfd_boolean
+check_loop_aligned (bfd_byte *contents,
+                   bfd_size_type content_length,
+                   bfd_vma offset,
+                   bfd_vma address)
 {
-  bfd *abfd;
-  Elf_Internal_Rela rela;
-  bfd_vma target_offset;
-};
+  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;
 
-static bfd_boolean r_reloc_is_const
-  PARAMS ((const r_reloc *));
-static void r_reloc_init
-  PARAMS ((r_reloc *, bfd *, Elf_Internal_Rela *));
-static bfd_vma r_reloc_get_target_offset
-  PARAMS ((const r_reloc *));
-static asection *r_reloc_get_section
-  PARAMS ((const r_reloc *));
-static bfd_boolean r_reloc_is_defined
-  PARAMS ((const r_reloc *));
-static struct elf_link_hash_entry *r_reloc_get_hash_entry
-  PARAMS ((const r_reloc *));
+  loop_len = insn_decode_len (contents, content_length, offset);
+  BFD_ASSERT (loop_len != 0);
+  if (loop_len == 0)
+    return FALSE;
 
+  insn_len = insn_decode_len (contents, content_length, offset + loop_len);
+  BFD_ASSERT (insn_len != 0);
+  if (insn_len == 0)
+    return FALSE;
+
+  return check_branch_target_aligned_address (address + loop_len, insn_len);
+}
 
-/* The r_reloc structure is included by value in literal_value, but not
-   every literal_value has an associated relocation -- some are simple
-   constants.  In such cases, we set all the fields in the r_reloc
-   struct to zero.  The r_reloc_is_const function should be used to
-   detect this case.  */
 
 static bfd_boolean
-r_reloc_is_const (r_rel)
-     const r_reloc *r_rel;
+check_branch_target_aligned_address (bfd_vma addr, int len)
 {
-  return (r_rel->abfd == NULL);
+  if (len == 8)
+    return (addr % 8 == 0);
+  return ((addr >> 2) == ((addr + len - 1) >> 2));
 }
 
+\f
+/* Instruction widening and narrowing.  */
+
+/* When FLIX is available we need to access certain instructions only
+   when they are 16-bit or 24-bit instructions.  This table caches
+   information about such instructions by walking through all the
+   opcodes and finding the smallest single-slot format into which each
+   can be encoded.  */
+
+static xtensa_format *op_single_fmt_table = NULL;
+
 
 static void
-r_reloc_init (r_rel, abfd, irel) 
-     r_reloc *r_rel;
-     bfd *abfd;
-     Elf_Internal_Rela *irel;
+init_op_single_format_table (void)
 {
-  if (irel != NULL)
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_insnbuf ibuf;
+  xtensa_opcode opcode;
+  xtensa_format fmt;
+  int num_opcodes;
+
+  if (op_single_fmt_table)
+    return;
+
+  ibuf = xtensa_insnbuf_alloc (isa);
+  num_opcodes = xtensa_isa_num_opcodes (isa);
+
+  op_single_fmt_table = (xtensa_format *)
+    bfd_malloc (sizeof (xtensa_format) * num_opcodes);
+  for (opcode = 0; opcode < num_opcodes; opcode++)
     {
-      r_rel->rela = *irel;
-      r_rel->abfd = abfd;
-      r_rel->target_offset = r_reloc_get_target_offset (r_rel);
+      op_single_fmt_table[opcode] = XTENSA_UNDEFINED;
+      for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
+       {
+         if (xtensa_format_num_slots (isa, fmt) == 1
+             && xtensa_opcode_encode (isa, fmt, 0, ibuf, opcode) == 0)
+           {
+             xtensa_opcode old_fmt = op_single_fmt_table[opcode];
+             int fmt_length = xtensa_format_length (isa, fmt);
+             if (old_fmt == XTENSA_UNDEFINED
+                 || fmt_length < xtensa_format_length (isa, old_fmt))
+               op_single_fmt_table[opcode] = fmt;
+           }
+       }
     }
-  else
-    memset (r_rel, 0, sizeof (r_reloc));
+  xtensa_insnbuf_free (isa, ibuf);
 }
 
 
-static bfd_vma
-r_reloc_get_target_offset (r_rel)
-     const r_reloc *r_rel;
+static xtensa_format
+get_single_format (xtensa_opcode opcode)
 {
-  bfd_vma target_offset;
-  unsigned long r_symndx;
-
-  BFD_ASSERT (!r_reloc_is_const (r_rel));
-  r_symndx = ELF32_R_SYM (r_rel->rela.r_info);
-  target_offset = get_elf_r_symndx_offset (r_rel->abfd, r_symndx);
-  return (target_offset + r_rel->rela.r_addend);
+  init_op_single_format_table ();
+  return op_single_fmt_table[opcode];
 }
 
 
-static struct elf_link_hash_entry *
-r_reloc_get_hash_entry (r_rel)
-     const r_reloc *r_rel;
+/* For the set of narrowable instructions we do NOT include the
+   narrowings beqz -> beqz.n or bnez -> bnez.n because of complexities
+   involved during linker relaxation that may require these to
+   re-expand in some conditions.  Also, the narrowing "or" -> mov.n
+   requires special case code to ensure it only works when op1 == op2.  */
+
+struct string_pair
 {
-  unsigned long r_symndx = ELF32_R_SYM (r_rel->rela.r_info);
-  return get_elf_r_symndx_hash_entry (r_rel->abfd, r_symndx);
-}
+  const char *wide;
+  const char *narrow;
+};
 
+struct string_pair narrowable[] =
+{
+  { "add", "add.n" },
+  { "addi", "addi.n" },
+  { "addmi", "addi.n" },
+  { "l32i", "l32i.n" },
+  { "movi", "movi.n" },
+  { "ret", "ret.n" },
+  { "retw", "retw.n" },
+  { "s32i", "s32i.n" },
+  { "or", "mov.n" } /* special case only when op1 == op2 */
+};
 
-static asection *
-r_reloc_get_section (r_rel)
-     const r_reloc *r_rel;
+struct string_pair widenable[] =
 {
-  unsigned long r_symndx = ELF32_R_SYM (r_rel->rela.r_info);
-  return get_elf_r_symndx_section (r_rel->abfd, r_symndx);
-}
+  { "add", "add.n" },
+  { "addi", "addi.n" },
+  { "addmi", "addi.n" },
+  { "beqz", "beqz.n" },
+  { "bnez", "bnez.n" },
+  { "l32i", "l32i.n" },
+  { "movi", "movi.n" },
+  { "ret", "ret.n" },
+  { "retw", "retw.n" },
+  { "s32i", "s32i.n" },
+  { "or", "mov.n" } /* special case only when op1 == op2 */
+};
 
 
+/* 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
+   but require some special case operand checks in some cases.  */
+
 static bfd_boolean
-r_reloc_is_defined (r_rel)
-     const r_reloc *r_rel;
+narrow_instruction (bfd_byte *contents,
+                   bfd_size_type content_length,
+                   bfd_size_type offset,
+                   bfd_boolean do_it)
 {
-  asection *sec = r_reloc_get_section (r_rel);
-  if (sec == bfd_abs_section_ptr
-      || sec == bfd_com_section_ptr
-      || sec == bfd_und_section_ptr)
-    return FALSE;
-  return TRUE;
-}
+  xtensa_opcode opcode;
+  bfd_size_type insn_len, opi;
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_format fmt, o_fmt;
 
-\f
-/* source_reloc: relocations that reference literal sections.  */
+  static xtensa_insnbuf insnbuf = NULL;
+  static xtensa_insnbuf slotbuf = NULL;
+  static xtensa_insnbuf o_insnbuf = NULL;
+  static xtensa_insnbuf o_slotbuf = NULL;
 
-/* To determine whether literals can be coalesced, we need to first
-   record all the relocations that reference the literals.  The
-   source_reloc structure below is used for this purpose.  The
-   source_reloc entries are kept in a per-literal-section array, sorted
-   by offset within the literal section (i.e., target offset).
+  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);
+    }
 
-   The source_sec and r_rel.rela.r_offset fields identify the source of
-   the relocation.  The r_rel field records the relocation value, i.e.,
-   the offset of the literal being referenced.  The opnd field is needed
-   to determine the range of the immediate field to which the relocation
-   applies, so we can determine whether another literal with the same
-   value is within range.  The is_null field is true when the relocation
-   is being removed (e.g., when an L32R is being removed due to a CALLX
-   that is converted to a direct CALL).  */
+  BFD_ASSERT (offset < content_length);
 
-typedef struct source_reloc_struct source_reloc;
+  if (content_length < 2)
+    return FALSE;
 
-struct source_reloc_struct
-{
-  asection *source_sec;
-  r_reloc r_rel;
-  xtensa_operand opnd;
-  bfd_boolean is_null;
-};
+  /* 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;
 
-static void init_source_reloc
-  PARAMS ((source_reloc *, asection *, const r_reloc *, xtensa_operand));
-static source_reloc *find_source_reloc
-  PARAMS ((source_reloc *, int, asection *, Elf_Internal_Rela *));
-static int source_reloc_compare
-  PARAMS ((const PTR, const PTR));
+  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;
 
+  for (opi = 0; opi < (sizeof (narrowable)/sizeof (struct string_pair)); ++opi)
+    {
+      bfd_boolean is_or = (strcmp ("or", narrowable[opi].wide) == 0);
 
-static void
-init_source_reloc (reloc, source_sec, r_rel, opnd)
-     source_reloc *reloc;
-     asection *source_sec;
-     const r_reloc *r_rel;
-     xtensa_operand opnd;
-{
-  reloc->source_sec = source_sec;
-  reloc->r_rel = *r_rel;
-  reloc->opnd = opnd;
-  reloc->is_null = FALSE;
-}
+      if (opcode == xtensa_opcode_lookup (isa, narrowable[opi].wide))
+       {
+         uint32 value, newval;
+         int i, operand_count, o_operand_count;
+         xtensa_opcode o_opcode;
 
+         /* Address does not matter in this case.  We might need to
+            fix it to handle branches/jumps.  */
+         bfd_vma self_address = 0;
 
-/* Find the source_reloc for a particular source offset and relocation
-   type.  Note that the array is sorted by _target_ offset, so this is
-   just a linear search.  */
+         o_opcode = xtensa_opcode_lookup (isa, narrowable[opi].narrow);
+         if (o_opcode == XTENSA_UNDEFINED)
+           return FALSE;
+         o_fmt = get_single_format (o_opcode);
+         if (o_fmt == XTENSA_UNDEFINED)
+           return FALSE;
 
-static source_reloc *
-find_source_reloc (src_relocs, src_count, sec, irel)
-     source_reloc *src_relocs;
-     int src_count;
-     asection *sec;
-     Elf_Internal_Rela *irel;
-{
-  int i;
+         if (xtensa_format_length (isa, fmt) != 3
+             || xtensa_format_length (isa, o_fmt) != 2)
+           return FALSE;
 
-  for (i = 0; i < src_count; i++)
-    {
-      if (src_relocs[i].source_sec == sec
-         && src_relocs[i].r_rel.rela.r_offset == irel->r_offset
-         && (ELF32_R_TYPE (src_relocs[i].r_rel.rela.r_info)
-             == ELF32_R_TYPE (irel->r_info)))
-       return &src_relocs[i];
-    }
+         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);
 
-  return NULL;
-}
+         if (xtensa_opcode_encode (isa, o_fmt, 0, o_slotbuf, o_opcode) != 0)
+           return FALSE;
 
+         if (!is_or)
+           {
+             if (xtensa_opcode_num_operands (isa, o_opcode) != operand_count)
+               return FALSE;
+           }
+         else
+           {
+             uint32 rawval0, rawval1, rawval2;
 
-static int
-source_reloc_compare (ap, bp)
-     const PTR ap;
-     const PTR bp;
-{
-  const source_reloc *a = (const source_reloc *) ap;
-  const source_reloc *b = (const source_reloc *) bp;
+             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;
 
-  return (a->r_rel.target_offset - b->r_rel.target_offset);
-}
+             if (rawval1 != rawval2)
+               return FALSE;
+             if (rawval0 == rawval1) /* it is a nop */
+               return FALSE;
+           }
 
-\f
-/* Literal values and value hash tables.  */
+         for (i = 0; i < o_operand_count; ++i)
+           {
+             if (xtensa_operand_get_field (isa, opcode, i, fmt, 0,
+                                           slotbuf, &value)
+                 || xtensa_operand_decode (isa, opcode, i, &value))
+               return FALSE;
 
-/* Literals with the same value can be coalesced.  The literal_value
-   structure records the value of a literal: the "r_rel" field holds the
-   information from the relocation on the literal (if there is one) and
-   the "value" field holds the contents of the literal word itself.
+             /* PC-relative branches need adjustment, but
+                the PC-rel operand will always have a relocation.  */
+             newval = value;
+             if (xtensa_operand_do_reloc (isa, o_opcode, i, &newval,
+                                          self_address)
+                 || xtensa_operand_encode (isa, o_opcode, i, &newval)
+                 || xtensa_operand_set_field (isa, o_opcode, i, o_fmt, 0,
+                                              o_slotbuf, newval))
+               return FALSE;
+           }
 
-   The value_map structure records a literal value along with the
-   location of a literal holding that value.  The value_map hash table
-   is indexed by the literal value, so that we can quickly check if a
-   particular literal value has been seen before and is thus a candidate
-   for coalescing.  */
+         if (xtensa_format_set_slot (isa, o_fmt, 0,
+                                     o_insnbuf, o_slotbuf) != 0)
+           return FALSE;
 
-typedef struct literal_value_struct literal_value;
-typedef struct value_map_struct value_map;
-typedef struct value_map_hash_table_struct value_map_hash_table;
+         if (do_it)
+           xtensa_insnbuf_to_chars (isa, o_insnbuf, contents + offset,
+                                    content_length - offset);
+         return TRUE;
+       }
+    }
+  return FALSE;
+}
 
-struct literal_value_struct
-{
-  r_reloc r_rel; 
-  unsigned long value;
-};
 
-struct value_map_struct
-{
-  literal_value val;                   /* The literal value.  */
-  r_reloc loc;                         /* Location of the literal.  */
-  value_map *next;
-};
+/* 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.  */
 
-struct value_map_hash_table_struct
+static bfd_boolean
+widen_instruction (bfd_byte *contents,
+                  bfd_size_type content_length,
+                  bfd_size_type offset,
+                  bfd_boolean do_it)
 {
-  unsigned bucket_count;
-  value_map **buckets;
-  unsigned count;
-};
-
+  xtensa_opcode opcode;
+  bfd_size_type insn_len, opi;
+  xtensa_isa isa = xtensa_default_isa;
+  xtensa_format fmt, o_fmt;
 
-static bfd_boolean is_same_value
-  PARAMS ((const literal_value *, const literal_value *, bfd_boolean));
-static value_map_hash_table *value_map_hash_table_init
-  PARAMS ((void));
-static unsigned hash_literal_value
-  PARAMS ((const literal_value *));
-static unsigned hash_bfd_vma
-  PARAMS ((bfd_vma));
-static value_map *get_cached_value
-  PARAMS ((value_map_hash_table *, const literal_value *, bfd_boolean));
-static value_map *add_value_map
-  PARAMS ((value_map_hash_table *, const literal_value *, const r_reloc *,
-          bfd_boolean));
+  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);
+    }
 
-static bfd_boolean
-is_same_value (src1, src2, final_static_link)
-     const literal_value *src1;
-     const literal_value *src2;
-     bfd_boolean final_static_link;
-{
-  struct elf_link_hash_entry *h1, *h2;
+  BFD_ASSERT (offset < content_length);
 
-  if (r_reloc_is_const (&src1->r_rel) != r_reloc_is_const (&src2->r_rel)) 
+  if (content_length < 2)
     return FALSE;
 
-  if (r_reloc_is_const (&src1->r_rel))
-    return (src1->value == src2->value);
-
-  if (ELF32_R_TYPE (src1->r_rel.rela.r_info)
-      != ELF32_R_TYPE (src2->r_rel.rela.r_info))
+  /* 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 (r_reloc_get_target_offset (&src1->r_rel)
-      != r_reloc_get_target_offset (&src2->r_rel))
+  if (xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf) != 0)
     return FALSE;
 
-  if (src1->value != src2->value)
+  opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
+  if (opcode == XTENSA_UNDEFINED)
     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);
-  h2 = r_reloc_get_hash_entry (&src2->r_rel);
-  if (r_reloc_is_defined (&src1->r_rel)
-      && (final_static_link
-         || ((!h1 || h1->root.type != bfd_link_hash_defweak)
-             && (!h2 || h2->root.type != bfd_link_hash_defweak))))
-    {
-      if (r_reloc_get_section (&src1->r_rel)
-         != r_reloc_get_section (&src2->r_rel))
-       return FALSE;
-    }
-  else
+  insn_len = xtensa_format_length (isa, fmt);
+  if (insn_len > content_length)
+    return FALSE;
+
+  for (opi = 0; opi < (sizeof (widenable)/sizeof (struct string_pair)); ++opi)
     {
-      /* Require that the hash entries (i.e., symbols) be identical.  */
-      if (h1 != h2 || h1 == 0)
-       return FALSE;
-    }
+      bfd_boolean is_or = (strcmp ("or", widenable[opi].wide) == 0);
+      bfd_boolean is_branch = (strcmp ("beqz", widenable[opi].wide) == 0
+                              || strcmp ("bnez", widenable[opi].wide) == 0);
 
-  return TRUE;
-}
+      if (opcode == xtensa_opcode_lookup (isa, widenable[opi].narrow))
+       {
+         uint32 value, newval;
+         int i, operand_count, o_operand_count, check_operand_count;
+         xtensa_opcode o_opcode;
 
+         /* Address does not matter in this case.  We might need to fix it
+            to handle branches/jumps.  */
+         bfd_vma self_address = 0;
 
-/* Must be power of 2.  */
-#define INITIAL_HASH_RELOC_BUCKET_COUNT 1024
+         o_opcode = xtensa_opcode_lookup (isa, widenable[opi].wide);
+         if (o_opcode == XTENSA_UNDEFINED)
+           return FALSE;
+         o_fmt = get_single_format (o_opcode);
+         if (o_fmt == XTENSA_UNDEFINED)
+           return FALSE;
 
-static value_map_hash_table *
-value_map_hash_table_init ()
-{
-  value_map_hash_table *values;
+         if (xtensa_format_length (isa, fmt) != 2
+             || xtensa_format_length (isa, o_fmt) != 3)
+           return FALSE;
 
-  values = (value_map_hash_table *)
-    bfd_malloc (sizeof (value_map_hash_table));
+         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);
+         check_operand_count = o_operand_count;
 
-  values->bucket_count = INITIAL_HASH_RELOC_BUCKET_COUNT;
-  values->count = 0;
-  values->buckets = (value_map **)
-    bfd_zmalloc (sizeof (value_map *) * values->bucket_count);
+         if (xtensa_opcode_encode (isa, o_fmt, 0, o_slotbuf, o_opcode) != 0)
+           return FALSE;
 
-  return values;
-}
+         if (!is_or)
+           {
+             if (xtensa_opcode_num_operands (isa, o_opcode) != operand_count)
+               return FALSE;
+           }
+         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 (is_branch)
+           check_operand_count--;
 
+         for (i = 0; i < check_operand_count; ++i)
+           {
+             int new_i = i;
+             if (is_or && i == o_operand_count - 1)
+               new_i = i - 1;
+             if (xtensa_operand_get_field (isa, opcode, new_i, fmt, 0,
+                                           slotbuf, &value)
+                 || xtensa_operand_decode (isa, opcode, new_i, &value))
+               return FALSE;
 
-static unsigned
-hash_bfd_vma (val) 
-     bfd_vma val;
-{
-  return (val >> 2) + (val >> 10);
+             /* PC-relative branches need adjustment, but
+                the PC-rel operand will always have a relocation.  */
+             newval = value;
+             if (xtensa_operand_do_reloc (isa, o_opcode, i, &newval,
+                                          self_address)
+                 || xtensa_operand_encode (isa, o_opcode, i, &newval)
+                 || xtensa_operand_set_field (isa, o_opcode, i, o_fmt, 0,
+                                              o_slotbuf, newval))
+               return FALSE;
+           }
+
+         if (xtensa_format_set_slot (isa, o_fmt, 0, o_insnbuf, o_slotbuf))
+           return FALSE;
+
+         if (do_it)
+           xtensa_insnbuf_to_chars (isa, o_insnbuf, contents + offset,
+                                    content_length - offset);
+         return TRUE;
+       }
+    }
+  return FALSE;
 }
 
+\f
+/* Code for transforming CALLs at link-time.  */
 
-static unsigned
-hash_literal_value (src)
-     const literal_value *src;
+static bfd_reloc_status_type
+elf_xtensa_do_asm_simplify (bfd_byte *contents,
+                           bfd_vma address,
+                           bfd_vma content_length,
+                           char **error_message)
 {
-  unsigned hash_val;
+  static xtensa_insnbuf insnbuf = NULL;
+  static xtensa_insnbuf slotbuf = NULL;
+  xtensa_format core_format = XTENSA_UNDEFINED;
+  xtensa_opcode opcode;
+  xtensa_opcode direct_call_opcode;
+  xtensa_isa isa = xtensa_default_isa;
+  bfd_byte *chbuf = contents + address;
+  int opn;
+
+  if (insnbuf == NULL)
+    {
+      insnbuf = xtensa_insnbuf_alloc (isa);
+      slotbuf = xtensa_insnbuf_alloc (isa);
+    }
 
-  if (r_reloc_is_const (&src->r_rel))
-    return hash_bfd_vma (src->value);
+  if (content_length < address)
+    {
+      *error_message = _("Attempt to convert L32R/CALLX to CALL failed");
+      return bfd_reloc_other;
+    }
 
-  hash_val = (hash_bfd_vma (r_reloc_get_target_offset (&src->r_rel))
-             + hash_bfd_vma (src->value));
+  opcode = get_expanded_call_opcode (chbuf, content_length - address, 0);
+  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");
+      return bfd_reloc_other;
+    }
   
-  /* Now check for the same section and the same elf_hash.  */
-  if (r_reloc_is_defined (&src->r_rel))
-    hash_val += hash_bfd_vma ((bfd_vma) (unsigned) r_reloc_get_section (&src->r_rel));
-  else
-    hash_val += hash_bfd_vma ((bfd_vma) (unsigned) r_reloc_get_hash_entry (&src->r_rel));
+  /* 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++) 
+    {
+      uint32 regno = 1;
+      xtensa_operand_encode (isa, opcode, opn, &regno);
+      xtensa_operand_set_field (isa, opcode, opn, core_format, 0,
+                               slotbuf, regno);
+    }
+  xtensa_format_encode (isa, core_format, insnbuf);
+  xtensa_format_set_slot (isa, core_format, 0, insnbuf, slotbuf);
+  xtensa_insnbuf_to_chars (isa, insnbuf, chbuf, content_length - address);
 
-  return hash_val;
-}
+  /* Assemble a CALL ("callN 0") into the 3 byte offset.  */
+  xtensa_opcode_encode (isa, core_format, 0, slotbuf, direct_call_opcode);
+  xtensa_operand_set_field (isa, opcode, 0, core_format, 0, slotbuf, 0);
 
+  xtensa_format_encode (isa, core_format, insnbuf);
+  xtensa_format_set_slot (isa, core_format, 0, insnbuf, slotbuf);
+  xtensa_insnbuf_to_chars (isa, insnbuf, chbuf + 3,
+                          content_length - address - 3);
 
-/* Check if the specified literal_value has been seen before.  */
+  return bfd_reloc_ok;
+}
 
-static value_map *
-get_cached_value (map, val, final_static_link)
-     value_map_hash_table *map;
-     const literal_value *val;
-     bfd_boolean final_static_link;
+
+static bfd_reloc_status_type
+contract_asm_expansion (bfd_byte *contents,
+                       bfd_vma content_length,
+                       Elf_Internal_Rela *irel,
+                       char **error_message)
 {
-  value_map *map_e;
-  value_map *bucket;
-  unsigned idx;
+  bfd_reloc_status_type retval =
+    elf_xtensa_do_asm_simplify (contents, irel->r_offset, content_length,
+                               error_message);
 
-  idx = hash_literal_value (val);
-  idx = idx & (map->bucket_count - 1);
-  bucket = map->buckets[idx];
-  for (map_e = bucket; map_e; map_e = map_e->next)
-    {
-      if (is_same_value (&map_e->val, val, final_static_link))
-       return map_e;
-    }
-  return NULL;
-}
+  if (retval != bfd_reloc_ok)
+    return bfd_reloc_dangerous;
 
+  /* Update the irel->r_offset field so that the right immediate and
+     the right instruction are modified during the relocation.  */
+  irel->r_offset += 3;
+  irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_XTENSA_SLOT0_OP);
+  return bfd_reloc_ok;
+}
 
-/* Record a new literal value.  It is illegal to call this if VALUE
-   already has an entry here.  */
 
-static value_map *
-add_value_map (map, val, loc, final_static_link)
-     value_map_hash_table *map;
-     const literal_value *val;
-     const r_reloc *loc;
-     bfd_boolean final_static_link;
+static xtensa_opcode
+swap_callx_for_call_opcode (xtensa_opcode opcode)
 {
-  value_map **bucket_p;
-  unsigned idx;
-
-  value_map *val_e = (value_map *) bfd_zmalloc (sizeof (value_map));
-
-  BFD_ASSERT (get_cached_value (map, val, final_static_link) == NULL);
-  val_e->val = *val;
-  val_e->loc = *loc;
+  init_call_opcodes ();
 
-  idx = hash_literal_value (val);
-  idx = idx & (map->bucket_count - 1);
-  bucket_p = &map->buckets[idx];
+  if (opcode == callx0_op) return call0_op;
+  if (opcode == callx4_op) return call4_op;
+  if (opcode == callx8_op) return call8_op;
+  if (opcode == callx12_op) return call12_op;
 
-  val_e->next = *bucket_p;
-  *bucket_p = val_e;
-  map->count++;
-  /* FIXME: consider resizing the hash table if we get too many entries */
-  
-  return val_e;
+  /* Return XTENSA_UNDEFINED if the opcode is not an indirect call.  */
+  return XTENSA_UNDEFINED;
 }
 
-\f
-/* Lists of literals being coalesced or removed.  */
 
-/* In the usual case, the literal identified by "from" is being
-   coalesced with another literal identified by "to".  If the literal is
-   unused and is being removed altogether, "to.abfd" will be NULL.
-   The removed_literal entries are kept on a per-section list, sorted
-   by the "from" offset field.  */
+/* Check if "buf" is pointing to a "L32R aN; CALLX aN" or "CONST16 aN;
+   CONST16 aN; CALLX aN" sequence, and if so, return the CALLX opcode.
+   If not, return XTENSA_UNDEFINED.  */
 
-typedef struct removed_literal_struct removed_literal;
-typedef struct removed_literal_list_struct removed_literal_list;
+#define L32R_TARGET_REG_OPERAND 0
+#define CONST16_TARGET_REG_OPERAND 0
+#define CALLN_SOURCE_OPERAND 0
 
-struct removed_literal_struct
-{
-  r_reloc from;
-  r_reloc to;
-  removed_literal *next;
-};
-
-struct removed_literal_list_struct
+static xtensa_opcode 
+get_expanded_call_opcode (bfd_byte *buf, int bufsize, bfd_boolean *p_uses_l32r)
 {
-  removed_literal *head;
-  removed_literal *tail;
-};
-
-
-static void add_removed_literal
-  PARAMS ((removed_literal_list *, const r_reloc *, const r_reloc *));
-static removed_literal *find_removed_literal
-  PARAMS ((removed_literal_list *, bfd_vma));
-static bfd_vma offset_with_removed_literals
-  PARAMS ((removed_literal_list *, bfd_vma));
-
+  static xtensa_insnbuf insnbuf = NULL;
+  static xtensa_insnbuf slotbuf = NULL;
+  xtensa_format fmt;
+  xtensa_opcode opcode;
+  xtensa_isa isa = xtensa_default_isa;
+  uint32 regno, const16_regno, call_regno;
+  int offset = 0;
 
-/* Record that the literal at "from" is being removed.  If "to" is not
-   NULL, the "from" literal is being coalesced with the "to" literal.  */
+  if (insnbuf == NULL)
+    {
+      insnbuf = xtensa_insnbuf_alloc (isa);
+      slotbuf = xtensa_insnbuf_alloc (isa);
+    }
 
-static void
-add_removed_literal (removed_list, from, to)
-     removed_literal_list *removed_list;
-     const r_reloc *from;
-     const r_reloc *to;
-{
-  removed_literal *r, *new_r, *next_r;
+  xtensa_insnbuf_from_chars (isa, insnbuf, buf, bufsize);
+  fmt = xtensa_format_decode (isa, insnbuf);
+  if (fmt == XTENSA_UNDEFINED
+      || xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf))
+    return XTENSA_UNDEFINED;
 
-  new_r = (removed_literal *) bfd_zmalloc (sizeof (removed_literal));
+  opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
+  if (opcode == XTENSA_UNDEFINED)
+    return XTENSA_UNDEFINED;
 
-  new_r->from = *from;
-  if (to)
-    new_r->to = *to;
-  else
-    new_r->to.abfd = NULL;
-  new_r->next = NULL;
-  
-  r = removed_list->head;
-  if (r == NULL) 
+  if (opcode == get_l32r_opcode ())
     {
-      removed_list->head = new_r;
-      removed_list->tail = new_r;
+      if (p_uses_l32r)
+       *p_uses_l32r = TRUE;
+      if (xtensa_operand_get_field (isa, opcode, L32R_TARGET_REG_OPERAND,
+                                   fmt, 0, slotbuf, &regno)
+         || xtensa_operand_decode (isa, opcode, L32R_TARGET_REG_OPERAND,
+                                   &regno))
+       return XTENSA_UNDEFINED;
     }
-  /* Special check for common case of append.  */
-  else if (removed_list->tail->from.target_offset < from->target_offset)
+  else if (opcode == get_const16_opcode ())
     {
-      removed_list->tail->next = new_r;
-      removed_list->tail = new_r;
+      if (p_uses_l32r)
+       *p_uses_l32r = FALSE;
+      if (xtensa_operand_get_field (isa, opcode, CONST16_TARGET_REG_OPERAND,
+                                   fmt, 0, slotbuf, &regno)
+         || xtensa_operand_decode (isa, opcode, CONST16_TARGET_REG_OPERAND,
+                                   &regno))
+       return XTENSA_UNDEFINED;
+
+      /* Check that the next instruction is also CONST16.  */
+      offset += xtensa_format_length (isa, fmt);
+      xtensa_insnbuf_from_chars (isa, insnbuf, buf + offset, bufsize - offset);
+      fmt = xtensa_format_decode (isa, insnbuf);
+      if (fmt == XTENSA_UNDEFINED
+         || xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf))
+       return XTENSA_UNDEFINED;
+      opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
+      if (opcode != get_const16_opcode ())
+       return XTENSA_UNDEFINED;
+
+      if (xtensa_operand_get_field (isa, opcode, CONST16_TARGET_REG_OPERAND,
+                                   fmt, 0, slotbuf, &const16_regno)
+         || xtensa_operand_decode (isa, opcode, CONST16_TARGET_REG_OPERAND,
+                                   &const16_regno)
+         || const16_regno != regno)
+       return XTENSA_UNDEFINED;
     }
   else
-    {
-      while (r->from.target_offset < from->target_offset
-            && r->next != NULL) 
-       {
-         r = r->next;
-       }
-      next_r = r->next;
-      r->next = new_r;
-      new_r->next = next_r;
-      if (next_r == NULL)
-       removed_list->tail = new_r;
-    }
-}
-
+    return XTENSA_UNDEFINED;
 
-/* Check if the list of removed literals contains an entry for the
-   given address.  Return the entry if found.  */
+  /* Next instruction should be an CALLXn with operand 0 == regno.  */
+  offset += xtensa_format_length (isa, fmt);
+  xtensa_insnbuf_from_chars (isa, insnbuf, buf + offset, bufsize - offset);
+  fmt = xtensa_format_decode (isa, insnbuf);
+  if (fmt == XTENSA_UNDEFINED
+      || xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf))
+    return XTENSA_UNDEFINED;
+  opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
+  if (opcode == XTENSA_UNDEFINED 
+      || !is_indirect_call_opcode (opcode))
+    return XTENSA_UNDEFINED;
 
-static removed_literal *
-find_removed_literal (removed_list, addr)
-     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;
-}
+  if (xtensa_operand_get_field (isa, opcode, CALLN_SOURCE_OPERAND,
+                               fmt, 0, slotbuf, &call_regno)
+      || xtensa_operand_decode (isa, opcode, CALLN_SOURCE_OPERAND,
+                               &call_regno))
+    return XTENSA_UNDEFINED;
 
+  if (call_regno != regno)
+    return XTENSA_UNDEFINED;
 
-/* Adjust an offset in a section to compensate for literals that are
-   being removed.  Search the list of removed literals and subtract
-   4 bytes for every removed literal prior to the given address.  */
+  return opcode;
+}
 
-static bfd_vma 
-offset_with_removed_literals (removed_list, addr)
-     removed_literal_list *removed_list;
-     bfd_vma addr;
-{
-  removed_literal *r = removed_list->head;
-  unsigned num_bytes = 0;
+\f
+/* Data structures used during relaxation.  */
 
-  if (r == NULL)
-    return addr;
+/* r_reloc: relocation values.  */
 
-  while (r && r->from.target_offset <= addr)
-    {
-      num_bytes += 4;
-      r = r->next;
-    }
-  if (num_bytes > addr)
-    return 0;
-  return (addr - num_bytes);
-}
+/* Through the relaxation process, we need to keep track of the values
+   that will result from evaluating relocations.  The standard ELF
+   relocation structure is not sufficient for this purpose because we're
+   operating on multiple input files at once, so we need to know which
+   input file a relocation refers to.  The r_reloc structure thus
+   records both the input file (bfd) and ELF relocation.
 
-\f
-/* Coalescing literals may require a relocation to refer to a section in
-   a different input file, but the standard relocation information
-   cannot express that.  Instead, the reloc_bfd_fix structures are used
-   to "fix" the relocations that refer to sections in other input files.
-   These structures are kept on per-section lists.  The "src_type" field
-   records the relocation type in case there are multiple relocations on
-   the same location.  FIXME: This is ugly; an alternative might be to
-   add new symbols with the "owner" field to some other input file.  */
+   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.  */
 
-typedef struct reloc_bfd_fix_struct reloc_bfd_fix;
+typedef struct r_reloc_struct r_reloc;
 
-struct reloc_bfd_fix_struct
+struct r_reloc_struct
 {
-  asection *src_sec;
-  bfd_vma src_offset;
-  unsigned src_type;                   /* Relocation type.  */
-  
-  bfd *target_abfd;
-  asection *target_sec;
+  bfd *abfd;
+  Elf_Internal_Rela rela;
   bfd_vma target_offset;
-  
-  reloc_bfd_fix *next;
+  bfd_vma virtual_offset;
 };
 
 
-static reloc_bfd_fix *reloc_bfd_fix_init
-  PARAMS ((asection *, bfd_vma, unsigned, bfd *, asection *, bfd_vma));
-static reloc_bfd_fix *get_bfd_fix
-  PARAMS ((reloc_bfd_fix *, asection *, bfd_vma, unsigned));
-
+/* The r_reloc structure is included by value in literal_value, but not
+   every literal_value has an associated relocation -- some are simple
+   constants.  In such cases, we set all the fields in the r_reloc
+   struct to zero.  The r_reloc_is_const function should be used to
+   detect this case.  */
 
-static reloc_bfd_fix *
-reloc_bfd_fix_init (src_sec, src_offset, src_type,
-                   target_abfd, target_sec, target_offset)
-     asection *src_sec;
-     bfd_vma src_offset;
-     unsigned src_type;
-     bfd *target_abfd;
-     asection *target_sec;
-     bfd_vma target_offset;
+static bfd_boolean
+r_reloc_is_const (const r_reloc *r_rel)
 {
-  reloc_bfd_fix *fix;
-
-  fix = (reloc_bfd_fix *) bfd_malloc (sizeof (reloc_bfd_fix));
-  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;
-
-  return fix;
+  return (r_rel->abfd == NULL);
 }
 
 
-static reloc_bfd_fix *
-get_bfd_fix (fix_list, sec, offset, type)
-     reloc_bfd_fix *fix_list;
-     asection *sec;
-     bfd_vma offset;
-     unsigned type;
+static bfd_vma
+r_reloc_get_target_offset (const r_reloc *r_rel)
 {
-  reloc_bfd_fix *r;
+  bfd_vma target_offset;
+  unsigned long r_symndx;
 
-  for (r = fix_list; r != NULL; r = r->next) 
-    {
-      if (r->src_sec == sec
-         && r->src_offset == offset
-         && r->src_type == type)
-       return r;
-    }
-  return NULL;
+  BFD_ASSERT (!r_reloc_is_const (r_rel));
+  r_symndx = ELF32_R_SYM (r_rel->rela.r_info);
+  target_offset = get_elf_r_symndx_offset (r_rel->abfd, r_symndx);
+  return (target_offset + r_rel->rela.r_addend);
 }
 
-\f
-/* Per-section data for relaxation.  */
 
-struct xtensa_relax_info_struct
+static struct elf_link_hash_entry *
+r_reloc_get_hash_entry (const r_reloc *r_rel)
 {
-  bfd_boolean is_relaxable_literal_section;
-  int visited;                         /* Number of times visited.  */
-
-  source_reloc *src_relocs;            /* Array[src_count].  */
-  int src_count;
-  int src_next;                                /* Next src_relocs entry to assign.  */
-
-  removed_literal_list removed_list;
+  unsigned long r_symndx = ELF32_R_SYM (r_rel->rela.r_info);
+  return get_elf_r_symndx_hash_entry (r_rel->abfd, r_symndx);
+}
 
-  reloc_bfd_fix *fix_list;
-};
 
-struct elf_xtensa_section_data
+static asection *
+r_reloc_get_section (const r_reloc *r_rel)
 {
-  struct bfd_elf_section_data elf;
-  xtensa_relax_info relax_info;
-};
-
-static void init_xtensa_relax_info
-  PARAMS ((asection *));
-static xtensa_relax_info *get_xtensa_relax_info
-  PARAMS ((asection *));
-static void add_fix
-  PARAMS ((asection *, reloc_bfd_fix *));
+  unsigned long r_symndx = ELF32_R_SYM (r_rel->rela.r_info);
+  return get_elf_r_symndx_section (r_rel->abfd, r_symndx);
+}
 
 
 static bfd_boolean
-elf_xtensa_new_section_hook (abfd, sec)
-     bfd *abfd;
-     asection *sec;
+r_reloc_is_defined (const r_reloc *r_rel)
 {
-  struct elf_xtensa_section_data *sdata;
-  bfd_size_type amt = sizeof (*sdata);
-
-  sdata = (struct elf_xtensa_section_data *) bfd_zalloc (abfd, amt);
-  if (sdata == NULL)
+  asection *sec;
+  if (r_rel == NULL)
     return FALSE;
-  sec->used_by_bfd = (PTR) sdata;
 
-  return _bfd_elf_new_section_hook (abfd, sec);
+  sec = r_reloc_get_section (r_rel);
+  if (sec == bfd_abs_section_ptr
+      || sec == bfd_com_section_ptr
+      || sec == bfd_und_section_ptr)
+    return FALSE;
+  return TRUE;
 }
 
 
 static void
-init_xtensa_relax_info (sec)
-     asection *sec;
+r_reloc_init (r_reloc *r_rel,
+             bfd *abfd,
+             Elf_Internal_Rela *irel,
+             bfd_byte *contents,
+             bfd_size_type content_length)
 {
-  xtensa_relax_info *relax_info = get_xtensa_relax_info (sec);
-
-  relax_info->is_relaxable_literal_section = FALSE;
-  relax_info->visited = 0;
-
-  relax_info->src_relocs = NULL;
-  relax_info->src_count = 0;
-  relax_info->src_next = 0;
+  int r_type;
+  reloc_howto_type *howto;
 
-  relax_info->removed_list.head = NULL;
-  relax_info->removed_list.tail = NULL;
+  if (irel)
+    {
+      r_rel->rela = *irel;
+      r_rel->abfd = abfd;
+      r_rel->target_offset = r_reloc_get_target_offset (r_rel);
+      r_rel->virtual_offset = 0;
+      r_type = ELF32_R_TYPE (r_rel->rela.r_info);
+      howto = &elf_howto_table[r_type];
+      if (howto->partial_inplace)
+       {
+         bfd_vma inplace_val;
+         BFD_ASSERT (r_rel->rela.r_offset < content_length);
 
-  relax_info->fix_list = NULL;
+         inplace_val = bfd_get_32 (abfd, &contents[r_rel->rela.r_offset]);
+         r_rel->target_offset += inplace_val;
+       }
+    }
+  else
+    memset (r_rel, 0, sizeof (r_reloc));
 }
 
 
-static xtensa_relax_info *
-get_xtensa_relax_info (sec)
-     asection *sec;
-{
-  struct elf_xtensa_section_data *section_data;
-
-  /* No info available if no section or if it is an output section.  */
-  if (!sec || sec == sec->output_section)
-    return NULL;
-
-  section_data = (struct elf_xtensa_section_data *) elf_section_data (sec);
-  return &section_data->relax_info;
-}
-
+#if DEBUG
 
 static void
-add_fix (src_sec, fix)
-     asection *src_sec;
-     reloc_bfd_fix *fix;
+print_r_reloc (FILE *fp, const r_reloc *r_rel)
 {
-  xtensa_relax_info *relax_info;
+  if (r_reloc_is_defined (r_rel))
+    {
+      asection *sec = r_reloc_get_section (r_rel);
+      fprintf (fp, " %s(%s + ", sec->owner->filename, sec->name);
+    }
+  else if (r_reloc_get_hash_entry (r_rel))
+    fprintf (fp, " %s + ", r_reloc_get_hash_entry (r_rel)->root.root.string);
+  else
+    fprintf (fp, " ?? + ");
 
-  relax_info = get_xtensa_relax_info (src_sec);
-  fix->next = relax_info->fix_list;
-  relax_info->fix_list = fix;
+  fprintf_vma (fp, r_rel->target_offset);
+  if (r_rel->virtual_offset)
+    {
+      fprintf (fp, " + ");
+      fprintf_vma (fp, r_rel->virtual_offset);
+    }
+    
+  fprintf (fp, ")");
 }
 
-\f
-/* Access to internal relocations, section contents and symbols.  */
-
-/* During relaxation, we need to modify relocations, section contents,
-   and symbol definitions, and we need to keep the original values from
-   being reloaded from the input files, i.e., we need to "pin" the
-   modified values in memory.  We also want to continue to observe the
-   setting of the "keep-memory" flag.  The following functions wrap the
-   standard BFD functions to take care of this for us.  */
+#endif /* DEBUG */
 
-static Elf_Internal_Rela *
-retrieve_internal_relocs (abfd, sec, keep_memory)
-     bfd *abfd;
-     asection *sec;
-     bfd_boolean keep_memory;
-{
-  Elf_Internal_Rela *internal_relocs;
+\f
+/* source_reloc: relocations that reference literals.  */
 
-  if ((sec->flags & SEC_LINKER_CREATED) != 0)
-    return NULL;
+/* To determine whether literals can be coalesced, we need to first
+   record all the relocations that reference the literals.  The
+   source_reloc structure below is used for this purpose.  The
+   source_reloc entries are kept in a per-literal-section array, sorted
+   by offset within the literal section (i.e., target offset).
 
-  internal_relocs = elf_section_data (sec)->relocs;
-  if (internal_relocs == NULL)
-    internal_relocs = (_bfd_elf_link_read_relocs
-                      (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL,
-                       keep_memory));
+   The source_sec and r_rel.rela.r_offset fields identify the source of
+   the relocation.  The r_rel field records the relocation value, i.e.,
+   the offset of the literal being referenced.  The opnd field is needed
+   to determine the range of the immediate field to which the relocation
+   applies, so we can determine whether another literal with the same
+   value is within range.  The is_null field is true when the relocation
+   is being removed (e.g., when an L32R is being removed due to a CALLX
+   that is converted to a direct CALL).  */
+
+typedef struct source_reloc_struct source_reloc;
+
+struct source_reloc_struct
+{
+  asection *source_sec;
+  r_reloc r_rel;
+  xtensa_opcode opcode;
+  int opnd;
+  bfd_boolean is_null;
+  bfd_boolean is_abs_literal;
+};
+
+
+static void
+init_source_reloc (source_reloc *reloc,
+                  asection *source_sec,
+                  const r_reloc *r_rel,
+                  xtensa_opcode opcode,
+                  int opnd,
+                  bfd_boolean is_abs_literal)
+{
+  reloc->source_sec = source_sec;
+  reloc->r_rel = *r_rel;
+  reloc->opcode = opcode;
+  reloc->opnd = opnd;
+  reloc->is_null = FALSE;
+  reloc->is_abs_literal = is_abs_literal;
+}
+
+
+/* Find the source_reloc for a particular source offset and relocation
+   type.  Note that the array is sorted by _target_ offset, so this is
+   just a linear search.  */
+
+static source_reloc *
+find_source_reloc (source_reloc *src_relocs,
+                  int src_count,
+                  asection *sec,
+                  Elf_Internal_Rela *irel)
+{
+  int i;
+
+  for (i = 0; i < src_count; i++)
+    {
+      if (src_relocs[i].source_sec == sec
+         && src_relocs[i].r_rel.rela.r_offset == irel->r_offset
+         && (ELF32_R_TYPE (src_relocs[i].r_rel.rela.r_info)
+             == ELF32_R_TYPE (irel->r_info)))
+       return &src_relocs[i];
+    }
+
+  return NULL;
+}
+
+
+static int
+source_reloc_compare (const void *ap, const void *bp)
+{
+  const source_reloc *a = (const source_reloc *) ap;
+  const source_reloc *b = (const source_reloc *) bp;
+
+  if (a->r_rel.target_offset != b->r_rel.target_offset)
+    return (a->r_rel.target_offset - b->r_rel.target_offset);
+
+  /* We don't need to sort on these criteria for correctness,
+     but enforcing a more strict ordering prevents unstable qsort
+     from behaving differently with different implementations.
+     Without the code below we get correct but different results
+     on Solaris 2.7 and 2.8.  We would like to always produce the
+     same results no matter the host. */
+
+  if ((!a->is_null) - (!b->is_null))
+    return ((!a->is_null) - (!b->is_null));
+  return internal_reloc_compare (&a->r_rel.rela, &b->r_rel.rela);
+}
+
+\f
+/* Literal values and value hash tables.  */
+
+/* Literals with the same value can be coalesced.  The literal_value
+   structure records the value of a literal: the "r_rel" field holds the
+   information from the relocation on the literal (if there is one) and
+   the "value" field holds the contents of the literal word itself.
+
+   The value_map structure records a literal value along with the
+   location of a literal holding that value.  The value_map hash table
+   is indexed by the literal value, so that we can quickly check if a
+   particular literal value has been seen before and is thus a candidate
+   for coalescing.  */
+
+typedef struct literal_value_struct literal_value;
+typedef struct value_map_struct value_map;
+typedef struct value_map_hash_table_struct value_map_hash_table;
+
+struct literal_value_struct
+{
+  r_reloc r_rel; 
+  unsigned long value;
+  bfd_boolean is_abs_literal;
+};
+
+struct value_map_struct
+{
+  literal_value val;                   /* The literal value.  */
+  r_reloc loc;                         /* Location of the literal.  */
+  value_map *next;
+};
+
+struct value_map_hash_table_struct
+{
+  unsigned bucket_count;
+  value_map **buckets;
+  unsigned count;
+  bfd_boolean has_last_loc;
+  r_reloc last_loc;
+};
+
+
+static void
+init_literal_value (literal_value *lit,
+                   const r_reloc *r_rel,
+                   unsigned long value,
+                   bfd_boolean is_abs_literal)
+{
+  lit->r_rel = *r_rel;
+  lit->value = value;
+  lit->is_abs_literal = is_abs_literal;
+}
+
+
+static bfd_boolean
+literal_value_equal (const literal_value *src1,
+                    const literal_value *src2,
+                    bfd_boolean final_static_link)
+{
+  struct elf_link_hash_entry *h1, *h2;
+
+  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))
+    return (src1->value == src2->value);
+
+  if (ELF32_R_TYPE (src1->r_rel.rela.r_info)
+      != ELF32_R_TYPE (src2->r_rel.rela.r_info))
+    return FALSE;
+
+  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);
+  h2 = r_reloc_get_hash_entry (&src2->r_rel);
+  if (r_reloc_is_defined (&src1->r_rel)
+      && (final_static_link
+         || ((!h1 || h1->root.type != bfd_link_hash_defweak)
+             && (!h2 || h2->root.type != bfd_link_hash_defweak))))
+    {
+      if (r_reloc_get_section (&src1->r_rel)
+         != r_reloc_get_section (&src2->r_rel))
+       return FALSE;
+    }
+  else
+    {
+      /* Require that the hash entries (i.e., symbols) be identical.  */
+      if (h1 != h2 || h1 == 0)
+       return FALSE;
+    }
+
+  if (src1->is_abs_literal != src2->is_abs_literal)
+    return FALSE;
+
+  return TRUE;
+}
+
+
+/* Must be power of 2.  */
+#define INITIAL_HASH_RELOC_BUCKET_COUNT 1024
+
+static value_map_hash_table *
+value_map_hash_table_init (void)
+{
+  value_map_hash_table *values;
+
+  values = (value_map_hash_table *)
+    bfd_zmalloc (sizeof (value_map_hash_table));
+  values->bucket_count = INITIAL_HASH_RELOC_BUCKET_COUNT;
+  values->count = 0;
+  values->buckets = (value_map **)
+    bfd_zmalloc (sizeof (value_map *) * values->bucket_count);
+  if (values->buckets == NULL) 
+    {
+      free (values);
+      return NULL;
+    }
+  values->has_last_loc = FALSE;
+
+  return values;
+}
+
+
+static void
+value_map_hash_table_delete (value_map_hash_table *table)
+{
+  free (table->buckets);
+  free (table);
+}
+
+
+static unsigned
+hash_bfd_vma (bfd_vma val)
+{
+  return (val >> 2) + (val >> 10);
+}
+
+
+static unsigned
+literal_value_hash (const literal_value *src)
+{
+  unsigned hash_val;
+
+  hash_val = hash_bfd_vma (src->value);
+  if (!r_reloc_is_const (&src->r_rel))
+    {
+      void *sec_or_hash;
+
+      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);
+      else
+       sec_or_hash = r_reloc_get_hash_entry (&src->r_rel);
+      hash_val += hash_bfd_vma ((bfd_vma) (size_t) sec_or_hash);
+    }
+  return hash_val;
+}
+
+
+/* Check if the specified literal_value has been seen before.  */
+
+static value_map *
+value_map_get_cached_value (value_map_hash_table *map,
+                           const literal_value *val,
+                           bfd_boolean final_static_link)
+{
+  value_map *map_e;
+  value_map *bucket;
+  unsigned idx;
+
+  idx = literal_value_hash (val);
+  idx = idx & (map->bucket_count - 1);
+  bucket = map->buckets[idx];
+  for (map_e = bucket; map_e; map_e = map_e->next)
+    {
+      if (literal_value_equal (&map_e->val, val, final_static_link))
+       return map_e;
+    }
+  return NULL;
+}
+
+
+/* Record a new literal value.  It is illegal to call this if VALUE
+   already has an entry here.  */
+
+static value_map *
+add_value_map (value_map_hash_table *map,
+              const literal_value *val,
+              const r_reloc *loc,
+              bfd_boolean final_static_link)
+{
+  value_map **bucket_p;
+  unsigned idx;
+
+  value_map *val_e = (value_map *) bfd_zmalloc (sizeof (value_map));
+  if (val_e == NULL)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return NULL;
+    }
+
+  BFD_ASSERT (!value_map_get_cached_value (map, val, final_static_link));
+  val_e->val = *val;
+  val_e->loc = *loc;
+
+  idx = literal_value_hash (val);
+  idx = idx & (map->bucket_count - 1);
+  bucket_p = &map->buckets[idx];
+
+  val_e->next = *bucket_p;
+  *bucket_p = val_e;
+  map->count++;
+  /* FIXME: Consider resizing the hash table if we get too many entries.  */
+  
+  return val_e;
+}
+
+\f
+/* Lists of text actions (ta_) for narrowing, widening, longcall
+   conversion, space fill, code & literal removal, etc.  */
+
+/* The following text actions are generated:
+
+   "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
+      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
+                           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
+                           multiple literals can be placed at the
+                            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"
+   or a "ta_fill" that adds space, the removed_bytes will be negative.  */
+
+typedef struct text_action_struct text_action;
+typedef struct text_action_list_struct text_action_list;
+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_literal,
+  ta_add_literal
+};
+
+
+/* Structure for a text action record.  */
+struct text_action_struct
+{
+  text_action_t action;
+  asection *sec;       /* Optional */
+  bfd_vma offset;
+  bfd_vma virtual_offset;  /* Zero except for adding literals.  */
+  int removed_bytes;
+  literal_value value; /* Only valid when adding literals.  */
+
+  text_action *next;
+};
+
+
+/* List of all of the actions taken on a text section.  */
+struct text_action_list_struct
+{
+  text_action *head;
+};
+
+
+static text_action *
+find_fill_action (text_action_list *l, asection *sec, bfd_vma offset)
+{
+  text_action **m_p;
+
+  /* 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;
+    }
+  return NULL;
+}
+
+
+static int
+compute_removed_action_diff (const text_action *ta,
+                            asection *sec,
+                            bfd_vma offset,
+                            int removed,
+                            int removable_space)
+{
+  int new_removed;
+  int current_removed = 0;
+
+  if (ta)
+    current_removed = ta->removed_bytes;
+
+  BFD_ASSERT (ta == NULL || ta->offset == offset);
+  BFD_ASSERT (ta == NULL || ta->action == ta_fill);
+
+  /* It is not necessary to fill at the end of a section.  Clean this up.  */
+  if (sec->size == offset)
+    new_removed = removable_space - 0;
+  else
+    {
+      int space;
+      int added = -removed - current_removed;
+      /* Ignore multiples of the section alignment.  */
+      added = ((1 << sec->alignment_power) - 1) & added;
+      new_removed = (-added);
+
+      /* Modify for removable.  */
+      space = removable_space - new_removed;
+      new_removed = (removable_space
+                    - (((1 << sec->alignment_power) - 1) & space));
+    }
+  return (new_removed - current_removed);
+}
+
+
+static void
+adjust_fill_action (text_action *ta, int fill_diff)
+{
+  ta->removed_bytes += fill_diff;
+}
+
+
+/* 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 
+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;
+
+  /* It is not necessary to fill at the end of a section.  */
+  if (action == ta_fill && sec->size == offset)
+    return;
+
+  /* It is not necessary to fill 0 bytes.  */
+  if (action == ta_fill && removed == 0)
+    return;
+
+  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 && action == ta_fill)
+       {
+         t->removed_bytes += removed;
+         return;
+       }
+    }
+
+  /* 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;
+}
+
+
+static void
+text_action_add_literal (text_action_list *l,
+                        text_action_t action,
+                        const r_reloc *loc,
+                        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;
+  bfd_vma virtual_offset = loc->virtual_offset;
+
+  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;
+  ta->sec = sec;
+  ta->offset = offset;
+  ta->virtual_offset = virtual_offset;
+  ta->value = *value;
+  ta->removed_bytes = removed;
+  ta->next = (*m_p);
+  *m_p = ta;
+}
+
+
+static bfd_vma 
+offset_with_removed_text (text_action_list *action_list, bfd_vma offset)
+{
+  text_action *r;
+  int removed = 0;
+
+  for (r = action_list->head; r && r->offset <= offset; r = r->next)
+    {
+      if (r->offset < offset
+         || (r->action == ta_fill && r->removed_bytes < 0))
+       removed += r->removed_bytes;
+    }
+
+  return (offset - removed);
+}
+
+
+static bfd_vma
+offset_with_removed_text_before_fill (text_action_list *action_list,
+                                     bfd_vma offset)
+{
+  text_action *r;
+  int removed = 0;
+
+  for (r = action_list->head; r && r->offset < offset; r = r->next)
+    removed += r->removed_bytes;
+
+  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)
+{
+  text_action *t;
+  for (t = action_list->head; t; t = t->next)
+    {
+      if (t->offset == offset)
+       {
+         switch (t->action)
+           {
+           case ta_none:
+           case ta_fill:
+             break;
+           case ta_remove_insn:
+           case ta_remove_longcall:
+           case ta_convert_longcall:
+           case ta_narrow_insn:
+           case ta_widen_insn:
+             return t;
+           case ta_remove_literal:
+           case ta_add_literal:
+             BFD_ASSERT (0);
+             break;
+           }
+       }
+    }
+  return NULL;
+}
+
+
+#if DEBUG
+
+static void
+print_action_list (FILE *fp, text_action_list *action_list)
+{
+  text_action *r;
+
+  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;
+       }
+
+      fprintf (fp, "%s: %s[0x%lx] \"%s\" %d\n",
+              r->sec->owner->filename,
+              r->sec->name, r->offset, t, r->removed_bytes);
+    }
+}
+
+#endif /* DEBUG */
+
+\f
+/* Lists of literals being coalesced or removed.  */
+
+/* In the usual case, the literal identified by "from" is being
+   coalesced with another literal identified by "to".  If the literal is
+   unused and is being removed altogether, "to.abfd" will be NULL.
+   The removed_literal entries are kept on a per-section list, sorted
+   by the "from" offset field.  */
+
+typedef struct removed_literal_struct removed_literal;
+typedef struct removed_literal_list_struct removed_literal_list;
+
+struct removed_literal_struct
+{
+  r_reloc from;
+  r_reloc to;
+  removed_literal *next;
+};
+
+struct removed_literal_list_struct
+{
+  removed_literal *head;
+  removed_literal *tail;
+};
+
+
+/* Record that the literal at "from" is being removed.  If "to" is not
+   NULL, the "from" literal is being coalesced with the "to" literal.  */
+
+static void
+add_removed_literal (removed_literal_list *removed_list,
+                    const r_reloc *from,
+                    const r_reloc *to)
+{
+  removed_literal *r, *new_r, *next_r;
+
+  new_r = (removed_literal *) bfd_zmalloc (sizeof (removed_literal));
+
+  new_r->from = *from;
+  if (to)
+    new_r->to = *to;
+  else
+    new_r->to.abfd = NULL;
+  new_r->next = NULL;
+  
+  r = removed_list->head;
+  if (r == NULL) 
+    {
+      removed_list->head = new_r;
+      removed_list->tail = new_r;
+    }
+  /* Special check for common case of append.  */
+  else if (removed_list->tail->from.target_offset < from->target_offset)
+    {
+      removed_list->tail->next = new_r;
+      removed_list->tail = new_r;
+    }
+  else
+    {
+      while (r->from.target_offset < from->target_offset && r->next) 
+       {
+         r = r->next;
+       }
+      next_r = r->next;
+      r->next = new_r;
+      new_r->next = next_r;
+      if (next_r == NULL)
+       removed_list->tail = new_r;
+    }
+}
+
+
+/* Check if the list of removed literals contains an entry for the
+   given address.  Return the entry if found.  */
+
+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;
+}
+
+
+#if DEBUG
+
+static void
+print_removed_literals (FILE *fp, removed_literal_list *removed_list)
+{
+  removed_literal *r;
+  r = removed_list->head;
+  if (r)
+    fprintf (fp, "Removed Literals\n");
+  for (; r != NULL; r = r->next)
+    {
+      print_r_reloc (fp, &r->from);
+      fprintf (fp, " => ");
+      if (r->to.abfd == NULL)
+       fprintf (fp, "REMOVED");
+      else
+       print_r_reloc (fp, &r->to);
+      fprintf (fp, "\n");
+    }
+}
+
+#endif /* DEBUG */
+
+\f
+/* Per-section data for relaxation.  */
+
+typedef struct reloc_bfd_fix_struct reloc_bfd_fix;
+
+struct xtensa_relax_info_struct
+{
+  bfd_boolean is_relaxable_literal_section;
+  bfd_boolean is_relaxable_asm_section;
+  int visited;                         /* Number of times visited.  */
+
+  source_reloc *src_relocs;            /* Array[src_count].  */
+  int src_count;
+  int src_next;                                /* Next src_relocs entry to assign.  */
+
+  removed_literal_list removed_list;
+  text_action_list action_list;
+
+  reloc_bfd_fix *fix_list;
+  reloc_bfd_fix *fix_array;
+  unsigned fix_array_count;
+
+  /* Support for expanding the reloc array that is stored
+     in the section structure.  If the relocations have been
+     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; 
+  unsigned relocs_count;
+  unsigned allocated_relocs_count;
+};
+
+struct elf_xtensa_section_data
+{
+  struct bfd_elf_section_data elf;
+  xtensa_relax_info relax_info;
+};
+
+
+static bfd_boolean
+elf_xtensa_new_section_hook (bfd *abfd, asection *sec)
+{
+  struct elf_xtensa_section_data *sdata;
+  bfd_size_type amt = sizeof (*sdata);
+
+  sdata = (struct elf_xtensa_section_data *) bfd_zalloc (abfd, amt);
+  if (sdata == NULL)
+    return FALSE;
+  sec->used_by_bfd = (void *) sdata;
+
+  return _bfd_elf_new_section_hook (abfd, sec);
+}
+
+
+static xtensa_relax_info *
+get_xtensa_relax_info (asection *sec)
+{
+  struct elf_xtensa_section_data *section_data;
+
+  /* No info available if no section or if it is an output section.  */
+  if (!sec || sec == sec->output_section)
+    return NULL;
+
+  section_data = (struct elf_xtensa_section_data *) elf_section_data (sec);
+  return &section_data->relax_info;
+}
+
+
+static void
+init_xtensa_relax_info (asection *sec)
+{
+  xtensa_relax_info *relax_info = get_xtensa_relax_info (sec);
+
+  relax_info->is_relaxable_literal_section = FALSE;
+  relax_info->is_relaxable_asm_section = FALSE;
+  relax_info->visited = 0;
+
+  relax_info->src_relocs = NULL;
+  relax_info->src_count = 0;
+  relax_info->src_next = 0;
+
+  relax_info->removed_list.head = NULL;
+  relax_info->removed_list.tail = NULL;
+
+  relax_info->action_list.head = NULL;
+
+  relax_info->fix_list = NULL;
+  relax_info->fix_array = NULL;
+  relax_info->fix_array_count = 0;
+
+  relax_info->allocated_relocs = NULL; 
+  relax_info->relocs_count = 0;
+  relax_info->allocated_relocs_count = 0;
+}
+
+\f
+/* Coalescing literals may require a relocation to refer to a section in
+   a different input file, but the standard relocation information
+   cannot express that.  Instead, the reloc_bfd_fix structures are used
+   to "fix" the relocations that refer to sections in other input files.
+   These structures are kept on per-section lists.  The "src_type" field
+   records the relocation type in case there are multiple relocations on
+   the same location.  FIXME: This is ugly; an alternative might be to
+   add new symbols with the "owner" field to some other input file.  */
+
+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;
+};
+
+
+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)
+{
+  reloc_bfd_fix *fix;
+
+  fix = (reloc_bfd_fix *) bfd_malloc (sizeof (reloc_bfd_fix));
+  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;
+
+  return fix;
+}
+
+
+static void
+add_fix (asection *src_sec, reloc_bfd_fix *fix)
+{
+  xtensa_relax_info *relax_info;
+
+  relax_info = get_xtensa_relax_info (src_sec);
+  fix->next = relax_info->fix_list;
+  relax_info->fix_list = fix;
+}
+
+
+static int
+fix_compare (const void *ap, const void *bp)
+{
+  const reloc_bfd_fix *a = (const reloc_bfd_fix *) ap;
+  const reloc_bfd_fix *b = (const reloc_bfd_fix *) bp;
+
+  if (a->src_offset != b->src_offset)
+    return (a->src_offset - b->src_offset);
+  return (a->src_type - b->src_type);
+}
+
+
+static void
+cache_fix_array (asection *sec)
+{
+  unsigned i, count = 0;
+  reloc_bfd_fix *r;
+  xtensa_relax_info *relax_info = get_xtensa_relax_info (sec);
+
+  if (relax_info == NULL)
+    return;
+  if (relax_info->fix_list == NULL)
+    return;
+
+  for (r = relax_info->fix_list; r != NULL; r = r->next)
+    count++;
+
+  relax_info->fix_array =
+    (reloc_bfd_fix *) bfd_malloc (sizeof (reloc_bfd_fix) * count);
+  relax_info->fix_array_count = count;
+
+  r = relax_info->fix_list;
+  for (i = 0; i < count; i++, r = r->next)
+    {
+      relax_info->fix_array[count - 1 - i] = *r;
+      relax_info->fix_array[count - 1 - i].next = NULL;
+    }
+
+  qsort (relax_info->fix_array, relax_info->fix_array_count,
+        sizeof (reloc_bfd_fix), fix_compare);
+}
+
+
+static reloc_bfd_fix *
+get_bfd_fix (asection *sec, bfd_vma offset, unsigned type)
+{
+  xtensa_relax_info *relax_info = get_xtensa_relax_info (sec);
+  reloc_bfd_fix *rv;
+  reloc_bfd_fix key;
+
+  if (relax_info == NULL)
+    return NULL;
+  if (relax_info->fix_list == NULL)
+    return NULL;
+
+  if (relax_info->fix_array == NULL)
+    cache_fix_array (sec);
+
+  key.src_offset = offset;
+  key.src_type = type;
+  rv = bsearch (&key, relax_info->fix_array,  relax_info->fix_array_count,
+               sizeof (reloc_bfd_fix), fix_compare);
+  return rv;
+}
+
+\f
+/* Section caching.  */
+
+typedef struct section_cache_struct section_cache_t;
+
+struct section_cache_struct
+{
+  asection *sec;
+
+  bfd_byte *contents;          /* Cache of the section contents.  */
+  bfd_size_type content_length;
+
+  property_table_entry *ptbl;  /* Cache of the section property table.  */
+  unsigned pte_count;
+
+  Elf_Internal_Rela *relocs;   /* Cache of the section relocations.  */
+  unsigned reloc_count;
+};
+
+
+static void
+init_section_cache (section_cache_t *sec_cache)
+{
+  memset (sec_cache, 0, sizeof (*sec_cache));
+}
+
+
+static void
+clear_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));
+    }
+}
+
+
+static bfd_boolean
+section_cache_section (section_cache_t *sec_cache,
+                      asection *sec,
+                      struct bfd_link_info *link_info)
+{
+  bfd *abfd;
+  property_table_entry *prop_table = NULL;
+  int ptblsize = 0;
+  bfd_byte *contents = NULL;
+  Elf_Internal_Rela *internal_relocs = NULL;
+  bfd_size_type sec_size;
+
+  if (sec == NULL)
+    return FALSE;
+  if (sec == sec_cache->sec)
+    return TRUE;
+
+  abfd = sec->owner;
+  sec_size = bfd_get_section_limit (abfd, sec);
+
+  /* Get the contents.  */
+  contents = retrieve_contents (abfd, sec, link_info->keep_memory);
+  if (contents == NULL && sec_size != 0)
+    goto err;
+
+  /* Get the relocations.  */
+  internal_relocs = retrieve_internal_relocs (abfd, sec,
+                                             link_info->keep_memory);
+
+  /* Get the entry table.  */
+  ptblsize = xtensa_read_table_entries (abfd, sec, &prop_table,
+                                       XTENSA_PROP_SEC_NAME, FALSE);
+  if (ptblsize < 0)
+    goto err;
+
+  /* Fill in the new section cache.  */
+  clear_section_cache (sec_cache);
+  memset (sec_cache, 0, sizeof (sec_cache));
+
+  sec_cache->sec = sec;
+  sec_cache->contents = contents;
+  sec_cache->content_length = sec_size;
+  sec_cache->relocs = internal_relocs;
+  sec_cache->reloc_count = sec->reloc_count;
+  sec_cache->pte_count = ptblsize;
+  sec_cache->ptbl = prop_table;
+
+  return TRUE;
+
+ err:
+  release_contents (sec, contents);
+  release_internal_relocs (sec, internal_relocs);
+  if (prop_table)
+    free (prop_table);
+  return FALSE;
+}
+
+\f
+/* Extended basic blocks.  */
+
+/* An ebb_struct represents an Extended Basic Block.  Within this
+   range, we guarantee that all instructions are decodable, the
+   property table entries are contiguous, and no property table
+   specifies a segment that cannot have instructions moved.  This
+   structure contains caches of the contents, property table and
+   relocations for the specified section for easy use.  The range is
+   specified by ranges of indices for the byte offset, property table
+   offsets and relocation offsets.  These must be consistent.  */
+
+typedef struct ebb_struct ebb_t;
+
+struct ebb_struct
+{
+  asection *sec;
+
+  bfd_byte *contents;          /* Cache of the section contents.  */
+  bfd_size_type content_length;
+
+  property_table_entry *ptbl;  /* Cache of the section property table.  */
+  unsigned pte_count;
+
+  Elf_Internal_Rela *relocs;   /* Cache of the section relocations.  */
+  unsigned reloc_count;
+
+  bfd_vma start_offset;                /* Offset in section.  */
+  unsigned start_ptbl_idx;     /* Offset in the property table.  */
+  unsigned start_reloc_idx;    /* Offset in the relocations.  */
+
+  bfd_vma end_offset;
+  unsigned end_ptbl_idx;
+  unsigned end_reloc_idx;
+
+  bfd_boolean ends_section;    /* Is this the last ebb in a section?  */
+
+  /* The unreachable property table at the end of this set of blocks;
+     NULL if the end is not an unreachable block.  */
+  property_table_entry *ends_unreachable;
+};
+
+
+enum ebb_target_enum
+{
+  EBB_NO_ALIGN = 0,
+  EBB_DESIRE_TGT_ALIGN,
+  EBB_REQUIRE_TGT_ALIGN,
+  EBB_REQUIRE_LOOP_ALIGN,
+  EBB_REQUIRE_ALIGN
+};
+
+
+/* proposed_action_struct is similar to the text_action_struct except
+   that is represents a potential transformation, not one that will
+   occur.  We build a list of these for an extended basic block
+   and use them to compute the actual actions desired.  We must be
+   careful that the entire set of actual actions we perform do not
+   break any relocations that would fit if the actions were not
+   performed.  */
+
+typedef struct proposed_action_struct proposed_action;
+
+struct proposed_action_struct
+{
+  enum ebb_target_enum align_type; /* for the target alignment */
+  bfd_vma alignment_pow;
+  text_action_t action;
+  bfd_vma offset;
+  int removed_bytes;
+  bfd_boolean do_action; /* If false, then we will not perform the action.  */
+};
+
+
+/* The ebb_constraint_struct keeps a set of proposed actions for an
+   extended basic block.   */
+
+typedef struct ebb_constraint_struct ebb_constraint;
+
+struct ebb_constraint_struct
+{
+  ebb_t ebb;
+  bfd_boolean start_movable;
+
+  /* Bytes of extra space at the beginning if movable.  */
+  int start_extra_space;
+
+  enum ebb_target_enum start_align;
+
+  bfd_boolean end_movable;
+
+  /* Bytes of extra space at the end if movable.  */
+  int end_extra_space;
+
+  unsigned action_count;
+  unsigned action_allocated;
+
+  /* Array of proposed actions.  */
+  proposed_action *actions;
+
+  /* Action alignments -- one for each proposed action.  */
+  enum ebb_target_enum *action_aligns;
+};
+
+
+static void
+init_ebb_constraint (ebb_constraint *c)
+{
+  memset (c, 0, sizeof (ebb_constraint));
+}
+
+
+static void
+free_ebb_constraint (ebb_constraint *c)
+{
+  if (c->actions)
+    free (c->actions);
+}
+
+
+static void
+init_ebb (ebb_t *ebb,
+         asection *sec,
+         bfd_byte *contents,
+         bfd_size_type content_length,
+         property_table_entry *prop_table,
+         unsigned ptblsize,
+         Elf_Internal_Rela *internal_relocs,
+         unsigned reloc_count)
+{
+  memset (ebb, 0, sizeof (ebb_t));
+  ebb->sec = sec;
+  ebb->contents = contents;
+  ebb->content_length = content_length;
+  ebb->ptbl = prop_table;
+  ebb->pte_count = ptblsize;
+  ebb->relocs = internal_relocs;
+  ebb->reloc_count = reloc_count;
+  ebb->start_offset = 0;
+  ebb->end_offset = ebb->content_length - 1;
+  ebb->start_ptbl_idx = 0;
+  ebb->end_ptbl_idx = ptblsize;
+  ebb->start_reloc_idx = 0;
+  ebb->end_reloc_idx = reloc_count;
+}
+
+
+/* Extend the ebb to all decodable contiguous sections.  The algorithm
+   for building a basic block around an instruction is to push it
+   forward until we hit the end of a section, an unreachable block or
+   a block that cannot be transformed.  Then we push it backwards
+   searching for similar conditions.  */
+
+static bfd_boolean extend_ebb_bounds_forward (ebb_t *);
+static bfd_boolean extend_ebb_bounds_backward (ebb_t *);
+static bfd_size_type insn_block_decodable_len
+  (bfd_byte *, bfd_size_type, bfd_vma, bfd_size_type);
+
+static bfd_boolean
+extend_ebb_bounds (ebb_t *ebb)
+{
+  if (!extend_ebb_bounds_forward (ebb))
+    return FALSE;
+  if (!extend_ebb_bounds_backward (ebb))
+    return FALSE;
+  return TRUE;
+}
+
+
+static bfd_boolean
+extend_ebb_bounds_forward (ebb_t *ebb)
+{
+  property_table_entry *the_entry, *new_entry;
+
+  the_entry = &ebb->ptbl[ebb->end_ptbl_idx];
+
+  /* Stop when (1) we cannot decode an instruction, (2) we are at
+     the end of the property tables, (3) we hit a non-contiguous property
+     table entry, (4) we hit a NO_TRANSFORM region.  */
+
+  while (1)
+    {
+      bfd_vma entry_end;
+      bfd_size_type insn_block_len;
+
+      entry_end = the_entry->address - ebb->sec->vma + the_entry->size;
+      insn_block_len =
+       insn_block_decodable_len (ebb->contents, ebb->content_length,
+                                 ebb->end_offset,
+                                 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);
+         return FALSE;
+       }
+      ebb->end_offset += insn_block_len;
+
+      if (ebb->end_offset == ebb->sec->size)
+       ebb->ends_section = TRUE;
+
+      /* Update the reloc counter.  */
+      while (ebb->end_reloc_idx + 1 < ebb->reloc_count
+            && (ebb->relocs[ebb->end_reloc_idx + 1].r_offset
+                < ebb->end_offset))
+       {
+         ebb->end_reloc_idx++;
+       }
+
+      if (ebb->end_ptbl_idx + 1 == ebb->pte_count)
+       return TRUE;
+
+      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)
+         || ((the_entry->flags & XTENSA_PROP_ALIGN) != 0))
+       break;
+
+      if (the_entry->address + the_entry->size != new_entry->address)
+       break;
+
+      the_entry = new_entry;
+      ebb->end_ptbl_idx++;
+    }
+
+  /* Quick check for an unreachable or end of file just at the end.  */
+  if (ebb->end_ptbl_idx + 1 == ebb->pte_count)
+    {
+      if (ebb->end_offset == ebb->content_length)
+       ebb->ends_section = TRUE;
+    }
+  else
+    {
+      new_entry = &ebb->ptbl[ebb->end_ptbl_idx + 1];
+      if ((new_entry->flags & XTENSA_PROP_UNREACHABLE) != 0
+         && the_entry->address + the_entry->size == new_entry->address)
+       ebb->ends_unreachable = new_entry;
+    }
+
+  /* Any other ending requires exact alignment.  */
+  return TRUE;
+}
+
+
+static bfd_boolean
+extend_ebb_bounds_backward (ebb_t *ebb)
+{
+  property_table_entry *the_entry, *new_entry;
+
+  the_entry = &ebb->ptbl[ebb->start_ptbl_idx];
+
+  /* Stop when (1) we cannot decode the instructions in the current entry.
+     (2) we are at the beginning of the property tables, (3) we hit a
+     non-contiguous property table entry, (4) we hit a NO_TRANSFORM region.  */
+
+  while (1)
+    {
+      bfd_vma block_begin;
+      bfd_size_type insn_block_len;
+
+      block_begin = the_entry->address - ebb->sec->vma;
+      insn_block_len =
+       insn_block_decodable_len (ebb->contents, ebb->content_length,
+                                 block_begin,
+                                 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);
+         return FALSE;
+       }
+      ebb->start_offset -= insn_block_len;
+
+      /* Update the reloc counter.  */
+      while (ebb->start_reloc_idx > 0
+            && (ebb->relocs[ebb->start_reloc_idx - 1].r_offset
+                >= ebb->start_offset))
+       {
+         ebb->start_reloc_idx--;
+       }
+
+      if (ebb->start_ptbl_idx == 0)
+       return TRUE;
+
+      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_ALIGN) != 0))
+       return TRUE;
+      if (new_entry->address + new_entry->size != the_entry->address)
+       return TRUE;
+
+      the_entry = new_entry;
+      ebb->start_ptbl_idx--;
+    }
+  return TRUE;
+}
+
+
+static bfd_size_type
+insn_block_decodable_len (bfd_byte *contents,
+                         bfd_size_type content_len,
+                         bfd_vma block_offset,
+                         bfd_size_type block_len)
+{
+  bfd_vma offset = block_offset;
+
+  while (offset < block_offset + block_len)
+    {
+      bfd_size_type insn_len = 0;
+
+      insn_len = insn_decode_len (contents, content_len, offset);
+      if (insn_len == 0)
+       return (offset - block_offset);
+      offset += insn_len;
+    }
+  return (offset - block_offset);
+}
+
+
+static void
+ebb_propose_action (ebb_constraint *c,
+                   enum ebb_target_enum align_type,
+                   bfd_vma alignment_pow,
+                   text_action_t action,
+                   bfd_vma offset,
+                   int removed_bytes,
+                   bfd_boolean do_action)
+{
+  proposed_action *act;
+
+  if (c->action_allocated <= c->action_count)
+    {
+      unsigned new_allocated, i;
+      proposed_action *new_actions;
+
+      new_allocated = (c->action_count + 2) * 2;
+      new_actions = (proposed_action *)
+       bfd_zmalloc (sizeof (proposed_action) * new_allocated);
+
+      for (i = 0; i < c->action_count; i++)
+       new_actions[i] = c->actions[i];
+      if (c->actions)
+       free (c->actions);
+      c->actions = new_actions;
+      c->action_allocated = new_allocated;
+    }
+
+  act = &c->actions[c->action_count];
+  act->align_type = align_type;
+  act->alignment_pow = alignment_pow;
+  act->action = action;
+  act->offset = offset;
+  act->removed_bytes = removed_bytes;
+  act->do_action = do_action;
+
+  c->action_count++;
+}
+
+\f
+/* Access to internal relocations, section contents and symbols.  */
+
+/* During relaxation, we need to modify relocations, section contents,
+   and symbol definitions, and we need to keep the original values from
+   being reloaded from the input files, i.e., we need to "pin" the
+   modified values in memory.  We also want to continue to observe the
+   setting of the "keep-memory" flag.  The following functions wrap the
+   standard BFD functions to take care of this for us.  */
+
+static Elf_Internal_Rela *
+retrieve_internal_relocs (bfd *abfd, asection *sec, bfd_boolean keep_memory)
+{
+  Elf_Internal_Rela *internal_relocs;
+
+  if ((sec->flags & SEC_LINKER_CREATED) != 0)
+    return NULL;
+
+  internal_relocs = elf_section_data (sec)->relocs;
+  if (internal_relocs == NULL)
+    internal_relocs = (_bfd_elf_link_read_relocs
+                      (abfd, sec, NULL, NULL, keep_memory));
   return internal_relocs;
 }
 
 
-static void
-pin_internal_relocs (sec, internal_relocs)
-     asection *sec;
-     Elf_Internal_Rela *internal_relocs;
-{
-  elf_section_data (sec)->relocs = internal_relocs;
-}
+static void
+pin_internal_relocs (asection *sec, Elf_Internal_Rela *internal_relocs)
+{
+  elf_section_data (sec)->relocs = internal_relocs;
+}
+
+
+static void
+release_internal_relocs (asection *sec, Elf_Internal_Rela *internal_relocs)
+{
+  if (internal_relocs
+      && elf_section_data (sec)->relocs != internal_relocs)
+    free (internal_relocs);
+}
+
+
+static bfd_byte *
+retrieve_contents (bfd *abfd, asection *sec, bfd_boolean keep_memory)
+{
+  bfd_byte *contents;
+  bfd_size_type sec_size;
+
+  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);
+         return NULL;
+       }
+      if (keep_memory) 
+       elf_section_data (sec)->this_hdr.contents = contents;
+    }
+  return contents;
+}
+
+
+static void
+pin_contents (asection *sec, bfd_byte *contents)
+{
+  elf_section_data (sec)->this_hdr.contents = contents;
+}
+
+
+static void
+release_contents (asection *sec, bfd_byte *contents)
+{
+  if (contents && elf_section_data (sec)->this_hdr.contents != contents)
+    free (contents);
+}
+
+
+static Elf_Internal_Sym *
+retrieve_local_syms (bfd *input_bfd)
+{
+  Elf_Internal_Shdr *symtab_hdr;
+  Elf_Internal_Sym *isymbuf;
+  size_t locsymcount;
+
+  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+  locsymcount = symtab_hdr->sh_info;
+
+  isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
+  if (isymbuf == NULL && locsymcount != 0)
+    isymbuf = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0,
+                                   NULL, NULL, NULL);
+
+  /* Save the symbols for this input file so they won't be read again.  */
+  if (isymbuf && isymbuf != (Elf_Internal_Sym *) symtab_hdr->contents)
+    symtab_hdr->contents = (unsigned char *) isymbuf;
+
+  return isymbuf;
+}
+
+\f
+/* Code for link-time relaxation.  */
+
+/* Initialization for relaxation: */
+static bfd_boolean analyze_relocations (struct bfd_link_info *);
+static bfd_boolean find_relaxable_sections
+  (bfd *, asection *, struct bfd_link_info *, bfd_boolean *);
+static bfd_boolean collect_source_relocs
+  (bfd *, asection *, struct bfd_link_info *);
+static bfd_boolean is_resolvable_asm_expansion
+  (bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, struct bfd_link_info *,
+   bfd_boolean *);
+static Elf_Internal_Rela *find_associated_l32r_irel
+  (bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, Elf_Internal_Rela *);
+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 *);
+static bfd_boolean check_section_ebb_pcrels_fit
+  (bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, const ebb_constraint *);
+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 remove_dead_literal
+  (bfd *, asection *, struct bfd_link_info *, Elf_Internal_Rela *,
+   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,
+   source_reloc *, property_table_entry *, int, section_cache_t *,
+   bfd_boolean);
+static bfd_boolean relocations_reach (source_reloc *, int, const r_reloc *);
+static bfd_boolean coalesce_shared_literal
+  (asection *, source_reloc *, property_table_entry *, int, value_map *);
+static bfd_boolean move_shared_literal
+  (asection *, struct bfd_link_info *, source_reloc *, property_table_entry *,
+   int, const r_reloc *, const literal_value *, section_cache_t *);
+
+/* Second pass: */
+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 void shrink_dynamic_reloc_sections
+  (struct bfd_link_info *, bfd *, asection *, Elf_Internal_Rela *);
+static bfd_boolean move_literal
+  (bfd *, struct bfd_link_info *, asection *, bfd_vma, bfd_byte *,
+   xtensa_relax_info *, Elf_Internal_Rela **, const literal_value *);
+static bfd_boolean relax_property_section
+  (bfd *, asection *, struct bfd_link_info *);
+
+/* Third pass: */
+static bfd_boolean relax_section_symbols (bfd *, asection *);
+
+
+static bfd_boolean 
+elf_xtensa_relax_section (bfd *abfd,
+                         asection *sec,
+                         struct bfd_link_info *link_info,
+                         bfd_boolean *again)
+{
+  static value_map_hash_table *values = NULL;
+  static bfd_boolean relocations_analyzed = FALSE;
+  xtensa_relax_info *relax_info;
+
+  if (!relocations_analyzed)
+    {
+      /* Do some overall initialization for relaxation.  */
+      values = value_map_hash_table_init ();
+      if (values == NULL)
+       return FALSE;
+      relaxing_section = TRUE;
+      if (!analyze_relocations (link_info))
+       return FALSE;
+      relocations_analyzed = TRUE;
+    }
+  *again = FALSE;
+
+  /* Don't mess with linker-created sections.  */
+  if ((sec->flags & SEC_LINKER_CREATED) != 0)
+    return TRUE;
+
+  relax_info = get_xtensa_relax_info (sec);
+  BFD_ASSERT (relax_info != NULL);
+
+  switch (relax_info->visited)
+    {
+    case 0:
+      /* Note: It would be nice to fold this pass into
+        analyze_relocations, but it is important for this step that the
+        sections be examined in link order.  */
+      if (!compute_removed_literals (abfd, sec, link_info, values))
+       return FALSE;
+      *again = TRUE;
+      break;
+
+    case 1:
+      if (values)
+       value_map_hash_table_delete (values);
+      values = NULL;
+      if (!relax_section (abfd, sec, link_info))
+       return FALSE;
+      *again = TRUE;
+      break;
+
+    case 2:
+      if (!relax_section_symbols (abfd, sec))
+       return FALSE;
+      break;
+    }
+
+  relax_info->visited++;
+  return TRUE;
+}
+
+\f
+/* Initialization for relaxation.  */
+
+/* This function is called once at the start of relaxation.  It scans
+   all the input sections and marks the ones that are relaxable (i.e.,
+   literal sections with L32R relocations against them), and then
+   collects source_reloc information for all the relocations against
+   those relaxable sections.  During this process, it also detects
+   longcalls, i.e., calls relaxed by the assembler into indirect
+   calls, that can be optimized back into direct calls.  Within each
+   extended basic block (ebb) containing an optimized longcall, it
+   computes a set of "text actions" that can be performed to remove
+   the L32R associated with the longcall while optionally preserving
+   branch target alignments.  */
+
+static bfd_boolean
+analyze_relocations (struct bfd_link_info *link_info)
+{
+  bfd *abfd;
+  asection *sec;
+  bfd_boolean is_relaxable = FALSE;
+
+  /* Initialize the per-section relaxation info.  */
+  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 (sec = abfd->sections; sec != NULL; sec = sec->next)
+      {
+       if (!find_relaxable_sections (abfd, sec, link_info, &is_relaxable))
+         return FALSE;
+      }
+
+  /* Bail out if there are no relaxable sections.  */
+  if (!is_relaxable)
+    return TRUE;
+
+  /* Allocate space for source_relocs.  */
+  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;
+
+       relax_info = get_xtensa_relax_info (sec);
+       if (relax_info->is_relaxable_literal_section
+           || relax_info->is_relaxable_asm_section)
+         {
+           relax_info->src_relocs = (source_reloc *)
+             bfd_malloc (relax_info->src_count * sizeof (source_reloc));
+         }
+      }
+
+  /* Collect info on relocations against each relaxable section.  */
+  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))
+         return FALSE;
+      }
+
+  /* Compute the text actions.  */
+  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))
+         return FALSE;
+      }
+
+  return TRUE;
+}
+
+
+/* Find all the sections that might be relaxed.  The motivation for
+   this pass is that collect_source_relocs() needs to record _all_ the
+   relocations that target each relaxable section.  That is expensive
+   and unnecessary unless the target section is actually going to be
+   relaxed.  This pass identifies all such sections by checking if
+   they have L32Rs pointing to them.  In the process, the total number
+   of relocations targeting each section is also counted so that we
+   know how much space to allocate for source_relocs against each
+   relaxable literal section.  */
+
+static bfd_boolean
+find_relaxable_sections (bfd *abfd,
+                        asection *sec,
+                        struct bfd_link_info *link_info,
+                        bfd_boolean *is_relaxable_p)
+{
+  Elf_Internal_Rela *internal_relocs;
+  bfd_byte *contents;
+  bfd_boolean ok = TRUE;
+  unsigned i;
+  xtensa_relax_info *source_relax_info;
+
+  internal_relocs = retrieve_internal_relocs (abfd, sec,
+                                             link_info->keep_memory);
+  if (internal_relocs == NULL) 
+    return ok;
+
+  contents = retrieve_contents (abfd, sec, link_info->keep_memory);
+  if (contents == NULL && sec->size != 0)
+    {
+      ok = FALSE;
+      goto error_return;
+    }
+
+  source_relax_info = get_xtensa_relax_info (sec);
+  for (i = 0; i < sec->reloc_count; i++) 
+    {
+      Elf_Internal_Rela *irel = &internal_relocs[i];
+      r_reloc r_rel;
+      asection *target_sec;
+      xtensa_relax_info *target_relax_info;
+
+      /* If this section has not already been marked as "relaxable", and
+        if it contains any ASM_EXPAND relocations (marking expanded
+        longcalls) that can be optimized into direct calls, then mark
+        the section as "relaxable".  */
+      if (source_relax_info
+         && !source_relax_info->is_relaxable_asm_section
+         && ELF32_R_TYPE (irel->r_info) == R_XTENSA_ASM_EXPAND)
+       {
+         bfd_boolean is_reachable = FALSE;
+         if (is_resolvable_asm_expansion (abfd, sec, contents, irel,
+                                          link_info, &is_reachable)
+             && is_reachable)
+           {
+             source_relax_info->is_relaxable_asm_section = TRUE;
+             *is_relaxable_p = TRUE;
+           }
+       }
+
+      r_reloc_init (&r_rel, abfd, irel, contents,
+                   bfd_get_section_limit (abfd, sec));
+
+      target_sec = r_reloc_get_section (&r_rel);
+      target_relax_info = get_xtensa_relax_info (target_sec);
+      if (!target_relax_info)
+       continue;
+
+      /* Count PC-relative operand relocations against the target section.
+         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++;
+
+      if (is_l32r_relocation (abfd, sec, contents, irel)
+         && r_reloc_is_defined (&r_rel))
+       {
+         /* Mark the target section as relaxable.  */
+         target_relax_info->is_relaxable_literal_section = TRUE;
+         *is_relaxable_p = TRUE;
+       }
+    }
+
+ error_return:
+  release_contents (sec, contents);
+  release_internal_relocs (sec, internal_relocs);
+  return ok;
+}
+
+
+/* Record _all_ the relocations that point to relaxable sections, and
+   get rid of ASM_EXPAND relocs by either converting them to
+   ASM_SIMPLIFY or by removing them.  */
+
+static bfd_boolean
+collect_source_relocs (bfd *abfd,
+                      asection *sec,
+                      struct bfd_link_info *link_info)
+{
+  Elf_Internal_Rela *internal_relocs;
+  bfd_byte *contents;
+  bfd_boolean ok = TRUE;
+  unsigned i;
+  bfd_size_type sec_size;
+
+  internal_relocs = retrieve_internal_relocs (abfd, sec, 
+                                             link_info->keep_memory);
+  if (internal_relocs == NULL) 
+    return ok;
+
+  sec_size = bfd_get_section_limit (abfd, sec);
+  contents = retrieve_contents (abfd, sec, link_info->keep_memory);
+  if (contents == NULL && sec_size != 0)
+    {
+      ok = FALSE;
+      goto error_return;
+    }
+
+  /* Record relocations against relaxable literal sections.  */
+  for (i = 0; i < sec->reloc_count; i++) 
+    {
+      Elf_Internal_Rela *irel = &internal_relocs[i];
+      r_reloc r_rel;
+      asection *target_sec;
+      xtensa_relax_info *target_relax_info;
+
+      r_reloc_init (&r_rel, abfd, irel, contents, sec_size);
+
+      target_sec = r_reloc_get_section (&r_rel);
+      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))
+       {
+         xtensa_opcode opcode = XTENSA_UNDEFINED;
+         int opnd = -1;
+         bfd_boolean is_abs_literal = FALSE;
+
+         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.  However, we
+                still need to record the opcode for literal
+                coalescing.  */
+             opcode = get_relocation_opcode (abfd, sec, contents, irel);
+             if (opcode == get_l32r_opcode ())
+               {
+                 is_abs_literal = TRUE;
+                 opnd = 1;
+               }
+             else
+               opcode = XTENSA_UNDEFINED;
+           }
+         else if (is_operand_relocation (ELF32_R_TYPE (irel->r_info)))
+           {
+             opcode = get_relocation_opcode (abfd, sec, contents, irel);
+             opnd = get_relocation_opnd (opcode, ELF32_R_TYPE (irel->r_info));
+           }
+
+         if (opcode != XTENSA_UNDEFINED)
+           {
+             int src_next = target_relax_info->src_next++;
+             source_reloc *s_reloc = &target_relax_info->src_relocs[src_next];
+
+             init_source_reloc (s_reloc, sec, &r_rel, opcode, opnd,
+                                is_abs_literal);
+           }
+       }
+    }
+
+  /* Now get rid of ASM_EXPAND relocations.  At this point, the
+     src_relocs array for the target literal section may still be
+     incomplete, but it must at least contain the entries for the L32R
+     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++) 
+    {
+      Elf_Internal_Rela *irel = &internal_relocs[i];
+      bfd_boolean is_reachable;
+
+      if (!is_resolvable_asm_expansion (abfd, sec, contents, irel, link_info,
+                                       &is_reachable))
+       continue;
+
+      if (is_reachable)
+       {
+         Elf_Internal_Rela *l32r_irel;
+         r_reloc r_rel;
+         asection *target_sec;
+         xtensa_relax_info *target_relax_info;
+
+         /* Mark the source_reloc for the L32R so that it will be
+            removed in compute_removed_literals(), along with the
+            associated literal.  */
+         l32r_irel = find_associated_l32r_irel (abfd, sec, contents,
+                                                irel, internal_relocs);
+         if (l32r_irel == NULL)
+           continue;
+
+         r_reloc_init (&r_rel, abfd, l32r_irel, contents, sec_size);
+
+         target_sec = r_reloc_get_section (&r_rel);
+         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))
+           {
+             source_reloc *s_reloc;
+
+             /* Search the source_relocs for the entry corresponding to
+                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, 
+                                          target_relax_info->src_next,
+                                          sec, l32r_irel);
+             BFD_ASSERT (s_reloc);
+             s_reloc->is_null = TRUE;
+           }
+
+         /* Convert this reloc to ASM_SIMPLIFY.  */
+         irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
+                                      R_XTENSA_ASM_SIMPLIFY);
+         l32r_irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE);
+
+         pin_internal_relocs (sec, internal_relocs);
+       }
+      else
+       {
+         /* It is resolvable but doesn't reach.  We resolve now
+            by eliminating the relocation -- the call will remain
+            expanded into L32R/CALLX.  */
+         irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE);
+         pin_internal_relocs (sec, internal_relocs);
+       }
+    }
+
+ error_return:
+  release_contents (sec, contents);
+  release_internal_relocs (sec, internal_relocs);
+  return ok;
+}
+
+
+/* Return TRUE if the asm expansion can be resolved.  Generally it can
+   be resolved on a final link or when a partial link locates it in the
+   same section as the target.  Set "is_reachable" flag if the target of
+   the call is within the range of a direct call, given the current VMA
+   for this section and the target section.  */
+
+bfd_boolean
+is_resolvable_asm_expansion (bfd *abfd,
+                            asection *sec,
+                            bfd_byte *contents,
+                            Elf_Internal_Rela *irel,
+                            struct bfd_link_info *link_info,
+                            bfd_boolean *is_reachable_p)
+{
+  asection *target_sec;
+  bfd_vma target_offset;
+  r_reloc r_rel;
+  xtensa_opcode opcode, direct_call_opcode;
+  bfd_vma self_address;
+  bfd_vma dest_address;
+  bfd_boolean uses_l32r;
+  bfd_size_type sec_size;
+
+  *is_reachable_p = FALSE;
+
+  if (contents == NULL)
+    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 (link_info->relocatable
+      && (target_sec->output_section != sec->output_section
+         || is_reloc_sym_weak (abfd, irel)))
+    return FALSE;
+
+  self_address = (sec->output_section->vma
+                 + sec->output_offset + irel->r_offset + 3);
+  dest_address = (target_sec->output_section->vma
+                 + target_sec->output_offset + target_offset);
+      
+  *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;
+}
+
+
+/* The compute_text_actions function will build a list of potential
+   transformation actions for code in the extended basic block of each
+   longcall that is optimized to a direct call.  From this list we
+   generate a set of actions to actually perform that optimizes for
+   space and, if not using size_opt, maintains branch target
+   alignments.
+
+   These actions to be performed are placed on a per-section list.
+   The actual changes are performed by relax_section() in the second
+   pass.  */
+
+bfd_boolean
+compute_text_actions (bfd *abfd,
+                     asection *sec,
+                     struct bfd_link_info *link_info)
+{
+  xtensa_relax_info *relax_info;
+  bfd_byte *contents;
+  Elf_Internal_Rela *internal_relocs;
+  bfd_boolean ok = TRUE;
+  unsigned i;
+  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;
+
+  /* Do nothing if the section contains no optimized longcalls.  */
+  relax_info = get_xtensa_relax_info (sec);
+  BFD_ASSERT (relax_info);
+  if (!relax_info->is_relaxable_asm_section)
+    return ok;
+
+  internal_relocs = retrieve_internal_relocs (abfd, sec,
+                                             link_info->keep_memory);
+
+  if (internal_relocs)
+    qsort (internal_relocs, sec->reloc_count, sizeof (Elf_Internal_Rela),
+          internal_reloc_compare);
+
+  sec_size = bfd_get_section_limit (abfd, sec);
+  contents = retrieve_contents (abfd, sec, link_info->keep_memory);
+  if (contents == NULL && sec_size != 0)
+    {
+      ok = FALSE;
+      goto error_return;
+    }
+
+  ptblsize = xtensa_read_table_entries (abfd, sec, &prop_table,
+                                       XTENSA_PROP_SEC_NAME, FALSE);
+  if (ptblsize < 0)
+    {
+      ok = FALSE;
+      goto error_return;
+    }
+
+  for (i = 0; i < sec->reloc_count; i++)
+    {
+      Elf_Internal_Rela *irel = &internal_relocs[i];
+      bfd_vma r_offset;
+      property_table_entry *the_entry;
+      int ptbl_idx;
+      ebb_t *ebb;
+      ebb_constraint ebb_table;
+      bfd_size_type simplify_size;
+
+      if (irel && ELF32_R_TYPE (irel->r_info) != R_XTENSA_ASM_SIMPLIFY)
+       continue;
+      r_offset = irel->r_offset;
+
+      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);
+         continue;
+       }
+
+      /* If the instruction table is not around, then don't do this
+        relaxation.  */
+      the_entry = elf_xtensa_find_property_entry (prop_table, ptblsize,
+                                                 sec->vma + irel->r_offset);
+      if (the_entry == NULL || XTENSA_NO_NOP_REMOVAL)
+       {
+         text_action_add (&relax_info->action_list,
+                          ta_convert_longcall, sec, r_offset,
+                          0);
+         continue;
+       }
+
+      /* If the next longcall happens to be at the same address as an
+        unreachable section of size 0, then skip forward.  */
+      ptbl_idx = the_entry - prop_table;
+      while ((the_entry->flags & XTENSA_PROP_UNREACHABLE)
+            && the_entry->size == 0
+            && ptbl_idx + 1 < ptblsize
+            && (prop_table[ptbl_idx + 1].address
+                == prop_table[ptbl_idx].address))
+       {
+         ptbl_idx++;
+         the_entry++;
+       }
+
+      if (the_entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM)
+         /* NO_REORDER is OK */
+       continue;
+
+      init_ebb_constraint (&ebb_table);
+      ebb = &ebb_table.ebb;
+      init_ebb (ebb, sec, contents, sec_size, prop_table, ptblsize,
+               internal_relocs, sec->reloc_count);
+      ebb->start_offset = r_offset + simplify_size;
+      ebb->end_offset = r_offset + simplify_size;
+      ebb->start_ptbl_idx = ptbl_idx;
+      ebb->end_ptbl_idx = ptbl_idx;
+      ebb->start_reloc_idx = i;
+      ebb->end_reloc_idx = i;
+
+      if (!extend_ebb_bounds (ebb)
+         || !compute_ebb_proposed_actions (&ebb_table)
+         || !compute_ebb_actions (&ebb_table)
+         || !check_section_ebb_pcrels_fit (abfd, sec, contents,
+                                           internal_relocs, &ebb_table)
+         || !check_section_ebb_reduces (&ebb_table))
+       {
+         /* If anything goes wrong or we get unlucky and something does
+            not fit, with our plan because of expansion between
+            critical branches, just convert to a NOP.  */
+
+         text_action_add (&relax_info->action_list,
+                          ta_convert_longcall, sec, r_offset, 0);
+         i = ebb_table.ebb.end_reloc_idx;
+         free_ebb_constraint (&ebb_table);
+         continue;
+       }
+
+      text_action_add_proposed (&relax_info->action_list, &ebb_table, sec);
+
+      /* Update the index so we do not go looking at the relocations
+        we have already processed.  */
+      i = ebb_table.ebb.end_reloc_idx;
+      free_ebb_constraint (&ebb_table);
+    }
+
+#if DEBUG
+  if (relax_info->action_list.head)
+    print_action_list (stderr, &relax_info->action_list);
+#endif
+
+error_return:
+  release_contents (sec, contents);
+  release_internal_relocs (sec, internal_relocs);
+  if (prop_table)
+    free (prop_table);
+
+  return ok;
+}
+
+
+/* Find all of the possible actions for an extended basic block.  */
+
+bfd_boolean
+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;
+
+  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_size_type insn_len;
+
+      start_offset = entry->address - ebb->sec->vma;
+      end_offset = entry->address + entry->size - ebb->sec->vma;
+
+      if (entry == start_entry)
+       start_offset = ebb->start_offset;
+      if (entry == end_entry)
+       end_offset = ebb->end_offset;
+      offset = start_offset;
+
+      if (offset == entry->address - ebb->sec->vma
+         && (entry->flags & XTENSA_PROP_INSN_BRANCH_TARGET) != 0)
+       {
+         enum ebb_target_enum align_type = EBB_DESIRE_TGT_ALIGN;
+         BFD_ASSERT (offset != end_offset);
+         if (offset == end_offset)
+           return FALSE;
+
+         insn_len = insn_decode_len (ebb->contents, ebb->content_length,
+                                     offset);
+
+         /* 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;
+
+         ebb_propose_action (ebb_table, align_type, 0,
+                             ta_none, offset, 0, TRUE);
+       }
+
+      while (offset != end_offset)
+       {
+         Elf_Internal_Rela *irel;
+         xtensa_opcode opcode;
+
+         while (rel_idx < ebb->end_reloc_idx
+                && (ebb->relocs[rel_idx].r_offset < offset
+                    || (ebb->relocs[rel_idx].r_offset == offset
+                        && (ELF32_R_TYPE (ebb->relocs[rel_idx].r_info)
+                            != R_XTENSA_ASM_SIMPLIFY))))
+           rel_idx++;
+
+         /* Check for longcall.  */
+         irel = &ebb->relocs[rel_idx];
+         if (irel->r_offset == offset
+             && ELF32_R_TYPE (irel->r_info) == R_XTENSA_ASM_SIMPLIFY)
+           {
+             bfd_size_type simplify_size;
+
+             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;
+               }
+
+             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)
+           {
+             (*_bfd_error_handler)
+               (_("%B(%A+0x%lx): could not decode instruction; possible configuration mismatch"),
+                ebb->sec->owner, ebb->sec, offset);
+             return FALSE;
+           }
+           
+         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))
+           {
+             /* 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))
+           {
+             /* 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))
+           {
+             /* 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;
+       }
+    }
+
+  if (ebb->ends_unreachable)
+    {
+      ebb_propose_action (ebb_table, EBB_NO_ALIGN, 0,
+                         ta_fill, ebb->end_offset, 0, TRUE);
+    }
+
+  return TRUE;
+}
+
+
+/* After all of the information has collected about the
+   transformations possible in an EBB, compute the appropriate actions
+   here in compute_ebb_actions.  We still must check later to make
+   sure that the actions do not break any relocations.  The algorithm
+   used here is pretty greedy.  Basically, it removes as many no-ops
+   as possible so that the end of the EBB has the same alignment
+   characteristics as the original.  First, it uses narrowing, then
+   fill space at the end of the EBB, and finally widenings.  If that
+   does not work, it tries again with one fewer no-op removed.  The
+   optimization will only be performed if all of the branch targets
+   that were aligned before transformation are also aligned after the
+   transformation.
+
+   When the size_opt flag is set, ignore the branch target alignments,
+   narrow all wide instructions, and remove all no-ops unless the end
+   of the EBB prevents it.  */
+
+bfd_boolean
+compute_ebb_actions (ebb_constraint *ebb_table)
+{
+  unsigned i = 0;
+  unsigned j;
+  int removed_bytes = 0;
+  ebb_t *ebb = &ebb_table->ebb;
+  unsigned seg_idx_start = 0;
+  unsigned seg_idx_end = 0;
+
+  /* We perform this like the assembler relaxation algorithm: Start by
+     assuming all instructions are narrow and all no-ops removed; then
+     walk through....  */
+
+  /* For each segment of this that has a solid constraint, check to
+     see if there are any combinations that will keep the constraint.
+     If so, use it.  */
+  for (seg_idx_end = 0; seg_idx_end < ebb_table->action_count; seg_idx_end++)
+    {
+      bfd_boolean requires_text_end_align = FALSE;
+      unsigned longcall_count = 0;
+      unsigned longcall_convert_count = 0;
+      unsigned narrowable_count = 0;
+      unsigned narrowable_convert_count = 0;
+      unsigned widenable_count = 0;
+      unsigned widenable_convert_count = 0;
+
+      proposed_action *action = NULL;
+      int align = (1 << ebb_table->ebb.sec->alignment_power);
+
+      seg_idx_start = seg_idx_end;
+
+      for (i = seg_idx_start; i < ebb_table->action_count; i++)
+       {
+         action = &ebb_table->actions[i];
+         if (action->action == ta_convert_longcall)
+           longcall_count++;
+         if (action->action == ta_narrow_insn)
+           narrowable_count++;
+         if (action->action == ta_widen_insn)
+           widenable_count++;
+         if (action->action == ta_fill)
+           break;
+         if (action->align_type == EBB_REQUIRE_LOOP_ALIGN)
+           break;
+         if (action->align_type == EBB_REQUIRE_TGT_ALIGN
+             && !elf32xtensa_size_opt)
+           break;
+       }
+      seg_idx_end = i;
+
+      if (seg_idx_end == ebb_table->action_count && !ebb->ends_unreachable)
+       requires_text_end_align = TRUE;
+
+      if (elf32xtensa_size_opt && !requires_text_end_align
+         && action->align_type != EBB_REQUIRE_LOOP_ALIGN
+         && action->align_type != EBB_REQUIRE_TGT_ALIGN)
+       {
+         longcall_convert_count = longcall_count;
+         narrowable_convert_count = narrowable_count;
+         widenable_convert_count = 0;
+       }
+      else
+       {
+         /* There is a constraint.  Convert the max number of longcalls.  */
+         narrowable_convert_count = 0;
+         longcall_convert_count = 0;
+         widenable_convert_count = 0;
+
+         for (j = 0; j < longcall_count; j++)
+           {
+             int removed = (longcall_count - j) * 3 & (align - 1);
+             unsigned desire_narrow = (align - removed) & (align - 1);
+             unsigned desire_widen = removed;
+             if (desire_narrow <= narrowable_count)
+               {
+                 narrowable_convert_count = desire_narrow;
+                 narrowable_convert_count +=
+                   (align * ((narrowable_count - narrowable_convert_count)
+                             / align));
+                 longcall_convert_count = (longcall_count - j);
+                 widenable_convert_count = 0;
+                 break;
+               }
+             if (desire_widen <= widenable_count && !elf32xtensa_size_opt)
+               {
+                 narrowable_convert_count = 0;
+                 longcall_convert_count = longcall_count - j;
+                 widenable_convert_count = desire_widen;
+                 break;
+               }
+           }
+       }
+
+      /* Now the number of conversions are saved.  Do them.  */
+      for (i = seg_idx_start; i < seg_idx_end; i++)
+       {
+         action = &ebb_table->actions[i];
+         switch (action->action)
+           {
+           case ta_convert_longcall:
+             if (longcall_convert_count != 0)
+               {
+                 action->action = ta_remove_longcall;
+                 action->do_action = TRUE;
+                 action->removed_bytes += 3;
+                 longcall_convert_count--;
+               }
+             break;
+           case ta_narrow_insn:
+             if (narrowable_convert_count != 0)
+               {
+                 action->do_action = TRUE;
+                 action->removed_bytes += 1;
+                 narrowable_convert_count--;
+               }
+             break;
+           case ta_widen_insn:
+             if (widenable_convert_count != 0)
+               {
+                 action->do_action = TRUE;
+                 action->removed_bytes -= 1;
+                 widenable_convert_count--;
+               }
+             break;
+           default:
+             break;
+           }
+       }
+    }
+
+  /* Now we move on to some local opts.  Try to remove each of the
+     remaining longcalls.  */
+
+  if (ebb_table->ebb.ends_section || ebb_table->ebb.ends_unreachable)
+    {
+      removed_bytes = 0;
+      for (i = 0; i < ebb_table->action_count; i++)
+       {
+         int old_removed_bytes = removed_bytes;
+         proposed_action *action = &ebb_table->actions[i];
+
+         if (action->do_action && action->action == ta_convert_longcall)
+           {
+             bfd_boolean bad_alignment = FALSE;
+             removed_bytes += 3;
+             for (j = i + 1; j < ebb_table->action_count; j++)
+               {
+                 proposed_action *new_action = &ebb_table->actions[j];
+                 bfd_vma offset = new_action->offset;
+                 if (new_action->align_type == EBB_REQUIRE_TGT_ALIGN)
+                   {
+                     if (!check_branch_target_aligned
+                         (ebb_table->ebb.contents,
+                          ebb_table->ebb.content_length,
+                          offset, offset - removed_bytes))
+                       {
+                         bad_alignment = TRUE;
+                         break;
+                       }
+                   }
+                 if (new_action->align_type == EBB_REQUIRE_LOOP_ALIGN)
+                   {
+                     if (!check_loop_aligned (ebb_table->ebb.contents,
+                                              ebb_table->ebb.content_length,
+                                              offset,
+                                              offset - removed_bytes))
+                       {
+                         bad_alignment = TRUE;
+                         break;
+                       }
+                   }
+                 if (new_action->action == ta_narrow_insn
+                     && !new_action->do_action
+                     && ebb_table->ebb.sec->alignment_power == 2)
+                   {
+                     /* Narrow an instruction and we are done.  */
+                     new_action->do_action = TRUE;
+                     new_action->removed_bytes += 1;
+                     bad_alignment = FALSE;
+                     break;
+                   }
+                 if (new_action->action == ta_widen_insn
+                     && new_action->do_action
+                     && ebb_table->ebb.sec->alignment_power == 2)
+                   {
+                     /* Narrow an instruction and we are done.  */
+                     new_action->do_action = FALSE;
+                     new_action->removed_bytes += 1;
+                     bad_alignment = FALSE;
+                     break;
+                   }
+               }
+             if (!bad_alignment)
+               {
+                 action->removed_bytes += 3;
+                 action->action = ta_remove_longcall;
+                 action->do_action = TRUE;
+               }
+           }
+         removed_bytes = old_removed_bytes;
+         if (action->do_action)
+           removed_bytes += action->removed_bytes;
+       }
+    }
+
+  removed_bytes = 0;
+  for (i = 0; i < ebb_table->action_count; ++i)
+    {
+      proposed_action *action = &ebb_table->actions[i];
+      if (action->do_action)
+       removed_bytes += action->removed_bytes;
+    }
+
+  if ((removed_bytes % (1 << ebb_table->ebb.sec->alignment_power)) != 0
+      && ebb->ends_unreachable)
+    {
+      proposed_action *action;
+      int br;
+      int extra_space;
+
+      BFD_ASSERT (ebb_table->action_count != 0);
+      action = &ebb_table->actions[ebb_table->action_count - 1];
+      BFD_ASSERT (action->action == ta_fill);
+      BFD_ASSERT (ebb->ends_unreachable->flags & XTENSA_PROP_UNREACHABLE);
+
+      extra_space = compute_fill_extra_space (ebb->ends_unreachable);
+      br = action->removed_bytes + removed_bytes + extra_space;
+      br = br & ((1 << ebb->sec->alignment_power ) - 1);
+
+      action->removed_bytes = extra_space - br;
+    }
+  return TRUE;
+}
+
+
+/* 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.  */
+
+static bfd_boolean
+check_section_ebb_pcrels_fit (bfd *abfd,
+                             asection *sec,
+                             bfd_byte *contents,
+                             Elf_Internal_Rela *internal_relocs,
+                             const ebb_constraint *constraint)
+{
+  unsigned i, j;
+  Elf_Internal_Rela *irel;
+  xtensa_relax_info *relax_info;
+
+  relax_info = get_xtensa_relax_info (sec);
+
+  for (i = 0; i < sec->reloc_count; i++)
+    {
+      r_reloc r_rel;
+      bfd_vma orig_self_offset, orig_target_offset;
+      bfd_vma self_offset, target_offset;
+      int r_type;
+      reloc_howto_type *howto;
+      int self_removed_bytes, target_removed_bytes;
 
+      irel = &internal_relocs[i];
+      r_type = ELF32_R_TYPE (irel->r_info);
 
-static void
-release_internal_relocs (sec, internal_relocs)
-     asection *sec;
-     Elf_Internal_Rela *internal_relocs;
-{
-  if (internal_relocs
-      && elf_section_data (sec)->relocs != internal_relocs)
-    free (internal_relocs);
-}
+      howto = &elf_howto_table[r_type];
+      /* We maintain the required invariant: PC-relative relocations
+        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
+         || !howto->pc_relative)
+       continue;
 
+      r_reloc_init (&r_rel, abfd, irel, contents,
+                   bfd_get_section_limit (abfd, sec));
 
-static bfd_byte *
-retrieve_contents (abfd, sec, keep_memory)
-     bfd *abfd;
-     asection *sec;
-     bfd_boolean keep_memory;
-{
-  bfd_byte *contents;
+      if (r_reloc_get_section (&r_rel) != sec)
+       continue;
 
-  contents = elf_section_data (sec)->this_hdr.contents;
-  
-  if (contents == NULL && sec->size != 0)
-    {
-      if (!bfd_malloc_and_get_section (abfd, sec, &contents))
+      orig_self_offset = irel->r_offset;
+      orig_target_offset = r_rel.target_offset;
+
+      self_offset = orig_self_offset;
+      target_offset = orig_target_offset;
+
+      if (relax_info)
        {
-         if (contents != NULL)
-           free (contents);
-         return NULL;
+         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_removed_bytes = 0;
+      target_removed_bytes = 0;
+
+      for (j = 0; j < constraint->action_count; ++j)
+       {
+         proposed_action *action = &constraint->actions[j];
+         bfd_vma offset = action->offset;
+         int removed_bytes = action->removed_bytes;
+         if (offset < orig_self_offset
+             || (offset == orig_self_offset && action->action == ta_fill
+                 && action->removed_bytes < 0))
+           self_removed_bytes += removed_bytes;
+         if (offset < orig_target_offset
+             || (offset == orig_target_offset && action->action == ta_fill
+                 && action->removed_bytes < 0))
+           target_removed_bytes += removed_bytes;
+       }
+      self_offset -= self_removed_bytes;
+      target_offset -= target_removed_bytes;
+
+      /* Try to encode it.  Get the operand and check.  */
+      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;
+
+         opcode = get_relocation_opcode (abfd, sec, contents, irel);
+         if (opcode == XTENSA_UNDEFINED)
+           return FALSE;
+
+         opnum = get_relocation_opnd (opcode, ELF32_R_TYPE (irel->r_info));
+         if (opnum == XTENSA_UNDEFINED)
+           return FALSE;
+
+         if (!pcrel_reloc_fits (opcode, opnum, self_offset, target_offset))
+           return FALSE;
        }
-      if (keep_memory) 
-       elf_section_data (sec)->this_hdr.contents = contents;
     }
-  return contents;
+
+  return TRUE;
 }
 
 
-static void
-pin_contents (sec, contents)
-     asection *sec;
-     bfd_byte *contents;
+static bfd_boolean
+check_section_ebb_reduces (const ebb_constraint *constraint)
 {
-  elf_section_data (sec)->this_hdr.contents = contents;
+  int removed = 0;
+  unsigned i;
+
+  for (i = 0; i < constraint->action_count; i++)
+    {
+      const proposed_action *action = &constraint->actions[i];
+      if (action->do_action)
+       removed += action->removed_bytes;
+    }
+  if (removed < 0)
+    return FALSE;
+
+  return TRUE;
 }
 
 
-static void
-release_contents (sec, contents)
-     asection *sec;
-     bfd_byte *contents;
+void
+text_action_add_proposed (text_action_list *l,
+                         const ebb_constraint *ebb_table,
+                         asection *sec)
 {
-  if (contents && 
-      elf_section_data (sec)->this_hdr.contents != contents)
-    free (contents);
+  unsigned i;
+
+  for (i = 0; i < ebb_table->action_count; i++)
+    {
+      proposed_action *action = &ebb_table->actions[i];
+
+      if (!action->do_action)
+       continue;
+      switch (action->action)
+       {
+       case ta_remove_insn:
+       case ta_remove_longcall:
+       case ta_convert_longcall:
+       case ta_narrow_insn:
+       case ta_widen_insn:
+       case ta_fill:
+       case ta_remove_literal:
+         text_action_add (l, action->action, sec, action->offset,
+                          action->removed_bytes);
+         break;
+       case ta_none:
+         break;
+       default:
+         BFD_ASSERT (0);
+         break;
+       }
+    }
 }
 
 
-static Elf_Internal_Sym *
-retrieve_local_syms (input_bfd)
-     bfd *input_bfd;
+int
+compute_fill_extra_space (property_table_entry *entry)
 {
-  Elf_Internal_Shdr *symtab_hdr;
-  Elf_Internal_Sym *isymbuf;
-  size_t locsymcount;
-
-  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
-  locsymcount = symtab_hdr->sh_info;
+  int fill_extra_space;
 
-  isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
-  if (isymbuf == NULL && locsymcount != 0)
-    isymbuf = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0,
-                                   NULL, NULL, NULL);
+  if (!entry)
+    return 0;
 
-  /* Save the symbols for this input file so they won't be read again.  */
-  if (isymbuf && isymbuf != (Elf_Internal_Sym *) symtab_hdr->contents)
-    symtab_hdr->contents = (unsigned char *) isymbuf;
+  if ((entry->flags & XTENSA_PROP_UNREACHABLE) == 0)
+    return 0;
 
-  return isymbuf;
+  fill_extra_space = entry->size;
+  if ((entry->flags & XTENSA_PROP_ALIGN) != 0)
+    {
+      /* Fill bytes for alignment:
+        (2**n)-1 - (addr + (2**n)-1) & (2**n -1) */
+      int pow = GET_XTENSA_PROP_ALIGNMENT (entry->flags);
+      int nsm = (1 << pow) - 1;
+      bfd_vma addr = entry->address + entry->size;
+      bfd_vma align_fill = nsm - ((addr + nsm) & nsm);
+      fill_extra_space += align_fill;
+    }
+  return fill_extra_space;
 }
 
 \f
-/* Code for link-time relaxation.  */
-
-/* Local helper functions.  */
-static bfd_boolean analyze_relocations
-  PARAMS ((struct bfd_link_info *));
-static bfd_boolean find_relaxable_sections
-  PARAMS ((bfd *, asection *, struct bfd_link_info *, bfd_boolean *));
-static bfd_boolean collect_source_relocs
-  PARAMS ((bfd *, asection *, struct bfd_link_info *));
-static bfd_boolean is_resolvable_asm_expansion
-  PARAMS ((bfd *, asection *, bfd_byte *, Elf_Internal_Rela *,
-          struct bfd_link_info *, bfd_boolean *));
-static bfd_boolean remove_literals
-  PARAMS ((bfd *, asection *, struct bfd_link_info *, value_map_hash_table *));
-static bfd_boolean relax_section
-  PARAMS ((bfd *, asection *, struct bfd_link_info *));
-static bfd_boolean relax_property_section
-  PARAMS ((bfd *, asection *, struct bfd_link_info *));
-static bfd_boolean relax_section_symbols
-  PARAMS ((bfd *, asection *));
-static bfd_boolean relocations_reach
-  PARAMS ((source_reloc *, int, const r_reloc *));
-static void translate_reloc
-  PARAMS ((const r_reloc *, r_reloc *));
-static Elf_Internal_Rela *get_irel_at_offset
-  PARAMS ((asection *, Elf_Internal_Rela *, bfd_vma));
-static Elf_Internal_Rela *find_associated_l32r_irel
-  PARAMS ((asection *, bfd_byte *, Elf_Internal_Rela *,
-          Elf_Internal_Rela *));
-static void shrink_dynamic_reloc_sections
-  PARAMS ((struct bfd_link_info *, bfd *, asection *, Elf_Internal_Rela *));
+/* First relaxation pass.  */
 
+/* If the section contains relaxable literals, check each literal to
+   see if it has the same value as another literal that has already
+   been seen, either in the current section or a previous one.  If so,
+   add an entry to the per-section list of removed literals.  The
+   actual changes are deferred until the next pass.  */
 
 static bfd_boolean 
-elf_xtensa_relax_section (abfd, sec, link_info, again)
-     bfd *abfd;
-     asection *sec;
-     struct bfd_link_info *link_info;
-     bfd_boolean *again;
+compute_removed_literals (bfd *abfd,
+                         asection *sec,
+                         struct bfd_link_info *link_info,
+                         value_map_hash_table *values)
 {
-  static value_map_hash_table *values = NULL;
   xtensa_relax_info *relax_info;
+  bfd_byte *contents;
+  Elf_Internal_Rela *internal_relocs;
+  source_reloc *src_relocs, *rel;
+  bfd_boolean ok = TRUE;
+  property_table_entry *prop_table = NULL;
+  int ptblsize;
+  int i, prev_i;
+  bfd_boolean last_loc_is_prev = FALSE;
+  bfd_vma last_target_offset = 0;
+  section_cache_t target_sec_cache;
+  bfd_size_type sec_size;
+
+  init_section_cache (&target_sec_cache);
+
+  /* Do nothing if it is not a relaxable literal section.  */
+  relax_info = get_xtensa_relax_info (sec);
+  BFD_ASSERT (relax_info);
+  if (!relax_info->is_relaxable_literal_section)
+    return ok;
+
+  internal_relocs = retrieve_internal_relocs (abfd, sec, 
+                                             link_info->keep_memory);
 
-  if (!values)
+  sec_size = bfd_get_section_limit (abfd, sec);
+  contents = retrieve_contents (abfd, sec, link_info->keep_memory);
+  if (contents == NULL && sec_size != 0)
     {
-      /* Do some overall initialization for relaxation.  */
-      values = value_map_hash_table_init ();
-      relaxing_section = TRUE;
-      if (!analyze_relocations (link_info))
-       return FALSE;
+      ok = FALSE;
+      goto error_return;
     }
-  *again = FALSE;
 
-  /* Don't mess with linker-created sections.  */
-  if ((sec->flags & SEC_LINKER_CREATED) != 0)
-    return TRUE;
+  /* Sort the source_relocs by target offset.  */
+  src_relocs = relax_info->src_relocs;
+  qsort (src_relocs, relax_info->src_count,
+        sizeof (source_reloc), source_reloc_compare);
+  qsort (internal_relocs, sec->reloc_count, sizeof (Elf_Internal_Rela),
+        internal_reloc_compare);
 
-  relax_info = get_xtensa_relax_info (sec);
-  BFD_ASSERT (relax_info != NULL);
+  ptblsize = xtensa_read_table_entries (abfd, sec, &prop_table,
+                                       XTENSA_PROP_SEC_NAME, FALSE);
+  if (ptblsize < 0)
+    {
+      ok = FALSE;
+      goto error_return;
+    }
 
-  switch (relax_info->visited)
+  prev_i = -1;
+  for (i = 0; i < relax_info->src_count; i++)
     {
-    case 0:
-      /* Note: It would be nice to fold this pass into
-        analyze_relocations, but it is important for this step that the
-        sections be examined in link order.  */
-      if (!remove_literals (abfd, sec, link_info, values))
-       return FALSE;
-      *again = TRUE;
-      break;
+      Elf_Internal_Rela *irel = NULL;
 
-    case 1:
-      if (!relax_section (abfd, sec, link_info))
-       return FALSE;
-      *again = TRUE;
-      break;
+      rel = &src_relocs[i];
+      if (get_l32r_opcode () != rel->opcode)
+       continue;
+      irel = get_irel_at_offset (sec, internal_relocs,
+                                rel->r_rel.target_offset);
 
-    case 2:
-      if (!relax_section_symbols (abfd, sec))
-       return FALSE;
-      break;
+      /* If the relocation on this is not a simple R_XTENSA_32 or
+        R_XTENSA_PLT then do not consider it.  This may happen when
+        the difference of two symbols is used in a literal.  */
+      if (irel && (ELF32_R_TYPE (irel->r_info) != R_XTENSA_32
+                  && ELF32_R_TYPE (irel->r_info) != R_XTENSA_PLT))
+       continue;
+
+      /* If the target_offset for this relocation is the same as the
+        previous relocation, then we've already considered whether the
+        literal can be coalesced.  Skip to the next one....  */
+      if (i != 0 && prev_i != -1
+         && src_relocs[i-1].r_rel.target_offset == rel->r_rel.target_offset)
+       continue;
+      prev_i = i;
+
+      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 (!remove_dead_literal (abfd, sec, link_info, internal_relocs,
+                                   irel, rel, prop_table, ptblsize))
+           {
+             ok = FALSE;
+             goto error_return;
+           }
+         last_target_offset = rel->r_rel.target_offset;
+         continue;
+       }
+
+      if (!identify_literal_placement (abfd, sec, contents, link_info,
+                                      values, 
+                                      &last_loc_is_prev, irel, 
+                                      relax_info->src_count - i, rel,
+                                      prop_table, ptblsize,
+                                      &target_sec_cache, rel->is_abs_literal))
+       {
+         ok = FALSE;
+         goto error_return;
+       }
+      last_target_offset = rel->r_rel.target_offset;
     }
 
-  relax_info->visited++;
-  return TRUE;
-}
+#if DEBUG
+  print_removed_literals (stderr, &relax_info->removed_list);
+  print_action_list (stderr, &relax_info->action_list);
+#endif /* DEBUG */
 
-/* Initialization for relaxation.  */
+error_return:
+  if (prop_table) free (prop_table);
+  clear_section_cache (&target_sec_cache);
 
-/* This function is called once at the start of relaxation.  It scans
-   all the input sections and marks the ones that are relaxable (i.e.,
-   literal sections with L32R relocations against them).  It then
-   collect source_reloc information for all the relocations against
-   those relaxable sections.  */
+  release_contents (sec, contents);
+  release_internal_relocs (sec, internal_relocs);
+  return ok;
+}
 
-static bfd_boolean
-analyze_relocations (link_info)
-     struct bfd_link_info *link_info;
-{
-  bfd *abfd;
-  asection *sec;
-  bfd_boolean is_relaxable = FALSE;
 
-  /* Initialize the per-section relaxation info.  */
-  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);
-      }
+static Elf_Internal_Rela *
+get_irel_at_offset (asection *sec,
+                   Elf_Internal_Rela *internal_relocs,
+                   bfd_vma offset)
+{
+  unsigned i;
+  Elf_Internal_Rela *irel;
+  unsigned r_type;
+  Elf_Internal_Rela key;
 
-  /* Mark relaxable sections (and count relocations against each one).  */
-  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))
-         return FALSE;
-      }
+  if (!internal_relocs) 
+    return NULL;
 
-  /* Bail out if there are no relaxable sections.  */
-  if (!is_relaxable)
-    return TRUE;
+  key.r_offset = offset;
+  irel = bsearch (&key, internal_relocs, sec->reloc_count,
+                 sizeof (Elf_Internal_Rela), internal_reloc_matches);
+  if (!irel)
+    return NULL;
 
-  /* Allocate space for source_relocs.  */
-  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;
+  /* bsearch does not guarantee which will be returned if there are
+     multiple matches.  We need the first that is not an alignment.  */
+  i = irel - internal_relocs;
+  while (i > 0)
+    {
+      if (internal_relocs[i-1].r_offset != offset)
+       break;
+      i--;
+    }
+  for ( ; i < sec->reloc_count; i++)
+    {
+      irel = &internal_relocs[i];
+      r_type = ELF32_R_TYPE (irel->r_info);
+      if (irel->r_offset == offset && r_type != R_XTENSA_NONE)
+       return irel;
+    }
 
-       relax_info = get_xtensa_relax_info (sec);
-       if (relax_info->is_relaxable_literal_section)
-         {
-           relax_info->src_relocs = (source_reloc *)
-             bfd_malloc (relax_info->src_count * sizeof (source_reloc));
-         }
-      }
+  return NULL;
+}
 
-  /* Collect info on relocations against each relaxable section.  */
-  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))
-         return FALSE;
-      }
 
+bfd_boolean
+is_removable_literal (const source_reloc *rel,
+                     int i,
+                     const source_reloc *src_relocs,
+                     int src_count)
+{
+  const source_reloc *curr_rel;
+  if (!rel->is_null)
+    return FALSE;
+  
+  for (++i; i < src_count; ++i)
+    {
+      curr_rel = &src_relocs[i];
+      /* If all others have the same target offset....  */
+      if (curr_rel->r_rel.target_offset != rel->r_rel.target_offset)
+       return TRUE;
+
+      if (!curr_rel->is_null
+         && !xtensa_is_property_section (curr_rel->source_sec)
+         && !(curr_rel->source_sec->flags & SEC_DEBUGGING))
+       return FALSE;
+    }
   return TRUE;
 }
 
 
-/* Find all the literal sections that might be relaxed.  The motivation
-   for this pass is that collect_source_relocs() needs to record _all_
-   the relocations that target each relaxable section.  That is
-   expensive and unnecessary unless the target section is actually going
-   to be relaxed.  This pass identifies all such sections by checking if
-   they have L32Rs pointing to them.  In the process, the total number
-   of relocations targeting each section is also counted so that we
-   know how much space to allocate for source_relocs against each
-   relaxable literal section.  */
-
-static bfd_boolean
-find_relaxable_sections (abfd, sec, link_info, is_relaxable_p)
-     bfd *abfd;
-     asection *sec;
-     struct bfd_link_info *link_info;
-     bfd_boolean *is_relaxable_p;
+bfd_boolean 
+remove_dead_literal (bfd *abfd,
+                    asection *sec,
+                    struct bfd_link_info *link_info,
+                    Elf_Internal_Rela *internal_relocs,
+                    Elf_Internal_Rela *irel,
+                    source_reloc *rel,
+                    property_table_entry *prop_table,
+                    int ptblsize)
 {
-  Elf_Internal_Rela *internal_relocs;
-  bfd_byte *contents;
-  bfd_boolean ok = TRUE;
-  unsigned i;
+  property_table_entry *entry;
+  xtensa_relax_info *relax_info;
 
-  internal_relocs = retrieve_internal_relocs (abfd, sec,
-                                             link_info->keep_memory);
-  if (internal_relocs == NULL) 
-    return ok;
+  relax_info = get_xtensa_relax_info (sec);
+  if (!relax_info)
+    return FALSE;
 
-  contents = retrieve_contents (abfd, sec, link_info->keep_memory);
-  if (contents == NULL && sec->size != 0)
+  entry = elf_xtensa_find_property_entry (prop_table, ptblsize,
+                                         sec->vma + rel->r_rel.target_offset);
+
+  /* Mark the unused literal so that it will be removed.  */
+  add_removed_literal (&relax_info->removed_list, &rel->r_rel, NULL);
+
+  text_action_add (&relax_info->action_list,
+                  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) 
     {
-      ok = FALSE;
-      goto error_return;
+      int fill_extra_space;
+      bfd_vma entry_sec_offset;
+      text_action *fa;
+      property_table_entry *the_add_entry;
+      int removed_diff;
+
+      if (entry)
+       entry_sec_offset = entry->address - sec->vma + entry->size;
+      else
+       entry_sec_offset = rel->r_rel.target_offset + 4;
+
+      /* If the literal range is at the end of the section,
+        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);
+
+      fa = find_fill_action (&relax_info->action_list, sec, entry_sec_offset);
+      removed_diff = compute_removed_action_diff (fa, sec, entry_sec_offset,
+                                                 -4, fill_extra_space);
+      if (fa)
+       adjust_fill_action (fa, removed_diff);
+      else
+       text_action_add (&relax_info->action_list,
+                        ta_fill, sec, entry_sec_offset, removed_diff);
     }
 
-  for (i = 0; i < sec->reloc_count; i++) 
+  /* Zero out the relocation on this literal location.  */
+  if (irel)
     {
-      Elf_Internal_Rela *irel = &internal_relocs[i];
-      r_reloc r_rel;
-      asection *target_sec;
-      xtensa_relax_info *target_relax_info;
+      if (elf_hash_table (link_info)->dynamic_sections_created)
+       shrink_dynamic_reloc_sections (link_info, abfd, sec, irel);
 
-      r_reloc_init (&r_rel, abfd, irel);
+      irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE);
+      pin_internal_relocs (sec, internal_relocs);
+    }
 
-      target_sec = r_reloc_get_section (&r_rel);
-      target_relax_info = get_xtensa_relax_info (target_sec);
-      if (!target_relax_info)
-       continue;
+  /* Do not modify "last_loc_is_prev".  */
+  return TRUE;
+}
 
-      /* Count relocations against the target section.  */
-      target_relax_info->src_count++;
 
-      if (is_literal_section (target_sec)
-         && is_l32r_relocation (sec, contents, irel)
-         && r_reloc_is_defined (&r_rel))
+bfd_boolean 
+identify_literal_placement (bfd *abfd,
+                           asection *sec,
+                           bfd_byte *contents,
+                           struct bfd_link_info *link_info,
+                           value_map_hash_table *values,
+                           bfd_boolean *last_loc_is_prev_p,
+                           Elf_Internal_Rela *irel,
+                           int remaining_src_rels,
+                           source_reloc *rel,
+                           property_table_entry *prop_table,
+                           int ptblsize,
+                           section_cache_t *target_sec_cache,
+                           bfd_boolean is_abs_literal)
+{
+  literal_value val;
+  value_map *val_map;
+  xtensa_relax_info *relax_info;
+  bfd_boolean literal_placed = FALSE;
+  r_reloc r_rel;
+  unsigned long value;
+  bfd_boolean final_static_link;
+  bfd_size_type sec_size;
+
+  relax_info = get_xtensa_relax_info (sec);
+  if (!relax_info)
+    return FALSE;
+
+  sec_size = bfd_get_section_limit (abfd, sec);
+
+  final_static_link =
+    (!link_info->relocatable
+     && !elf_hash_table (link_info)->dynamic_sections_created);
+
+  /* The placement algorithm first checks to see if the literal is
+     already in the value map.  If so and the value map is reachable
+     from all uses, then the literal is moved to that location.  If
+     not, then we identify the last location where a fresh literal was
+     placed.  If the literal can be safely moved there, then we do so.
+     If not, then we assume that the literal is not to move and leave
+     the literal where it is, marking it as the last literal
+     location.  */
+
+  /* Find the literal value.  */
+  value = 0;
+  r_reloc_init (&r_rel, abfd, irel, contents, sec_size);
+  if (!irel)
+    {
+      BFD_ASSERT (rel->r_rel.target_offset < sec_size);
+      value = bfd_get_32 (abfd, contents + rel->r_rel.target_offset);
+    }
+  init_literal_value (&val, &r_rel, value, is_abs_literal);
+
+  /* Check if we've seen another literal with the same value that
+     is in the same output section.  */
+  val_map = value_map_get_cached_value (values, &val, final_static_link);
+
+  if (val_map
+      && (r_reloc_get_section (&val_map->loc)->output_section
+         == sec->output_section)
+      && relocations_reach (rel, remaining_src_rels, &val_map->loc)
+      && coalesce_shared_literal (sec, rel, prop_table, ptblsize, val_map))
+    {
+      /* No change to last_loc_is_prev.  */
+      literal_placed = TRUE;
+    }
+
+  /* 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 
+      && values->has_last_loc && !(*last_loc_is_prev_p))
+    {
+      asection *target_sec = r_reloc_get_section (&values->last_loc);
+      if (target_sec && target_sec->output_section == sec->output_section)
        {
-         /* Mark the target section as relaxable.  */
-         target_relax_info->is_relaxable_literal_section = TRUE;
-         *is_relaxable_p = TRUE;
+         /* Increment the virtual offset.  */
+         r_reloc try_loc = values->last_loc;
+         try_loc.virtual_offset += 4;
+
+         /* 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, 
+                                     &try_loc, &val, target_sec_cache))
+           {
+             values->last_loc.virtual_offset += 4;
+             literal_placed = TRUE;
+             if (!val_map)
+               val_map = add_value_map (values, &val, &try_loc,
+                                        final_static_link);
+             else
+               val_map->loc = try_loc;
+           }
        }
     }
 
- error_return:
-  release_contents (sec, contents);
-  release_internal_relocs (sec, internal_relocs);
-  return ok;
+  if (!literal_placed)
+    {
+      /* Nothing worked, leave the literal alone but update the last loc.  */
+      values->has_last_loc = TRUE;
+      values->last_loc = rel->r_rel;
+      if (!val_map)
+       val_map = add_value_map (values, &val, &rel->r_rel, final_static_link);
+      else
+       val_map->loc = rel->r_rel;
+      *last_loc_is_prev_p = TRUE;
+    }
+
+  return TRUE;
 }
 
 
-/* Record _all_ the relocations that point to relaxable literal
-   sections, and get rid of ASM_EXPAND relocs by either converting them
-   to ASM_SIMPLIFY or by removing them.  */
+/* Check if the original relocations (presumably on L32R instructions)
+   identified by reloc[0..N] can be changed to reference the literal
+   identified by r_rel.  If r_rel is out of range for any of the
+   original relocations, then we don't want to coalesce the original
+   literal with the one at r_rel.  We only check reloc[0..N], where the
+   offsets are all the same as for reloc[0] (i.e., they're all
+   referencing the same literal) and where N is also bounded by the
+   number of remaining entries in the "reloc" array.  The "reloc" array
+   is sorted by target offset so we know all the entries for the same
+   literal will be contiguous.  */
 
 static bfd_boolean
-collect_source_relocs (abfd, sec, link_info)
-     bfd *abfd;
-     asection *sec;
-     struct bfd_link_info *link_info;
+relocations_reach (source_reloc *reloc,
+                  int remaining_relocs,
+                  const r_reloc *r_rel)
 {
-  Elf_Internal_Rela *internal_relocs;
-  bfd_byte *contents;
-  bfd_boolean ok = TRUE;
-  unsigned i;
+  bfd_vma from_offset, source_address, dest_address;
+  asection *sec;
+  int i;
 
-  internal_relocs = retrieve_internal_relocs (abfd, sec, 
-                                             link_info->keep_memory);
-  if (internal_relocs == NULL) 
-    return ok;
+  if (!r_reloc_is_defined (r_rel))
+    return FALSE;
 
-  contents = retrieve_contents (abfd, sec, link_info->keep_memory);
-  if (contents == NULL && sec->size != 0)
-    {
-      ok = FALSE;
-      goto error_return;
-    }
+  sec = r_reloc_get_section (r_rel);
+  from_offset = reloc[0].r_rel.target_offset;
 
-  /* Record relocations against relaxable literal sections.  */
-  for (i = 0; i < sec->reloc_count; i++) 
+  for (i = 0; i < remaining_relocs; i++)
     {
-      Elf_Internal_Rela *irel = &internal_relocs[i];
-      r_reloc r_rel;
-      asection *target_sec;
-      xtensa_relax_info *target_relax_info;
+      if (reloc[i].r_rel.target_offset != from_offset)
+       break;
 
-      r_reloc_init (&r_rel, abfd, irel);
+      /* Ignore relocations that have been removed.  */
+      if (reloc[i].is_null)
+       continue;
 
-      target_sec = r_reloc_get_section (&r_rel);
-      target_relax_info = get_xtensa_relax_info (target_sec);
+      /* The original and new output section for these must be the same
+         in order to coalesce.  */
+      if (r_reloc_get_section (&reloc[i].r_rel)->output_section
+         != sec->output_section)
+       return FALSE;
 
-      if (target_relax_info
-         && target_relax_info->is_relaxable_literal_section)
+      /* A literal with no PC-relative relocations can be moved anywhere.  */
+      if (reloc[i].opnd != -1)
        {
-         xtensa_opcode opcode;
-         xtensa_operand opnd;
-         source_reloc *s_reloc;
-         int src_next;
-
-         src_next = target_relax_info->src_next++;
-         s_reloc = &target_relax_info->src_relocs[src_next];
-
-         opcode = get_relocation_opcode (sec, contents, irel);
-         if (opcode == XTENSA_UNDEFINED)
-           opnd = NULL;
-         else
-           opnd = xtensa_get_operand (xtensa_default_isa, opcode,
-                                      get_relocation_opnd (irel));
+         /* Otherwise, check to see that it fits.  */
+         source_address = (reloc[i].source_sec->output_section->vma
+                           + reloc[i].source_sec->output_offset
+                           + reloc[i].r_rel.rela.r_offset);
+         dest_address = (sec->output_section->vma
+                         + sec->output_offset
+                         + r_rel->target_offset);
 
-         init_source_reloc (s_reloc, sec, &r_rel, opnd);
+         if (!pcrel_reloc_fits (reloc[i].opcode, reloc[i].opnd,
+                                source_address, dest_address))
+           return FALSE;
        }
     }
 
-  /* Now get rid of ASM_EXPAND relocations.  At this point, the
-     src_relocs array for the target literal section may still be
-     incomplete, but it must at least contain the entries for the L32R
-     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++) 
-    {
-      Elf_Internal_Rela *irel = &internal_relocs[i];
-      bfd_boolean is_reachable;
+  return TRUE;
+}
 
-      if (!is_resolvable_asm_expansion (abfd, sec, contents, irel, link_info,
-                                       &is_reachable))
-       continue;
 
-      if (is_reachable)
-       {
-         Elf_Internal_Rela *l32r_irel;
-         r_reloc r_rel;
-         asection *target_sec;
-         xtensa_relax_info *target_relax_info;
+/* Move a literal to another literal location because it is
+   the same as the other literal value.  */
 
-         /* Mark the source_reloc for the L32R so that it will be
-            removed in remove_literals(), along with the associated
-            literal.  */
-         l32r_irel = find_associated_l32r_irel (sec, contents,
-                                                irel, internal_relocs);
-         if (l32r_irel == NULL)
-           continue;
+static bfd_boolean 
+coalesce_shared_literal (asection *sec,
+                        source_reloc *rel,
+                        property_table_entry *prop_table,
+                        int ptblsize,
+                        value_map *val_map)
+{
+  property_table_entry *entry;
+  text_action *fa;
+  property_table_entry *the_add_entry;
+  int removed_diff;
+  xtensa_relax_info *relax_info;
 
-         r_reloc_init (&r_rel, abfd, l32r_irel);
+  relax_info = get_xtensa_relax_info (sec);
+  if (!relax_info)
+    return FALSE;
 
-         target_sec = r_reloc_get_section (&r_rel);
-         target_relax_info = get_xtensa_relax_info (target_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))
+    return TRUE;
 
-         if (target_relax_info
-             && target_relax_info->is_relaxable_literal_section)
-           {
-             source_reloc *s_reloc;
+  /* Mark that the literal will be coalesced.  */
+  add_removed_literal (&relax_info->removed_list, &rel->r_rel, &val_map->loc);
 
-             /* Search the source_relocs for the entry corresponding to
-                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, 
-                                          target_relax_info->src_next,
-                                          sec, l32r_irel);
-             BFD_ASSERT (s_reloc);
-             s_reloc->is_null = TRUE;
-           }
+  text_action_add (&relax_info->action_list,
+                  ta_remove_literal, sec, rel->r_rel.target_offset, 4);
 
-         /* Convert this reloc to ASM_SIMPLIFY.  */
-         irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
-                                      R_XTENSA_ASM_SIMPLIFY);
-         l32r_irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE);
+  /* If the section is 4-byte aligned, do not add fill.  */
+  if (sec->alignment_power > 2) 
+    {
+      int fill_extra_space;
+      bfd_vma entry_sec_offset;
 
-         pin_internal_relocs (sec, internal_relocs);
-       }
+      if (entry)
+       entry_sec_offset = entry->address - sec->vma + entry->size;
       else
-       {
-         /* It is resolvable but doesn't reach.  We resolve now
-            by eliminating the relocation -- the call will remain
-            expanded into L32R/CALLX.  */
-         irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE);
-         pin_internal_relocs (sec, internal_relocs);
-       }
+       entry_sec_offset = rel->r_rel.target_offset + 4;
+
+      /* If the literal range is at the end of the section,
+        do not add fill.  */
+      fill_extra_space = 0;
+      the_add_entry = elf_xtensa_find_property_entry (prop_table, ptblsize,
+                                                     entry_sec_offset);
+      if (the_add_entry && (the_add_entry->flags & XTENSA_PROP_UNREACHABLE))
+       fill_extra_space = the_add_entry->size;
+
+      fa = find_fill_action (&relax_info->action_list, sec, entry_sec_offset);
+      removed_diff = compute_removed_action_diff (fa, sec, entry_sec_offset,
+                                                 -4, fill_extra_space);
+      if (fa)
+       adjust_fill_action (fa, removed_diff);
+      else
+       text_action_add (&relax_info->action_list,
+                        ta_fill, sec, entry_sec_offset, removed_diff);
     }
 
- error_return:
-  release_contents (sec, contents);
-  release_internal_relocs (sec, internal_relocs);
-  return ok;
+  return TRUE;
 }
 
 
-/* Return TRUE if the asm expansion can be resolved.  Generally it can
-   be resolved on a final link or when a partial link locates it in the
-   same section as the target.  Set "is_reachable" flag if the target of
-   the call is within the range of a direct call, given the current VMA
-   for this section and the target section.  */
+/* Move a literal to another location.  This may actually increase the
+   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.  */
 
-bfd_boolean
-is_resolvable_asm_expansion (abfd, sec, contents, irel, link_info,
-                            is_reachable_p)
-     bfd *abfd;
-     asection *sec;
-     bfd_byte *contents;
-     Elf_Internal_Rela *irel;
-     struct bfd_link_info *link_info;
-     bfd_boolean *is_reachable_p;
+static bfd_boolean 
+move_shared_literal (asection *sec,
+                    struct bfd_link_info *link_info,
+                    source_reloc *rel,
+                    property_table_entry *prop_table,
+                    int ptblsize,
+                    const r_reloc *target_loc,
+                    const literal_value *lit_value,
+                    section_cache_t *target_sec_cache)
 {
+  property_table_entry *the_add_entry, *src_entry, *target_entry = NULL;
+  text_action *fa, *target_fa;
+  int removed_diff;
+  xtensa_relax_info *relax_info, *target_relax_info;
   asection *target_sec;
-  bfd_vma target_offset;
-  r_reloc r_rel;
-  xtensa_opcode opcode, direct_call_opcode;
-  bfd_vma self_address;
-  bfd_vma dest_address;
+  ebb_t *ebb;
+  ebb_constraint ebb_table;
+  bfd_boolean relocs_fit;
 
-  *is_reachable_p = FALSE;
-
-  if (contents == NULL)
+  /* If this routine always returns FALSE, the literals that cannot be
+     coalesced will not be moved.  */
+  if (elf32xtensa_no_literal_movement)
     return FALSE;
 
-  if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_ASM_EXPAND) 
-    return FALSE;
-  
-  opcode = get_expanded_call_opcode (contents + irel->r_offset,
-                                    sec->size - irel->r_offset);
-  
-  direct_call_opcode = swap_callx_for_call_opcode (opcode);
-  if (direct_call_opcode == XTENSA_UNDEFINED)
+  relax_info = get_xtensa_relax_info (sec);
+  if (!relax_info)
     return FALSE;
 
-  /* Check and see that the target resolves.  */
-  r_reloc_init (&r_rel, abfd, irel);
-  if (!r_reloc_is_defined (&r_rel))
+  target_sec = r_reloc_get_section (target_loc);
+  target_relax_info = get_xtensa_relax_info (target_sec);
+
+  /* Literals to undefined sections may not be moved because they
+     must report an error.  */
+  if (bfd_is_und_section (target_sec))
     return FALSE;
 
-  target_sec = r_reloc_get_section (&r_rel);
-  target_offset = r_reloc_get_target_offset (&r_rel);
+  src_entry = elf_xtensa_find_property_entry
+    (prop_table, ptblsize, sec->vma + rel->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 (link_info->relocatable
-      && (target_sec->output_section != sec->output_section))
+  if (!section_cache_section (target_sec_cache, target_sec, link_info))
     return FALSE;
 
-  self_address = (sec->output_section->vma
-                 + sec->output_offset + irel->r_offset + 3);
-  dest_address = (target_sec->output_section->vma
-                 + target_sec->output_offset + target_offset);
-      
-  *is_reachable_p = pcrel_reloc_fits
-    (xtensa_get_operand (xtensa_default_isa, direct_call_opcode, 0),
-     self_address, dest_address);
+  target_entry = elf_xtensa_find_property_entry
+    (target_sec_cache->ptbl, target_sec_cache->pte_count, 
+     target_sec->vma + target_loc->target_offset);
+
+  if (!target_entry)
+    return FALSE;
 
-  if ((self_address >> CALL_SEGMENT_BITS) !=
-      (dest_address >> CALL_SEGMENT_BITS))
+  /* Make sure that we have not broken any branches.  */
+  relocs_fit = FALSE;
+
+  init_ebb_constraint (&ebb_table);
+  ebb = &ebb_table.ebb;
+  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);
+
+  /* Propose to add 4 bytes + worst-case alignment size increase to
+     destination.  */
+  ebb_propose_action (&ebb_table, EBB_NO_ALIGN, 0,
+                     ta_fill, target_loc->target_offset,
+                     -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, 
+                                            target_sec_cache->contents,
+                                            target_sec_cache->relocs,
+                                            &ebb_table);
+
+  if (!relocs_fit) 
     return FALSE;
 
-  return TRUE;
-}
+  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) 
+    {
+      /* May need to add or remove some fill to maintain alignment.  */
+      int fill_extra_space;
+      bfd_vma 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,
+        do not add fill.  */
+      fill_extra_space = 0;
+      the_add_entry =
+       elf_xtensa_find_property_entry (target_sec_cache->ptbl,
+                                       target_sec_cache->pte_count,
+                                       entry_sec_offset);
+      if (the_add_entry && (the_add_entry->flags & XTENSA_PROP_UNREACHABLE))
+       fill_extra_space = the_add_entry->size;
+
+      target_fa = find_fill_action (&target_relax_info->action_list,
+                                   target_sec, entry_sec_offset);
+      removed_diff = compute_removed_action_diff (target_fa, target_sec,
+                                                 entry_sec_offset, 4,
+                                                 fill_extra_space);
+      if (target_fa)
+       adjust_fill_action (target_fa, removed_diff);
+      else
+       text_action_add (&target_relax_info->action_list,
+                        ta_fill, target_sec, entry_sec_offset, removed_diff);
+    }
 
+  /* Mark that the literal will be moved to the new location.  */
+  add_removed_literal (&relax_info->removed_list, &rel->r_rel, target_loc);
 
-static Elf_Internal_Rela *
-find_associated_l32r_irel (sec, contents, other_irel, internal_relocs)
-     asection *sec;
-     bfd_byte *contents;
-     Elf_Internal_Rela *other_irel;
-     Elf_Internal_Rela *internal_relocs;
-{
-  unsigned i;
+  /* Remove the literal.  */
+  text_action_add (&relax_info->action_list,
+                  ta_remove_literal, sec, rel->r_rel.target_offset, 4);
 
-  for (i = 0; i < sec->reloc_count; i++) 
+  /* If the section is 4-byte aligned, do not add fill.  */
+  if (sec->alignment_power > 2 && target_entry != src_entry) 
     {
-      Elf_Internal_Rela *irel = &internal_relocs[i];
+      int fill_extra_space;
+      bfd_vma entry_sec_offset;
 
-      if (irel == other_irel)
-       continue;
-      if (irel->r_offset != other_irel->r_offset)
-       continue;
-      if (is_l32r_relocation (sec, contents, irel))
-       return irel;
+      if (src_entry)
+       entry_sec_offset = src_entry->address - sec->vma + src_entry->size;
+      else
+       entry_sec_offset = rel->r_rel.target_offset+4;
+
+      /* If the literal range is at the end of the section,
+        do not add fill.  */
+      fill_extra_space = 0;
+      the_add_entry = elf_xtensa_find_property_entry (prop_table, ptblsize,
+                                                     entry_sec_offset);
+      if (the_add_entry && (the_add_entry->flags & XTENSA_PROP_UNREACHABLE))
+       fill_extra_space = the_add_entry->size;
+
+      fa = find_fill_action (&relax_info->action_list, sec, entry_sec_offset);
+      removed_diff = compute_removed_action_diff (fa, sec, entry_sec_offset,
+                                                 -4, fill_extra_space);
+      if (fa)
+       adjust_fill_action (fa, removed_diff);
+      else
+       text_action_add (&relax_info->action_list,
+                        ta_fill, sec, entry_sec_offset, removed_diff);
     }
 
-  return NULL;
+  return TRUE;
 }
 
-/* First relaxation pass.  */
+\f
+/* Second relaxation pass.  */
 
-/* If the section is relaxable (i.e., a literal section), check each
-   literal to see if it has the same value as another literal that has
-   already been seen, either in the current section or a previous one.
-   If so, add an entry to the per-section list of removed literals.  The
-   actual changes are deferred until the next pass.  */
+/* 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.  */
 
-static bfd_boolean 
-remove_literals (abfd, sec, link_info, values)
-     bfd *abfd;
-     asection *sec;
-     struct bfd_link_info *link_info;
-     value_map_hash_table *values;
+bfd_boolean
+relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
 {
+  Elf_Internal_Rela *internal_relocs;
   xtensa_relax_info *relax_info;
   bfd_byte *contents;
-  Elf_Internal_Rela *internal_relocs;
-  source_reloc *src_relocs;
-  bfd_boolean final_static_link;
   bfd_boolean ok = TRUE;
-  int i;
+  unsigned i;
+  bfd_boolean rv = FALSE;
+  bfd_boolean virtual_action;
+  bfd_size_type sec_size;
 
-  /* Do nothing if it is not a relaxable literal section.  */
+  sec_size = bfd_get_section_limit (abfd, sec);
   relax_info = get_xtensa_relax_info (sec);
   BFD_ASSERT (relax_info);
 
-  if (!relax_info->is_relaxable_literal_section)
-    return ok;
+  /* First translate any of the fixes that have been added already.  */
+  translate_section_fixes (sec);
+
+  /* Handle property sections (e.g., literal tables) specially.  */
+  if (xtensa_is_property_section (sec))
+    {
+      BFD_ASSERT (!relax_info->is_relaxable_literal_section);
+      return relax_property_section (abfd, sec, link_info);
+    }
 
   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)
+  if (contents == NULL && sec_size != 0)
     {
       ok = FALSE;
       goto error_return;
     }
 
-  final_static_link =
-    (!link_info->relocatable
-     && !elf_hash_table (link_info)->dynamic_sections_created);
+  if (internal_relocs)
+    {
+      for (i = 0; i < sec->reloc_count; i++)
+       {
+         Elf_Internal_Rela *irel;
+         xtensa_relax_info *target_relax_info;
+         bfd_vma source_offset, old_source_offset;
+         r_reloc r_rel;
+         unsigned r_type;
+         asection *target_sec;
 
-  /* Sort the source_relocs by target offset.  */
-  src_relocs = relax_info->src_relocs;
-  qsort (src_relocs, relax_info->src_count,
-        sizeof (source_reloc), source_reloc_compare);
+         /* Locally change the source address.
+            Translate the target to the new target address.
+            If it points to this section and has been removed,
+            NULLify it.
+            Write it back.  */
 
-  for (i = 0; i < relax_info->src_count; i++)
-    {
-      source_reloc *rel;
-      Elf_Internal_Rela *irel = NULL;
-      literal_value val;
-      value_map *val_map;
+         irel = &internal_relocs[i];
+         source_offset = irel->r_offset;
+         old_source_offset = source_offset;
 
-      rel = &src_relocs[i];
-      irel = get_irel_at_offset (sec, internal_relocs,
-                                rel->r_rel.target_offset);
+         r_type = ELF32_R_TYPE (irel->r_info);
+         r_reloc_init (&r_rel, abfd, irel, contents,
+                       bfd_get_section_limit (abfd, sec));
 
-      /* If the target_offset for this relocation is the same as the
-        previous relocation, then we've already considered whether the
-        literal can be coalesced.  Skip to the next one....  */
-      if (i != 0 && (src_relocs[i-1].r_rel.target_offset
-                    == rel->r_rel.target_offset))
-       continue;
+         /* If this section could have changed then we may need to
+            change the relocation's offset.  */
 
-      /* 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 (rel->is_null
-         && (i == relax_info->src_count - 1
-             || (src_relocs[i+1].r_rel.target_offset
-                 != rel->r_rel.target_offset)))
-       {
-         /* Mark the unused literal so that it will be removed.  */
-         add_removed_literal (&relax_info->removed_list, &rel->r_rel, NULL);
+         if (relax_info->is_relaxable_literal_section
+             || relax_info->is_relaxable_asm_section)
+           {
+             if (r_type != R_XTENSA_NONE
+                 && find_removed_literal (&relax_info->removed_list,
+                                          irel->r_offset))
+               {
+                 /* Remove this relocation.  */
+                 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
+                   (&relax_info->action_list, irel->r_offset);
+                 pin_internal_relocs (sec, internal_relocs);
+                 continue;
+               }
+
+             if (r_type == R_XTENSA_ASM_SIMPLIFY)
+               {
+                 text_action *action =
+                   find_insn_action (&relax_info->action_list,
+                                     irel->r_offset);
+                 if (action && (action->action == ta_convert_longcall
+                                || action->action == ta_remove_longcall))
+                   {
+                     bfd_reloc_status_type retval;
+                     char *error_message = NULL;
+
+                     retval = contract_asm_expansion (contents, sec_size,
+                                                      irel, &error_message);
+                     if (retval != bfd_reloc_ok)
+                       {
+                         (*link_info->callbacks->reloc_dangerous)
+                           (link_info, error_message, abfd, sec,
+                            irel->r_offset);
+                         goto error_return;
+                       }
+                     /* Update the action so that the code that moves
+                        the contents will do the right thing.  */
+                     if (action->action == ta_remove_longcall)
+                       action->action = ta_remove_insn;
+                     else
+                       action->action = ta_none;
+                     /* Refresh the info in the r_rel.  */
+                     r_reloc_init (&r_rel, abfd, irel, contents, sec_size);
+                     r_type = ELF32_R_TYPE (irel->r_info);
+                   }
+               }
+
+             source_offset = offset_with_removed_text
+               (&relax_info->action_list, irel->r_offset);
+             irel->r_offset = source_offset;
+           }
+
+         /* If the target section could have changed then
+            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);
 
-         /* Zero out the relocation on this literal location.  */
-         if (irel)
+         if (target_relax_info
+             && (target_relax_info->is_relaxable_literal_section
+                 || target_relax_info->is_relaxable_asm_section))
            {
-             if (elf_hash_table (link_info)->dynamic_sections_created)
-               shrink_dynamic_reloc_sections (link_info, abfd, sec, irel);
+             r_reloc new_reloc;
+             reloc_bfd_fix *fix;
+             bfd_vma addend_displacement;
 
-             irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE);
+             translate_reloc (&r_rel, &new_reloc);
+
+             if (r_type == R_XTENSA_DIFF8
+                 || r_type == R_XTENSA_DIFF16
+                 || r_type == R_XTENSA_DIFF32)
+               {
+                 bfd_vma diff_value = 0, new_end_offset, diff_mask = 0;
+
+                 if (bfd_get_section_limit (abfd, sec) < old_source_offset)
+                   {
+                     (*link_info->callbacks->reloc_dangerous)
+                       (link_info, _("invalid relocation address"),
+                        abfd, sec, old_source_offset);
+                     goto error_return;
+                   }
+
+                 switch (r_type)
+                   {
+                   case R_XTENSA_DIFF8:
+                     diff_value =
+                       bfd_get_8 (abfd, &contents[old_source_offset]);
+                     break;
+                   case R_XTENSA_DIFF16:
+                     diff_value =
+                       bfd_get_16 (abfd, &contents[old_source_offset]);
+                     break;
+                   case R_XTENSA_DIFF32:
+                     diff_value =
+                       bfd_get_32 (abfd, &contents[old_source_offset]);
+                     break;
+                   }
+
+                 new_end_offset = offset_with_removed_text
+                   (&target_relax_info->action_list,
+                    r_rel.target_offset + diff_value);
+                 diff_value = new_end_offset - new_reloc.target_offset;
+
+                 switch (r_type)
+                   {
+                   case R_XTENSA_DIFF8:
+                     diff_mask = 0xff;
+                     bfd_put_8 (abfd, diff_value,
+                                &contents[old_source_offset]);
+                     break;
+                   case R_XTENSA_DIFF16:
+                     diff_mask = 0xffff;
+                     bfd_put_16 (abfd, diff_value,
+                                 &contents[old_source_offset]);
+                     break;
+                   case R_XTENSA_DIFF32:
+                     diff_mask = 0xffffffff;
+                     bfd_put_32 (abfd, diff_value,
+                                 &contents[old_source_offset]);
+                     break;
+                   }
+
+                 /* Check for overflow.  */
+                 if ((diff_value & ~diff_mask) != 0)
+                   {
+                     (*link_info->callbacks->reloc_dangerous)
+                       (link_info, _("overflow after relaxation"),
+                        abfd, sec, old_source_offset);
+                     goto error_return;
+                   }
+
+                 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);
            }
 
-         continue;
+         pin_internal_relocs (sec, internal_relocs);
        }
+    }
 
-      /* Find the literal value.  */
-      r_reloc_init (&val.r_rel, abfd, irel);
-      BFD_ASSERT (rel->r_rel.target_offset < sec->size);
-      val.value = bfd_get_32 (abfd, contents + rel->r_rel.target_offset);
-          
-      /* Check if we've seen another literal with the same value.  */
-      val_map = get_cached_value (values, &val, final_static_link);
-      if (val_map != NULL) 
+  if ((relax_info->is_relaxable_literal_section
+       || relax_info->is_relaxable_asm_section)
+      && relax_info->action_list.head)
+    {
+      /* 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_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;
+
+      final_size = sec->size;
+      for (action = relax_info->action_list.head; action;
+          action = action->next)
        {
-         /* First check that THIS and all the other relocs to this
-            literal will FIT if we move them to the new address.  */
+         final_size -= action->removed_bytes;
+       }
+
+      scratch = (bfd_byte *) bfd_zmalloc (final_size);
+      dup_contents = (bfd_byte *) bfd_zmalloc (final_size);
 
-         if (relocations_reach (rel, relax_info->src_count - i,
-                                &val_map->loc))
+      /* The dot is the current fill location.  */
+#if DEBUG
+      print_action_list (stderr, &relax_info->action_list);
+#endif
+
+      for (action = relax_info->action_list.head; action;
+          action = action->next)
+       {
+         virtual_action = FALSE;
+         if (action->offset > orig_dot)
            {
-             /* Mark that the literal will be coalesced.  */
-             add_removed_literal (&relax_info->removed_list,
-                                  &rel->r_rel, &val_map->loc);
+             orig_dot += orig_dot_copied;
+             orig_dot_copied = 0;
+             orig_dot_vo = 0;
+             /* Out of the virtual world.  */
            }
-         else
+
+         if (action->offset > orig_dot)
            {
-             /* Relocations do not reach -- do not remove this literal.  */
-             val_map->loc = rel->r_rel;
+             copy_size = action->offset - orig_dot;
+             memmove (&dup_contents[dup_dot], &contents[orig_dot], copy_size);
+             orig_dot += copy_size;
+             dup_dot += copy_size;
+             BFD_ASSERT (action->offset == orig_dot);
            }
-       }
-      else
-       {
-         /* This is the first time we've seen this literal value.  */
-         BFD_ASSERT (sec == r_reloc_get_section (&rel->r_rel));
-         add_value_map (values, &val, &rel->r_rel, final_static_link);
-       }
-    }
+         else if (action->offset < orig_dot)
+           {
+             if (action->action == ta_fill
+                 && action->offset - action->removed_bytes == orig_dot)
+               {
+                 /* This is OK because the fill only effects the dup_dot.  */
+               }
+             else if (action->action == ta_add_literal)
+               {
+                 /* TBD.  Might need to handle this.  */
+               }
+           }
+         if (action->offset == orig_dot)
+           {
+             if (action->virtual_offset > orig_dot_vo)
+               {
+                 if (orig_dot_vo == 0)
+                   {
+                     /* Need to copy virtual_offset bytes.  Probably four.  */
+                     copy_size = action->virtual_offset - orig_dot_vo;
+                     memmove (&dup_contents[dup_dot],
+                              &contents[orig_dot], copy_size);
+                     orig_dot_copied = copy_size;
+                     dup_dot += copy_size;
+                   }
+                 virtual_action = TRUE;
+               } 
+             else
+               BFD_ASSERT (action->virtual_offset <= orig_dot_vo);
+           }
+         switch (action->action)
+           {
+           case ta_remove_literal:
+           case ta_remove_insn:
+             BFD_ASSERT (action->removed_bytes >= 0);
+             orig_dot += action->removed_bytes;
+             break;
 
-error_return:
-  release_contents (sec, contents);
-  release_internal_relocs (sec, internal_relocs);
-  return ok;
-}
+           case ta_narrow_insn:
+             orig_insn_size = 3;
+             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);
+             BFD_ASSERT (rv);
+             memmove (&dup_contents[dup_dot], scratch, copy_size);
+             orig_dot += orig_insn_size;
+             dup_dot += copy_size;
+             break;
 
+           case ta_fill:
+             if (action->removed_bytes >= 0)
+               orig_dot += action->removed_bytes;
+             else
+               {
+                 /* Already zeroed in dup_contents.  Just bump the
+                    counters.  */
+                 dup_dot += (-action->removed_bytes);
+               }
+             break;
 
-/* Check if the original relocations (presumably on L32R instructions)
-   identified by reloc[0..N] can be changed to reference the literal
-   identified by r_rel.  If r_rel is out of range for any of the
-   original relocations, then we don't want to coalesce the original
-   literal with the one at r_rel.  We only check reloc[0..N], where the
-   offsets are all the same as for reloc[0] (i.e., they're all
-   referencing the same literal) and where N is also bounded by the
-   number of remaining entries in the "reloc" array.  The "reloc" array
-   is sorted by target offset so we know all the entries for the same
-   literal will be contiguous.  */
+           case ta_none:
+             BFD_ASSERT (action->removed_bytes == 0);
+             break;
 
-static bfd_boolean
-relocations_reach (reloc, remaining_relocs, r_rel)
-     source_reloc *reloc;
-     int remaining_relocs;
-     const r_reloc *r_rel;
-{
-  bfd_vma from_offset, source_address, dest_address;
-  asection *sec;
-  int i;
+           case ta_convert_longcall:
+           case ta_remove_longcall:
+             /* These will be removed or converted before we get here.  */
+             BFD_ASSERT (0);
+             break;
 
-  if (!r_reloc_is_defined (r_rel))
-    return FALSE;
+           case ta_widen_insn:
+             orig_insn_size = 2;
+             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);
+             BFD_ASSERT (rv);
+             memmove (&dup_contents[dup_dot], scratch, copy_size);
+             orig_dot += orig_insn_size;
+             dup_dot += copy_size;
+             break;
 
-  sec = r_reloc_get_section (r_rel);
-  from_offset = reloc[0].r_rel.target_offset;
+           case ta_add_literal:
+             orig_insn_size = 0;
+             copy_size = 4;
+             BFD_ASSERT (action->removed_bytes == -4);
+             /* TBD -- place the literal value here and insert
+                into the table.  */
+             memset (&dup_contents[dup_dot], 0, 4);
+             pin_internal_relocs (sec, internal_relocs);
+             pin_contents (sec, contents);
 
-  for (i = 0; i < remaining_relocs; i++)
-    {
-      if (reloc[i].r_rel.target_offset != from_offset)
-       break;
+             if (!move_literal (abfd, link_info, sec, dup_dot, dup_contents,
+                                relax_info, &internal_relocs, &action->value))
+               goto error_return;
 
-      /* Ignore relocations that have been removed.  */
-      if (reloc[i].is_null)
-       continue;
+             if (virtual_action) 
+               orig_dot_vo += copy_size;
 
-      /* The original and new output section for these must be the same
-         in order to coalesce.  */
-      if (r_reloc_get_section (&reloc[i].r_rel)->output_section
-         != sec->output_section)
-       return FALSE;
+             orig_dot += orig_insn_size;
+             dup_dot += copy_size;
+             break;
 
-      /* A NULL operand means it is not a PC-relative relocation, so
-         the literal can be moved anywhere.  */
-      if (reloc[i].opnd)
-       {
-         /* Otherwise, check to see that it fits.  */
-         source_address = (reloc[i].source_sec->output_section->vma
-                           + reloc[i].source_sec->output_offset
-                           + reloc[i].r_rel.rela.r_offset);
-         dest_address = (sec->output_section->vma
-                         + sec->output_offset
-                         + r_rel->target_offset);
+           default:
+             /* Not implemented yet.  */
+             BFD_ASSERT (0);
+             break;
+           }
 
-         if (!pcrel_reloc_fits (reloc[i].opnd, source_address, dest_address))
-           return FALSE;
+         size -= action->removed_bytes;
+         removed += action->removed_bytes;
+         BFD_ASSERT (dup_dot <= final_size);
+         BFD_ASSERT (orig_dot <= orig_size);
        }
-    }
 
-  return TRUE;
-}
+      orig_dot += orig_dot_copied;
+      orig_dot_copied = 0;
 
+      if (orig_dot != orig_size)
+       {
+         copy_size = orig_size - orig_dot;
+         BFD_ASSERT (orig_size > orig_dot);
+         BFD_ASSERT (dup_dot + copy_size == final_size);
+         memmove (&dup_contents[dup_dot], &contents[orig_dot], copy_size);
+         orig_dot += copy_size;
+         dup_dot += copy_size;
+       }
+      BFD_ASSERT (orig_size == orig_dot);
+      BFD_ASSERT (final_size == dup_dot);
 
-/* WARNING: linear search here.  If the relocation are in order by
-   address, we can use a faster binary search.  ALSO, we assume that
-   there is only 1 non-NONE relocation per address.  */
+      /* Move the dup_contents back.  */
+      if (final_size > orig_size)
+       {
+         /* Contents need to be reallocated.  Swap the dup_contents into
+            contents.  */
+         sec->contents = dup_contents;
+         free (contents);
+         contents = dup_contents;
+         pin_contents (sec, contents);
+       }
+      else
+       {
+         BFD_ASSERT (final_size <= orig_size);
+         memset (contents, 0, orig_size);
+         memcpy (contents, dup_contents, final_size);
+         free (dup_contents);
+       }
+      free (scratch);
+      pin_contents (sec, contents);
 
-static Elf_Internal_Rela *
-get_irel_at_offset (sec, internal_relocs, offset)
-     asection *sec;
-     Elf_Internal_Rela *internal_relocs;
-     bfd_vma offset;
-{
-  unsigned i;
-  if (!internal_relocs) 
-    return NULL;
-  for (i = 0; i < sec->reloc_count; i++)
-    {
-      Elf_Internal_Rela *irel = &internal_relocs[i];
-      if (irel->r_offset == offset
-         && ELF32_R_TYPE (irel->r_info) != R_XTENSA_NONE)
-       return irel;
+      sec->size = final_size;
     }
-  return NULL;
+
+ error_return:
+  release_internal_relocs (sec, internal_relocs);
+  release_contents (sec, contents);
+  return ok;
 }
 
-\f
-/* Second relaxation pass.  */
-
-/* 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
-   cooked_size.  */
 
-bfd_boolean 
-relax_section (abfd, sec, link_info)
-     bfd *abfd;
-     asection *sec;
-     struct bfd_link_info *link_info;
+static bfd_boolean 
+translate_section_fixes (asection *sec)
 {
-  Elf_Internal_Rela *internal_relocs;
   xtensa_relax_info *relax_info;
-  bfd_byte *contents;
-  bfd_boolean ok = TRUE;
-  unsigned i;
+  reloc_bfd_fix *r;
 
   relax_info = get_xtensa_relax_info (sec);
-  BFD_ASSERT (relax_info);
+  if (!relax_info)
+    return TRUE;
 
-  /* Handle property sections (e.g., literal tables) specially.  */
-  if (xtensa_is_property_section (sec))
-    {
-      BFD_ASSERT (!relax_info->is_relaxable_literal_section);
-      return relax_property_section (abfd, sec, link_info);
-    }
+  for (r = relax_info->fix_list; r != NULL; r = r->next)
+    if (!translate_reloc_bfd_fix (r))
+      return FALSE;
 
-  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)
-    {
-      ok = FALSE;
-      goto error_return;
-    }
+  return TRUE;
+}
 
-  if (internal_relocs)
-    {
-      for (i = 0; i < sec->reloc_count; i++) 
-       {
-         Elf_Internal_Rela *irel;
-         xtensa_relax_info *target_relax_info;
-         bfd_vma source_offset;
-         r_reloc r_rel;
-         unsigned r_type;
-         asection *target_sec;
 
-         /* Locally change the source address.
-            Translate the target to the new target address.
-            If it points to this section and has been removed,
-            NULLify it.
-            Write it back.  */
+/* Translate a fix given the mapping in the relax info for the target
+   section.  If it has already been translated, no work is required.  */
 
-         irel = &internal_relocs[i];
-         source_offset = irel->r_offset;
+static bfd_boolean 
+translate_reloc_bfd_fix (reloc_bfd_fix *fix)
+{
+  reloc_bfd_fix new_fix;
+  asection *sec;
+  xtensa_relax_info *relax_info;
+  removed_literal *removed;
+  bfd_vma new_offset, target_offset;
 
-         r_type = ELF32_R_TYPE (irel->r_info);
-         r_reloc_init (&r_rel, abfd, irel);
-       
-         if (relax_info->is_relaxable_literal_section)
-           {
-             if (r_type != R_XTENSA_NONE
-                 && find_removed_literal (&relax_info->removed_list,
-                                          irel->r_offset))
-               {
-                 /* Remove this relocation.  */
-                 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_literals
-                   (&relax_info->removed_list, irel->r_offset);
-                 continue;
-               }
-             source_offset =
-               offset_with_removed_literals (&relax_info->removed_list,
-                                             irel->r_offset);
-             irel->r_offset = source_offset;
-           }
+  if (fix->translated)
+    return TRUE;
 
-         target_sec = r_reloc_get_section (&r_rel);
-         target_relax_info = get_xtensa_relax_info (target_sec);
+  sec = fix->target_sec;
+  target_offset = fix->target_offset;
 
-         if (target_relax_info
-             && target_relax_info->is_relaxable_literal_section)
-           {
-             r_reloc new_rel;
-             reloc_bfd_fix *fix;
+  relax_info = get_xtensa_relax_info (sec);
+  if (!relax_info)
+    {
+      fix->translated = TRUE;
+      return TRUE;
+    }
 
-             translate_reloc (&r_rel, &new_rel);
+  new_fix = *fix;
 
-             /* 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.  */
+  /* The fix does not need to be translated if the section cannot change.  */
+  if (!relax_info->is_relaxable_literal_section
+      && !relax_info->is_relaxable_asm_section)
+    {
+      fix->translated = TRUE;
+      return TRUE;
+    }
 
-             fix = reloc_bfd_fix_init (sec, source_offset, r_type, 0,
-                                       r_reloc_get_section (&new_rel),
-                                       new_rel.target_offset);
-             add_fix (sec, fix);
-           }
+  /* If the literal has been moved and this relocation was on an
+     opcode, then the relocation should move to the new literal
+     location.  Otherwise, the relocation should move within the
+     section.  */
 
-         pin_internal_relocs (sec, internal_relocs);
-       }
+  removed = FALSE;
+  if (is_operand_relocation (fix->src_type))
+    {
+      /* Check if the original relocation is against a literal being
+        removed.  */
+      removed = find_removed_literal (&relax_info->removed_list,
+                                     target_offset);
     }
 
-  if (relax_info->is_relaxable_literal_section)
+  if (removed) 
     {
-      /* Walk through the contents and delete literals that are not needed 
-         anymore.  */
+      asection *new_sec;
 
-      unsigned long size = sec->size;
-      unsigned long removed = 0;
+      /* The fact that there is still a relocation to this literal indicates
+        that the literal is being coalesced, not simply removed.  */
+      BFD_ASSERT (removed->to.abfd != NULL);
 
-      removed_literal *reloc = relax_info->removed_list.head;
-      for (; reloc; reloc = reloc->next) 
+      /* This was moved to some other address (possibly another section).  */
+      new_sec = r_reloc_get_section (&removed->to);
+      if (new_sec != sec) 
        {
-         unsigned long upper = sec->size;
-         bfd_vma start = reloc->from.target_offset + 4;
-         if (reloc->next)
-           upper = reloc->next->from.target_offset;
-         if (upper - start != 0) 
+         sec = new_sec;
+         relax_info = get_xtensa_relax_info (sec);
+         if (!relax_info || 
+             (!relax_info->is_relaxable_literal_section
+              && !relax_info->is_relaxable_asm_section))
            {
-             BFD_ASSERT (start <= upper);
-             memmove (contents + start - removed - 4,
-                      contents + start,
-                      upper - start );
-             pin_contents (sec, contents);
+             target_offset = removed->to.target_offset;
+             new_fix.target_sec = new_sec;
+             new_fix.target_offset = target_offset;
+             new_fix.translated = TRUE;
+             *fix = new_fix;
+             return TRUE;
            }
-         removed += 4;
-         size -= 4;
        }
-
-      /* Change the section size.  */
-      sec->size = size;
+      target_offset = removed->to.target_offset;
+      new_fix.target_sec = new_sec;
     }
-  
- error_return:
-  release_internal_relocs (sec, internal_relocs);
-  release_contents (sec, contents);
-  return ok;
+
+  /* The target address may have been moved within its section.  */
+  new_offset = offset_with_removed_text (&relax_info->action_list,
+                                        target_offset);
+
+  new_fix.target_offset = new_offset;
+  new_fix.target_offset = new_offset;
+  new_fix.translated = TRUE;
+  *fix = new_fix;
+  return TRUE;
 }
 
 
 /* Fix up a relocation to take account of removed literals.  */
 
 static void
-translate_reloc (orig_rel, new_rel)
-     const r_reloc *orig_rel;
-     r_reloc *new_rel;
+translate_reloc (const r_reloc *orig_rel, r_reloc *new_rel)
 {
   asection *sec;
   xtensa_relax_info *relax_info;
   removed_literal *removed;
-  unsigned long new_offset;
+  bfd_vma new_offset, target_offset, removed_bytes;
 
   *new_rel = *orig_rel;
 
@@ -4841,13 +8276,21 @@ translate_reloc (orig_rel, new_rel)
   relax_info = get_xtensa_relax_info (sec);
   BFD_ASSERT (relax_info);
 
-  if (!relax_info->is_relaxable_literal_section)
+  if (!relax_info->is_relaxable_literal_section
+      && !relax_info->is_relaxable_asm_section)
     return;
 
-  /* Check if the original relocation is against a literal being removed.  */
-  removed = find_removed_literal (&relax_info->removed_list,
-                                 orig_rel->target_offset);
-  if (removed) 
+  target_offset = orig_rel->target_offset;
+
+  removed = FALSE;
+  if (is_operand_relocation (ELF32_R_TYPE (orig_rel->rela.r_info)))
+    {
+      /* Check if the original relocation is against a literal being
+        removed.  */
+      removed = find_removed_literal (&relax_info->removed_list,
+                                     target_offset);
+    }
+  if (removed && removed->to.abfd)
     {
       asection *new_sec;
 
@@ -4855,25 +8298,30 @@ translate_reloc (orig_rel, new_rel)
         that the literal is being coalesced, not simply removed.  */
       BFD_ASSERT (removed->to.abfd != NULL);
 
-      /* This was moved to some other address (possibly in another section). */
+      /* This was moved to some other address
+        (possibly in another section).  */
       *new_rel = removed->to;
       new_sec = r_reloc_get_section (new_rel);
-      if (new_sec != sec) 
+      if (new_sec != sec)
        {
          sec = new_sec;
          relax_info = get_xtensa_relax_info (sec);
-         if (!relax_info || !relax_info->is_relaxable_literal_section)
+         if (!relax_info
+             || (!relax_info->is_relaxable_literal_section
+                 && !relax_info->is_relaxable_asm_section))
            return;
        }
+      target_offset = new_rel->target_offset;
     }
 
   /* ...and the target address may have been moved within its section.  */
-  new_offset = offset_with_removed_literals (&relax_info->removed_list,
-                                            new_rel->target_offset);
+  new_offset = offset_with_removed_text (&relax_info->action_list,
+                                        target_offset);
 
   /* Modify the offset and addend.  */
+  removed_bytes = target_offset - new_offset;
   new_rel->target_offset = new_offset;
-  new_rel->rela.r_addend += (new_offset - new_rel->target_offset);
+  new_rel->rela.r_addend -= removed_bytes;
 }
 
 
@@ -4887,11 +8335,10 @@ translate_reloc (orig_rel, new_rel)
    nothing else that needs to be done.  */
 
 static void
-shrink_dynamic_reloc_sections (info, abfd, input_section, rel)
-     struct bfd_link_info *info;
-     bfd *abfd;
-     asection *input_section;
-     Elf_Internal_Rela *rel;
+shrink_dynamic_reloc_sections (struct bfd_link_info *info,
+                              bfd *abfd,
+                              asection *input_section,
+                              Elf_Internal_Rela *rel)
 {
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
@@ -4983,40 +8430,185 @@ shrink_dynamic_reloc_sections (info, abfd, input_section, rel)
 }
 
 
+/* Take an r_rel and move it to another section.  This usually
+   requires extending the interal_relocation array and pinning it.  If
+   the original r_rel is from the same BFD, we can complete this here.
+   Otherwise, we add a fix record to let the final link fix the
+   appropriate address.  Contents and internal relocations for the
+   section must be pinned after calling this routine.  */
+
+static bfd_boolean
+move_literal (bfd *abfd,
+             struct bfd_link_info *link_info,
+             asection *sec,
+             bfd_vma offset,
+             bfd_byte *contents,
+             xtensa_relax_info *relax_info,
+             Elf_Internal_Rela **internal_relocs_p,
+             const literal_value *lit)
+{
+  Elf_Internal_Rela *new_relocs = NULL;
+  size_t new_relocs_count = 0;
+  Elf_Internal_Rela this_rela;
+  const r_reloc *r_rel;
+
+  r_rel = &lit->r_rel;
+  BFD_ASSERT (elf_section_data (sec)->relocs == *internal_relocs_p);
+
+  if (r_reloc_is_const (r_rel))
+    bfd_put_32 (abfd, lit->value, contents + offset);
+  else
+    {
+      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;
+      this_rela.r_info = ELF32_R_INFO (0, r_type);
+      this_rela.r_addend =
+       r_rel->target_offset - r_reloc_get_target_offset (r_rel);
+      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,
+                               r_reloc_get_section (r_rel),
+                               r_rel->target_offset + r_rel->virtual_offset,
+                               FALSE);
+      /* We also need to mark that relocations are needed here.  */
+      sec->flags |= SEC_RELOC;
+
+      translate_reloc_bfd_fix (fix);
+      /* This fix has not yet been translated.  */
+      add_fix (sec, fix);
+
+      /* Add the relocation.  If we have already allocated our own
+        space for the relocations and we have room for more, then use
+        it.  Otherwise, allocate new space and move the literals.  */
+      insert_at = sec->reloc_count;
+      for (i = 0; i < sec->reloc_count; ++i)
+       {
+         if (this_rela.r_offset < (*internal_relocs_p)[i].r_offset)
+           {
+             insert_at = i;
+             break;
+           }
+       }
+
+      if (*internal_relocs_p != relax_info->allocated_relocs
+         || sec->reloc_count + 1 > relax_info->allocated_relocs_count)
+       {
+         BFD_ASSERT (relax_info->allocated_relocs == NULL
+                     || sec->reloc_count == relax_info->relocs_count);
+
+         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;
+
+         new_relocs = (Elf_Internal_Rela *)
+           bfd_zmalloc (sizeof (Elf_Internal_Rela) * (new_relocs_count));
+         if (!new_relocs)
+           return FALSE;
+
+         /* We could handle this more quickly by finding the split point.  */
+         if (insert_at != 0)
+           memcpy (new_relocs, *internal_relocs_p,
+                   insert_at * sizeof (Elf_Internal_Rela));
+
+         new_relocs[insert_at] = this_rela;
+
+         if (insert_at != sec->reloc_count)
+           memcpy (new_relocs + insert_at + 1,
+                   (*internal_relocs_p) + insert_at,
+                   (sec->reloc_count - insert_at) 
+                   * sizeof (Elf_Internal_Rela));
+
+         if (*internal_relocs_p != relax_info->allocated_relocs)
+           {
+             /* The first time we re-allocate, we can only free the
+                old relocs if they were allocated with bfd_malloc.
+                This is not true when keep_memory is in effect.  */
+             if (!link_info->keep_memory)
+               free (*internal_relocs_p);
+           }
+         else
+           free (*internal_relocs_p);
+         relax_info->allocated_relocs = new_relocs;
+         relax_info->allocated_relocs_count = new_relocs_count;
+         elf_section_data (sec)->relocs = new_relocs;
+         sec->reloc_count++;
+         relax_info->relocs_count = sec->reloc_count;
+         *internal_relocs_p = new_relocs;
+       }
+      else
+       {
+         if (insert_at != sec->reloc_count)
+           {
+             unsigned idx;
+             for (idx = sec->reloc_count; idx > insert_at; idx--)
+               (*internal_relocs_p)[idx] = (*internal_relocs_p)[idx-1];
+           }
+         (*internal_relocs_p)[insert_at] = this_rela;
+         sec->reloc_count++;
+         if (relax_info->allocated_relocs)
+           relax_info->relocs_count = sec->reloc_count;
+       }
+    }
+  return TRUE;
+}
+
+
 /* This is similar to relax_section except that when a target is moved,
    we shift addresses up.  We also need to modify the size.  This
    algorithm does NOT allow for relocations into the middle of the
    property sections.  */
 
-static bfd_boolean 
-relax_property_section (abfd, sec, link_info)
-     bfd *abfd;
-     asection *sec;
-     struct bfd_link_info *link_info;
+static bfd_boolean
+relax_property_section (bfd *abfd,
+                       asection *sec,
+                       struct bfd_link_info *link_info)
 {
   Elf_Internal_Rela *internal_relocs;
   bfd_byte *contents;
   unsigned i, nexti;
   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;
 
+  sec_size = bfd_get_section_limit (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)
+  if (contents == NULL && sec_size != 0)
     {
       ok = FALSE;
       goto error_return;
     }
 
-  if (internal_relocs) 
+  is_full_prop_section =
+    ((strcmp (sec->name, XTENSA_PROP_SEC_NAME) == 0)
+     || (strncmp (sec->name, ".gnu.linkonce.prop.",
+                 sizeof ".gnu.linkonce.prop." - 1) == 0));
+
+  if (internal_relocs)
     {
-      for (i = 0; i < sec->reloc_count; i++) 
+      for (i = 0; i < sec->reloc_count; i++)
        {
          Elf_Internal_Rela *irel;
          xtensa_relax_info *target_relax_info;
-         r_reloc r_rel;
          unsigned r_type;
          asection *target_sec;
+         literal_value val;
+         bfd_byte *size_p, *flags_p;
 
          /* Locally change the source address.
             Translate the target to the new target address.
@@ -5029,42 +8621,88 @@ relax_property_section (abfd, sec, link_info)
          if (r_type == R_XTENSA_NONE)
            continue;
 
-         r_reloc_init (&r_rel, abfd, irel);
+         /* Find the literal value.  */
+         r_reloc_init (&val.r_rel, abfd, irel, contents, sec_size);
+         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);
 
-         target_sec = r_reloc_get_section (&r_rel);
+         target_sec = r_reloc_get_section (&val.r_rel);
          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_literal_section
+                 || target_relax_info->is_relaxable_asm_section ))
            {
              /* Translate the relocation's destination.  */
-             bfd_vma new_offset;
-             bfd_vma new_end_offset;
-             bfd_byte *size_p;
+             bfd_vma new_offset, new_end_offset;
              long old_size, new_size;
 
-             new_offset =
-               offset_with_removed_literals (&target_relax_info->removed_list,
-                                             r_rel.target_offset);
+             new_offset = offset_with_removed_text
+               (&target_relax_info->action_list, val.r_rel.target_offset);
 
              /* Assert that we are not out of bounds.  */
-             size_p = &contents[irel->r_offset + 4];
-             old_size = bfd_get_32 (abfd, &contents[irel->r_offset + 4]);
+             old_size = bfd_get_32 (abfd, size_p);
+
+             if (old_size == 0)
+               {
+                 /* Only the first zero-sized unreachable entry is
+                    allowed to expand.  In this case the new offset
+                    should be the offset before the fill and the new
+                    size is the expansion size.  For other zero-sized
+                    entries the resulting size should be zero with an
+                    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
+                   {
+                     new_end_offset = new_offset;
+                     new_offset = offset_with_removed_text_before_fill
+                       (&target_relax_info->action_list,
+                        val.r_rel.target_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
+                       {
+                         last_zfill_target_sec = target_sec;
+                         last_zfill_target_offset = val.r_rel.target_offset;
+                       }
+                   }
+               }
+             else
+               {
+                 new_end_offset = offset_with_removed_text_before_fill
+                   (&target_relax_info->action_list,
+                    val.r_rel.target_offset + old_size);
+               }
 
-             new_end_offset =
-               offset_with_removed_literals (&target_relax_info->removed_list,
-                                             r_rel.target_offset + old_size);
-             
              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 != r_rel.target_offset)
+
+             if (new_offset != val.r_rel.target_offset)
                {
-                 bfd_vma diff = new_offset - r_rel.target_offset;
+                 bfd_vma diff = new_offset - val.r_rel.target_offset;
                  irel->r_addend += diff;
                  pin_internal_relocs (sec, internal_relocs);
                }
@@ -5076,12 +8714,22 @@ relax_property_section (abfd, sec, link_info)
      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)
+  if (internal_relocs && (!link_info->relocatable
+                         || strcmp (sec->name, XTENSA_LIT_SEC_NAME) == 0))
     {
       Elf_Internal_Rela *last_irel = NULL;
       int removed_bytes = 0;
       bfd_vma offset, last_irel_offset;
       bfd_vma section_size;
+      bfd_size_type entry_size;
+      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.  */
@@ -5094,13 +8742,14 @@ relax_property_section (abfd, sec, link_info)
 
       last_irel_offset = (bfd_vma) -1;
       section_size = sec->size;
-      BFD_ASSERT (section_size % 8 == 0);
+      BFD_ASSERT (section_size % entry_size == 0);
 
-      for (offset = 0; offset < section_size; offset += 8)
+      for (offset = 0; offset < section_size; offset += entry_size)
        {
          Elf_Internal_Rela *irel, *next_irel;
          bfd_vma bytes_to_remove, size, actual_offset;
          bfd_boolean remove_this_irel;
+         flagword flags;
 
          irel = NULL;
          next_irel = NULL;
@@ -5138,28 +8787,38 @@ relax_property_section (abfd, sec, link_info)
          actual_offset = offset - removed_bytes;
          size = bfd_get_32 (abfd, &contents[actual_offset + 4]);
 
+         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 isn't a reloc on the size field.  */
-         if (irel && irel->r_offset == offset + 4)
+         /* 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)
+         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)
+         else if (size == 0 && (flags & XTENSA_PROP_ALIGN) == 0
+                  && (flags & XTENSA_PROP_UNREACHABLE) == 0)
            {
-             /* Always remove entries with zero size.  */
-             bytes_to_remove = 8;
+             /* Always remove entries with zero size and no alignment.  */
+             bytes_to_remove = entry_size;
              if (irel && irel->r_offset == offset)
                {
                  remove_this_irel = TRUE;
@@ -5174,23 +8833,32 @@ relax_property_section (abfd, sec, link_info)
                {
                  if (last_irel)
                    {
-                     bfd_vma old_size = 
+                     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_vma old_address =
+                       (last_irel->r_addend
                         + bfd_get_32 (abfd, &contents[last_irel->r_offset]));
-                     bfd_vma new_address = 
-                       (irel->r_addend 
+                     bfd_vma new_address =
+                       (irel->r_addend
                         + bfd_get_32 (abfd, &contents[actual_offset]));
-
-                     if ((ELF32_R_SYM (irel->r_info) ==
-                          ELF32_R_SYM (last_irel->r_info))
-                         && (old_address + old_size == new_address)) 
+                     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 */
+                         /* Fix the old size.  */
                          bfd_put_32 (abfd, old_size + size,
                                      &contents[last_irel->r_offset + 4]);
-                         bytes_to_remove = 8;
+                         bytes_to_remove = entry_size;
                          remove_this_irel = TRUE;
                        }
                      else
@@ -5213,14 +8881,14 @@ relax_property_section (abfd, sec, link_info)
          if (bytes_to_remove != 0)
            {
              removed_bytes += bytes_to_remove;
-             if (offset + 8 < section_size)
+             if (offset + bytes_to_remove < section_size)
                memmove (&contents[actual_offset],
-                        &contents[actual_offset+8],
-                        section_size - offset - 8);
+                        &contents[actual_offset + bytes_to_remove],
+                        section_size - offset - bytes_to_remove);
            }
        }
 
-      if (removed_bytes) 
+      if (removed_bytes)
        {
          /* Clear the removed bytes.  */
          memset (&contents[section_size - removed_bytes], 0, removed_bytes);
@@ -5252,10 +8920,8 @@ relax_property_section (abfd, sec, link_info)
 
 /* Change symbol values to account for removed literals.  */
 
-bfd_boolean 
-relax_section_symbols (abfd, sec)
-     bfd *abfd;
-     asection *sec;
+bfd_boolean
+relax_section_symbols (bfd *abfd, asection *sec)
 {
   xtensa_relax_info *relax_info;
   unsigned int sec_shndx;
@@ -5266,7 +8932,8 @@ relax_section_symbols (abfd, sec)
   relax_info = get_xtensa_relax_info (sec);
   BFD_ASSERT (relax_info);
 
-  if (!relax_info->is_relaxable_literal_section)
+  if (!relax_info->is_relaxable_literal_section
+      && !relax_info->is_relaxable_asm_section)
     return TRUE;
 
   sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
@@ -5284,10 +8951,19 @@ relax_section_symbols (abfd, sec)
 
       if (isym->st_shndx == sec_shndx)
        {
-         bfd_vma new_address = offset_with_removed_literals
-           (&relax_info->removed_list, isym->st_value);
-         if (new_address != isym->st_value)
-           isym->st_value = new_address;
+         bfd_vma new_address = offset_with_removed_text
+           (&relax_info->action_list, isym->st_value);
+         bfd_vma new_size = isym->st_size;
+
+         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;
        }
     }
 
@@ -5305,10 +8981,20 @@ relax_section_symbols (abfd, sec)
           || sym_hash->root.type == bfd_link_hash_defweak)
          && sym_hash->root.u.def.section == sec)
        {
-         bfd_vma new_address = offset_with_removed_literals
-           (&relax_info->removed_list, sym_hash->root.u.def.value);
-         if (new_address != sym_hash->root.u.def.value)
-           sym_hash->root.u.def.value = new_address;
+         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;
+
+         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 = new_address;
+         sym_hash->size = new_size;
        }
     }
 
@@ -5318,37 +9004,40 @@ relax_section_symbols (abfd, sec)
 \f
 /* "Fix" handling functions, called while performing relocations.  */
 
-static void
-do_fix_for_relocatable_link (rel, input_bfd, input_section)
-     Elf_Internal_Rela *rel;
-     bfd *input_bfd;
-     asection *input_section;
+static bfd_boolean
+do_fix_for_relocatable_link (Elf_Internal_Rela *rel,
+                            bfd *input_bfd,
+                            asection *input_section,
+                            bfd_byte *contents)
 {
   r_reloc r_rel;
   asection *sec, *old_sec;
   bfd_vma old_offset;
   int r_type = ELF32_R_TYPE (rel->r_info);
-  reloc_bfd_fix *fix_list;
   reloc_bfd_fix *fix;
 
   if (r_type == R_XTENSA_NONE)
-    return;
-
-  fix_list = (get_xtensa_relax_info (input_section))->fix_list;
-  if (fix_list == NULL)
-    return;
+    return TRUE;
 
-  fix = get_bfd_fix (fix_list, input_section, rel->r_offset, r_type);
-  if (fix == NULL)
-    return;
+  fix = get_bfd_fix (input_section, rel->r_offset, r_type);
+  if (!fix)
+    return TRUE;
 
-  r_reloc_init (&r_rel, input_bfd, rel);
+  r_reloc_init (&r_rel, input_bfd, rel, contents,
+               bfd_get_section_limit (input_bfd, input_section));
   old_sec = r_reloc_get_section (&r_rel);
-  old_offset = r_reloc_get_target_offset (&r_rel);
-             
-  if (old_sec == NULL || !r_reloc_is_defined (&r_rel))
+  old_offset = r_rel.target_offset;
+
+  if (!old_sec || !r_reloc_is_defined (&r_rel))
     {
-      BFD_ASSERT (r_type == R_XTENSA_ASM_EXPAND);
+      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,
+            elf_howto_table[r_type].name);
+         return FALSE;
+       }
       /* Leave it be.  Resolution will happen in a later stage.  */
     }
   else
@@ -5357,44 +9046,51 @@ do_fix_for_relocatable_link (rel, input_bfd, input_section)
       rel->r_addend += ((sec->output_offset + fix->target_offset)
                        - (old_sec->output_offset + old_offset));
     }
+  return TRUE;
 }
 
 
 static void
-do_fix_for_final_link (rel, input_section, relocationp)
-     Elf_Internal_Rela *rel;
-     asection *input_section;
-     bfd_vma *relocationp;
+do_fix_for_final_link (Elf_Internal_Rela *rel,
+                      bfd *input_bfd,
+                      asection *input_section,
+                      bfd_byte *contents,
+                      bfd_vma *relocationp)
 {
   asection *sec;
   int r_type = ELF32_R_TYPE (rel->r_info);
-  reloc_bfd_fix *fix_list;
   reloc_bfd_fix *fix;
+  bfd_vma fixup_diff;
 
   if (r_type == R_XTENSA_NONE)
     return;
 
-  fix_list = (get_xtensa_relax_info (input_section))->fix_list;
-  if (fix_list == NULL)
-    return;
-
-  fix = get_bfd_fix (fix_list, input_section, rel->r_offset, r_type);
-  if (fix == NULL)
+  fix = get_bfd_fix (input_section, rel->r_offset, r_type);
+  if (!fix)
     return;
 
   sec = fix->target_sec;
+
+  fixup_diff = rel->r_addend;
+  if (elf_howto_table[fix->src_type].partial_inplace)
+    {
+      bfd_vma inplace_val;
+      BFD_ASSERT (fix->src_offset
+                 < bfd_get_section_limit (input_bfd, input_section));
+      inplace_val = bfd_get_32 (input_bfd, &contents[fix->src_offset]);
+      fixup_diff += inplace_val;
+    }
+
   *relocationp = (sec->output_section->vma
                  + sec->output_offset
-                 + fix->target_offset - rel->r_addend);
+                 + fix->target_offset - fixup_diff);
 }
 
 \f
 /* Miscellaneous utility functions....  */
 
 static asection *
-elf_xtensa_get_plt_section (dynobj, chunk)
-     bfd *dynobj;
-     int chunk;
+elf_xtensa_get_plt_section (bfd *dynobj, int chunk)
 {
   char plt_name[10];
 
@@ -5407,9 +9103,7 @@ elf_xtensa_get_plt_section (dynobj, chunk)
 
 
 static asection *
-elf_xtensa_get_gotplt_section (dynobj, chunk)
-     bfd *dynobj;
-     int chunk;
+elf_xtensa_get_gotplt_section (bfd *dynobj, int chunk)
 {
   char got_name[14];
 
@@ -5430,13 +9124,11 @@ elf_xtensa_get_gotplt_section (dynobj, chunk)
    . an absolute value, return the absolute section.  */
 
 static asection *
-get_elf_r_symndx_section (abfd, r_symndx)
-     bfd *abfd;
-     unsigned long r_symndx;
+get_elf_r_symndx_section (bfd *abfd, unsigned long r_symndx)
 {
   Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   asection *target_sec = NULL;
-  if (r_symndx < symtab_hdr->sh_info) 
+  if (r_symndx < symtab_hdr->sh_info)
     {
       Elf_Internal_Sym *isymbuf;
       unsigned int section_index;
@@ -5452,7 +9144,7 @@ get_elf_r_symndx_section (abfd, r_symndx)
        target_sec = bfd_abs_section_ptr;
       else if (section_index == SHN_COMMON)
        target_sec = bfd_com_section_ptr;
-      else 
+      else
        /* Who knows?  */
        target_sec = NULL;
     }
@@ -5488,9 +9180,7 @@ get_elf_r_symndx_section (abfd, r_symndx)
 
 
 static struct elf_link_hash_entry *
-get_elf_r_symndx_hash_entry (abfd, r_symndx)
-     bfd *abfd;
-     unsigned long r_symndx;
+get_elf_r_symndx_hash_entry (bfd *abfd, unsigned long r_symndx)
 {
   unsigned long indx;
   struct elf_link_hash_entry *h;
@@ -5498,7 +9188,7 @@ get_elf_r_symndx_hash_entry (abfd, r_symndx)
 
   if (r_symndx < symtab_hdr->sh_info)
     return NULL;
-  
+
   indx = r_symndx - symtab_hdr->sh_info;
   h = elf_sym_hashes (abfd)[indx];
   while (h->root.type == bfd_link_hash_indirect
@@ -5511,14 +9201,12 @@ get_elf_r_symndx_hash_entry (abfd, r_symndx)
 /* Get the section-relative offset for a symbol number.  */
 
 static bfd_vma
-get_elf_r_symndx_offset (abfd, r_symndx)
-     bfd *abfd;
-     unsigned long r_symndx;
+get_elf_r_symndx_offset (bfd *abfd, unsigned long r_symndx)
 {
   Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   bfd_vma offset = 0;
 
-  if (r_symndx < symtab_hdr->sh_info) 
+  if (r_symndx < symtab_hdr->sh_info)
     {
       Elf_Internal_Sym *isymbuf;
       isymbuf = retrieve_local_syms (abfd);
@@ -5542,35 +9230,51 @@ get_elf_r_symndx_offset (abfd, r_symndx)
 
 
 static bfd_boolean
-pcrel_reloc_fits (opnd, self_address, dest_address)
-     xtensa_operand opnd;
-     bfd_vma self_address;
-     bfd_vma dest_address;
+is_reloc_sym_weak (bfd *abfd, Elf_Internal_Rela *rel)
+{
+  unsigned long r_symndx = ELF32_R_SYM (rel->r_info);
+  struct elf_link_hash_entry *h;
+
+  h = get_elf_r_symndx_hash_entry (abfd, r_symndx);
+  if (h && h->root.type == bfd_link_hash_defweak)
+    return TRUE;
+  return FALSE;
+}
+
+
+static bfd_boolean
+pcrel_reloc_fits (xtensa_opcode opc,
+                 int opnd,
+                 bfd_vma self_address,
+                 bfd_vma dest_address)
 {
-  uint32 new_address =
-    xtensa_operand_do_reloc (opnd, dest_address, self_address);
-  return (xtensa_operand_encode (opnd, &new_address)
-         == xtensa_encode_result_ok);
+  xtensa_isa isa = xtensa_default_isa;
+  uint32 valp = dest_address;
+  if (xtensa_operand_do_reloc (isa, opc, opnd, &valp, self_address)
+      || xtensa_operand_encode (isa, opc, opnd, &valp))
+    return FALSE;
+  return TRUE;
 }
 
 
 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 
-xtensa_is_property_section (sec)
-     asection *sec;
+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_LIT_SEC_NAME, sec->name, lit_sec_len) == 0
+      || strncmp (XTENSA_PROP_SEC_NAME, sec->name, prop_sec_len) == 0)
     return TRUE;
 
   if (strncmp (".gnu.linkonce.", sec->name, linkonce_len) == 0
-      && (sec->name[linkonce_len] == 'x'
-         || sec->name[linkonce_len] == 'p')
-      && sec->name[linkonce_len + 1] == '.')
+      && (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 TRUE;
 
   return FALSE;
@@ -5578,8 +9282,7 @@ xtensa_is_property_section (sec)
 
 
 static bfd_boolean 
-xtensa_is_littable_section (sec)
-     asection *sec;
+xtensa_is_littable_section (asection *sec)
 {
   if (strncmp (XTENSA_LIT_SEC_NAME, sec->name, lit_sec_len) == 0)
     return TRUE;
@@ -5593,58 +9296,70 @@ xtensa_is_littable_section (sec)
 }
 
 
-static bfd_boolean
-is_literal_section (sec)
-     asection *sec;
+static int
+internal_reloc_compare (const void *ap, const void *bp)
 {
-  /* FIXME: the current definition of this leaves a lot to be desired....  */
-  if (sec == NULL || sec->name == NULL)
-    return FALSE;
-  return (strstr (sec->name, "literal") != NULL);
+  const Elf_Internal_Rela *a = (const Elf_Internal_Rela *) ap;
+  const Elf_Internal_Rela *b = (const Elf_Internal_Rela *) bp;
+
+  if (a->r_offset != b->r_offset)
+    return (a->r_offset - b->r_offset);
+
+  /* We don't need to sort on these criteria for correctness,
+     but enforcing a more strict ordering prevents unstable qsort
+     from behaving differently with different implementations.
+     Without the code below we get correct but different results
+     on Solaris 2.7 and 2.8.  We would like to always produce the
+     same results no matter the host.  */
+
+  if (a->r_info != b->r_info)
+    return (a->r_info - b->r_info);
+
+  return (a->r_addend - b->r_addend);
 }
 
 
 static int
-internal_reloc_compare (ap, bp)
-     const PTR ap;
-     const PTR bp;
+internal_reloc_matches (const void *ap, const void *bp)
 {
   const Elf_Internal_Rela *a = (const Elf_Internal_Rela *) ap;
   const Elf_Internal_Rela *b = (const Elf_Internal_Rela *) bp;
 
+  /* Check if one entry overlaps with the other; this shouldn't happen
+     except when searching for a match.  */
   return (a->r_offset - b->r_offset);
 }
 
 
 char *
-xtensa_get_property_section_name (sec, base_name)
-     asection *sec;
-     const char *base_name;
+xtensa_get_property_section_name (asection *sec, const char *base_name)
 {
   if (strncmp (sec->name, ".gnu.linkonce.", linkonce_len) == 0)
     {
       char *prop_sec_name;
       const char *suffix;
-      char linkonce_kind = 0;
+      char *linkonce_kind = 0;
 
       if (strcmp (base_name, XTENSA_INSN_SEC_NAME) == 0) 
-       linkonce_kind = 'x';
+       linkonce_kind = "x";
       else if (strcmp (base_name, XTENSA_LIT_SEC_NAME) == 0) 
-       linkonce_kind = 'p';
+       linkonce_kind = "p";
+      else if (strcmp (base_name, XTENSA_PROP_SEC_NAME) == 0)
+       linkonce_kind = "prop.";
       else
        abort ();
 
-      prop_sec_name = (char *) bfd_malloc (strlen (sec->name) + 1);
+      prop_sec_name = (char *) bfd_malloc (strlen (sec->name)
+                                          + strlen (linkonce_kind) + 1);
       memcpy (prop_sec_name, ".gnu.linkonce.", linkonce_len);
-      prop_sec_name[linkonce_len] = linkonce_kind;
-      prop_sec_name[linkonce_len + 1] = '.';
+      strcpy (prop_sec_name + linkonce_len, linkonce_kind);
 
       suffix = sec->name + linkonce_len;
       /* For backward compatibility, replace "t." instead of inserting
-        the new linkonce_kind.  */
-      if (strncmp (suffix, "t.", 2) == 0)
-       suffix += 2;
-      strcpy (prop_sec_name + linkonce_len + 2, suffix);
+         the new linkonce_kind (but not for "prop" sections).  */
+      if (strncmp (suffix, "t.", 2) == 0 && linkonce_kind[1] == '.')
+        suffix += 2;
+      strcat (prop_sec_name + linkonce_len, suffix);
 
       return prop_sec_name;
     }
@@ -5652,21 +9367,42 @@ xtensa_get_property_section_name (sec, base_name)
   return strdup (base_name);
 }
 
+
+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)
+    return (XTENSA_PROP_INSN
+           | XTENSA_PROP_INSN_NO_TRANSFORM
+           | XTENSA_PROP_INSN_NO_REORDER);
+
+  if (xtensa_is_littable_section (sec))
+    return (XTENSA_PROP_LITERAL
+           | XTENSA_PROP_INSN_NO_TRANSFORM
+           | XTENSA_PROP_INSN_NO_REORDER);
+
+  return 0;
+}
+
 \f
 /* Other functions called directly by the linker.  */
 
 bfd_boolean
-xtensa_callback_required_dependence (abfd, sec, link_info, callback, closure)
-     bfd *abfd;
-     asection *sec;
-     struct bfd_link_info *link_info;
-     deps_callback_t callback;
-     PTR closure;
+xtensa_callback_required_dependence (bfd *abfd,
+                                    asection *sec,
+                                    struct bfd_link_info *link_info,
+                                    deps_callback_t callback,
+                                    void *closure)
 {
   Elf_Internal_Rela *internal_relocs;
   bfd_byte *contents;
   unsigned i;
   bfd_boolean ok = TRUE;
+  bfd_size_type sec_size;
+
+  sec_size = bfd_get_section_limit (abfd, sec);
 
   /* ".plt*" sections have no explicit relocations but they contain L32R
      instructions that reference the corresponding ".got.plt*" sections.  */
@@ -5694,43 +9430,43 @@ xtensa_callback_required_dependence (abfd, sec, link_info, callback, closure)
       /* Assume worst-case offsets: L32R at the very end of the ".plt"
         section referencing a literal at the very beginning of
         ".got.plt".  This is very close to the real dependence, anyway.  */
-      (*callback) (sec, sec->size, sgotplt, 0, closure);
+      (*callback) (sec, sec_size, sgotplt, 0, closure);
     }
 
   internal_relocs = retrieve_internal_relocs (abfd, sec, 
                                              link_info->keep_memory);
   if (internal_relocs == NULL
-      || sec->reloc_count == 0) 
+      || sec->reloc_count == 0)
     return ok;
 
   /* Cache the contents for the duration of this scan.  */
   contents = retrieve_contents (abfd, sec, link_info->keep_memory);
-  if (contents == NULL && sec->size != 0)
+  if (contents == NULL && sec_size != 0)
     {
       ok = FALSE;
       goto error_return;
     }
 
-  if (xtensa_default_isa == NULL)
-    xtensa_isa_init ();
+  if (!xtensa_default_isa)
+    xtensa_default_isa = xtensa_isa_init (0, 0);
 
-  for (i = 0; i < sec->reloc_count; i++) 
+  for (i = 0; i < sec->reloc_count; i++)
     {
       Elf_Internal_Rela *irel = &internal_relocs[i];
-      if (is_l32r_relocation (sec, contents, irel))
+      if (is_l32r_relocation (abfd, sec, contents, irel))
        {
          r_reloc l32r_rel;
          asection *target_sec;
          bfd_vma target_offset;
-         
-         r_reloc_init (&l32r_rel, abfd, irel);
+
+         r_reloc_init (&l32r_rel, abfd, irel, contents, sec_size);
          target_sec = NULL;
          target_offset = 0;
          /* L32Rs must be local to the input file.  */
          if (r_reloc_is_defined (&l32r_rel))
            {
              target_sec = r_reloc_get_section (&l32r_rel);
-             target_offset = r_reloc_get_target_offset (&l32r_rel);
+             target_offset = l32r_rel.target_offset;
            }
          (*callback) (sec, irel->r_offset, target_sec, target_offset,
                       closure);
@@ -5767,7 +9503,7 @@ static struct bfd_elf_special_section const elf_xtensa_special_sections[]=
    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
-   (e.g., prep_headers() in elf.c) should be changed to use EM_XTENSA.  */
+   should be changed to use EM_XTENSA.  */
 #define ELF_MACHINE_CODE               EM_XTENSA_OLD
 #define ELF_MACHINE_ALT1               EM_XTENSA