* elf32-xtensa.c (elf_xtensa_relocate_section): Remove updates of
[binutils-gdb.git] / bfd / elf32-xtensa.c
index 10e00aa27da8ac2d1e49e659b67bde32d2e00363..dac6896a015293e4a8862ccc2c17817a0944a6c7 100644 (file)
@@ -5,7 +5,7 @@
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of the
+   published by the Free Software Foundation; either version 3 of the
    License, or (at your option) any later version.
 
    This program is distributed in the hope that it will be useful, but
@@ -18,8 +18,8 @@
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
    02110-1301, USA.  */
 
-#include "bfd.h"
 #include "sysdep.h"
+#include "bfd.h"
 
 #include <stdarg.h>
 #include <strings.h>
@@ -103,7 +103,9 @@ static bfd_vma get_elf_r_symndx_offset (bfd *, unsigned long);
 static bfd_boolean is_reloc_sym_weak (bfd *, Elf_Internal_Rela *);
 static bfd_boolean pcrel_reloc_fits (xtensa_opcode, int, bfd_vma, bfd_vma);
 static bfd_boolean xtensa_is_property_section (asection *);
+static bfd_boolean xtensa_is_insntable_section (asection *);
 static bfd_boolean xtensa_is_littable_section (asection *);
+static bfd_boolean xtensa_is_proptable_section (asection *);
 static int internal_reloc_compare (const void *, const void *);
 static int internal_reloc_matches (const void *, const void *);
 extern asection *xtensa_get_property_section (asection *, const char *);
@@ -610,10 +612,10 @@ xtensa_read_table_entries (bfd *abfd,
   property_table_entry *blocks;
   int blk, block_count;
   bfd_size_type num_records;
-  Elf_Internal_Rela *internal_relocs;
-  bfd_vma section_addr;
+  Elf_Internal_Rela *internal_relocs, *irel, *rel_end;
+  bfd_vma section_addr, off;
   flagword predef_flags;
-  bfd_size_type table_entry_size;
+  bfd_size_type table_entry_size, section_limit;
 
   if (!section
       || !(section->flags & SEC_ALLOC)
@@ -649,67 +651,63 @@ xtensa_read_table_entries (bfd *abfd,
   else
     section_addr = section->vma;
 
-  /* If the file has not yet been relocated, process the relocations
-     and sort out the table entries that apply to the specified section.  */
   internal_relocs = retrieve_internal_relocs (abfd, table_section, TRUE);
   if (internal_relocs && !table_section->reloc_done)
     {
-      unsigned i;
-
-      for (i = 0; i < table_section->reloc_count; i++)
-       {
-         Elf_Internal_Rela *rel = &internal_relocs[i];
-         unsigned long r_symndx;
+      qsort (internal_relocs, table_section->reloc_count,
+            sizeof (Elf_Internal_Rela), internal_reloc_compare);
+      irel = internal_relocs;
+    }
+  else
+    irel = NULL;
 
-         if (ELF32_R_TYPE (rel->r_info) == R_XTENSA_NONE)
-           continue;
+  section_limit = bfd_get_section_limit (abfd, section);
+  rel_end = internal_relocs + table_section->reloc_count;
 
-         BFD_ASSERT (ELF32_R_TYPE (rel->r_info) == R_XTENSA_32);
-         r_symndx = ELF32_R_SYM (rel->r_info);
+  for (off = 0; off < table_size; off += table_entry_size) 
+    {
+      bfd_vma address = bfd_get_32 (abfd, table_data + off);
 
-         if (get_elf_r_symndx_section (abfd, r_symndx) == section)
-           {
-             bfd_vma sym_off = get_elf_r_symndx_offset (abfd, r_symndx);
-             BFD_ASSERT (sym_off == 0);
-             blocks[block_count].address =
-               (section_addr + sym_off + rel->r_addend
-                + bfd_get_32 (abfd, table_data + rel->r_offset));
-             blocks[block_count].size =
-               bfd_get_32 (abfd, table_data + rel->r_offset + 4);
-             if (predef_flags)
-               blocks[block_count].flags = predef_flags;
-             else
-               blocks[block_count].flags =
-                 bfd_get_32 (abfd, table_data + rel->r_offset + 8);
-             block_count++;
-           }
+      /* Skip any relocations before the current offset.  This should help
+        avoid confusion caused by unexpected relocations for the preceding
+        table entry.  */
+      while (irel &&
+            (irel->r_offset < off
+             || (irel->r_offset == off
+                 && ELF32_R_TYPE (irel->r_info) == R_XTENSA_NONE)))
+       {
+         irel += 1;
+         if (irel >= rel_end)
+           irel = 0;
        }
-    }
-  else
-    {
-      /* The file has already been relocated and the addresses are
-        already in the table.  */
-      bfd_vma off;
-      bfd_size_type section_limit = bfd_get_section_limit (abfd, section);
 
-      for (off = 0; off < table_size; off += table_entry_size) 
+      if (irel && irel->r_offset == off)
        {
-         bfd_vma address = bfd_get_32 (abfd, table_data + off);
+         bfd_vma sym_off;
+         unsigned long r_symndx = ELF32_R_SYM (irel->r_info);
+         BFD_ASSERT (ELF32_R_TYPE (irel->r_info) == R_XTENSA_32);
 
-         if (address >= section_addr
-             && address < section_addr + section_limit)
-           {
-             blocks[block_count].address = address;
-             blocks[block_count].size =
-               bfd_get_32 (abfd, table_data + off + 4);
-             if (predef_flags)
-               blocks[block_count].flags = predef_flags;
-             else
-               blocks[block_count].flags =
-                 bfd_get_32 (abfd, table_data + off + 8);
-             block_count++;
-           }
+         if (get_elf_r_symndx_section (abfd, r_symndx) != section)
+           continue;
+
+         sym_off = get_elf_r_symndx_offset (abfd, r_symndx);
+         BFD_ASSERT (sym_off == 0);
+         address += (section_addr + sym_off + irel->r_addend);
+       }
+      else
+       {
+         if (address < section_addr
+             || address >= section_addr + section_limit)
+           continue;
        }
+
+      blocks[block_count].address = address;
+      blocks[block_count].size = bfd_get_32 (abfd, table_data + off + 4);
+      if (predef_flags)
+       blocks[block_count].flags = predef_flags;
+      else
+       blocks[block_count].flags = bfd_get_32 (abfd, table_data + off + 8);
+      block_count++;
     }
 
   release_contents (table_section, table_data);
@@ -946,7 +944,9 @@ elf_xtensa_check_relocs (bfd *abfd,
        case R_XTENSA_GNU_VTENTRY:
          /* This relocation describes which C++ vtable entries are actually
             used.  Record for later use during GC.  */
-         if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+         BFD_ASSERT (h != NULL);
+         if (h != NULL
+             && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
            return FALSE;
          break;
 
@@ -959,6 +959,44 @@ elf_xtensa_check_relocs (bfd *abfd,
 }
 
 
+static void
+elf_xtensa_make_sym_local (struct bfd_link_info *info,
+                           struct elf_link_hash_entry *h)
+{
+  if (info->shared)
+    {
+      if (h->plt.refcount > 0)
+        {
+         /* For shared objects, there's no need for PLT entries for local
+            symbols (use RELATIVE relocs instead of JMP_SLOT relocs).  */
+          if (h->got.refcount < 0)
+            h->got.refcount = 0;
+          h->got.refcount += h->plt.refcount;
+          h->plt.refcount = 0;
+        }
+    }
+  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.  */
+  elf_xtensa_make_sym_local (info, h);
+
+  _bfd_elf_link_hash_hide_symbol (info, h, force_local);
+}
+
+
 /* Return the section that should be marked against GC for a given
    relocation.  */
 
@@ -1206,26 +1244,7 @@ elf_xtensa_allocate_dynrelocs (struct elf_link_hash_entry *h, void *arg)
   is_dynamic = elf_xtensa_dynamic_symbol_p (h, info);
 
   if (! is_dynamic)
-    {
-      if (info->shared)
-       {
-         /* For shared objects, there's no need for PLT entries for local
-            symbols (use RELATIVE relocs instead of JMP_SLOT relocs).  */
-         if (h->plt.refcount > 0)
-           {
-             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;
-       }
-    }
+    elf_xtensa_make_sym_local (info, h);
 
   if (h->plt.refcount > 0)
     htab->srelplt->size += (h->plt.refcount * sizeof (Elf32_External_Rela));
@@ -2076,7 +2095,6 @@ elf_xtensa_relocate_section (bfd *output_bfd,
              if (!do_fix_for_relocatable_link (rel, input_bfd, input_section,
                                                contents))
                return FALSE;
-             r_type = ELF32_R_TYPE (rel->r_info);
            }
 
          if (r_type == R_XTENSA_ASM_SIMPLIFY)
@@ -2146,10 +2164,6 @@ elf_xtensa_relocate_section (bfd *output_bfd,
          /* Check if this references a section in another input file.  */
          do_fix_for_final_link (rel, input_bfd, input_section, contents,
                                 &relocation);
-
-         /* Update some already cached values.  */
-         r_type = ELF32_R_TYPE (rel->r_info);
-         howto = &elf_howto_table[r_type];
        }
 
       /* Sanity check the address.  */
@@ -2632,19 +2646,19 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd,
          break;
 
        case DT_XTENSA_GOT_LOC_OFF:
-         dyn.d_un.d_ptr = htab->sgotloc->vma;
+         dyn.d_un.d_ptr = htab->sgotloc->output_section->vma;
          break;
 
        case DT_PLTGOT:
-         dyn.d_un.d_ptr = htab->sgot->vma;
+         dyn.d_un.d_ptr = htab->sgot->output_section->vma;
          break;
 
        case DT_JMPREL:
-         dyn.d_un.d_ptr = htab->srelplt->vma;
+         dyn.d_un.d_ptr = htab->srelplt->output_section->vma;
          break;
 
        case DT_PLTRELSZ:
-         dyn.d_un.d_val = htab->srelplt->size;
+         dyn.d_un.d_val = htab->srelplt->output_section->size;
          break;
 
        case DT_RELASZ:
@@ -2655,7 +2669,7 @@ elf_xtensa_finish_dynamic_sections (bfd *output_bfd,
             for .rela.plt to follow all other relocation sections, we
             don't have to worry about changing the DT_RELA entry.  */
          if (htab->srelplt)
-           dyn.d_un.d_val -= htab->srelplt->size;
+           dyn.d_un.d_val -= htab->srelplt->output_section->size;
          break;
        }
 
@@ -2829,16 +2843,22 @@ elf_xtensa_discard_info_for_section (bfd *abfd,
   bfd_byte *contents;
   bfd_vma section_size;
   bfd_vma offset, actual_offset;
-  size_t removed_bytes = 0;
-
-  section_size = sec->size;
-  if (section_size == 0 || section_size % 8 != 0)
-    return FALSE;
+  bfd_size_type removed_bytes = 0;
+  bfd_size_type entry_size;
 
   if (sec->output_section
       && bfd_is_abs_section (sec->output_section))
     return FALSE;
 
+  if (xtensa_is_proptable_section (sec))
+    entry_size = 12;
+  else
+    entry_size = 8;
+
+  section_size = sec->size;
+  if (section_size == 0 || section_size % entry_size != 0)
+    return FALSE;
+
   contents = retrieve_contents (abfd, sec, info->keep_memory);
   if (!contents)
     return FALSE;
@@ -2850,10 +2870,15 @@ elf_xtensa_discard_info_for_section (bfd *abfd,
       return FALSE;
     }
 
+  /* Sort the relocations.  They should already be in order when
+     relaxation is enabled, but it might not be.  */
+  qsort (cookie->rels, sec->reloc_count, sizeof (Elf_Internal_Rela),
+        internal_reloc_compare);
+
   cookie->rel = cookie->rels;
   cookie->relend = cookie->rels + sec->reloc_count;
 
-  for (offset = 0; offset < section_size; offset += 8)
+  for (offset = 0; offset < section_size; offset += entry_size)
     {
       actual_offset = offset - removed_bytes;
 
@@ -2877,11 +2902,11 @@ elf_xtensa_discard_info_for_section (bfd *abfd,
              if (ELF32_R_TYPE (cookie->rel->r_info) != R_XTENSA_NONE)
                {
                  /* Shift the contents up.  */
-                 if (offset + 8 < section_size)
+                 if (offset + entry_size < section_size)
                    memmove (&contents[actual_offset],
-                            &contents[actual_offset+8],
-                            section_size - offset - 8);
-                 removed_bytes += 8;
+                            &contents[actual_offset + entry_size],
+                            section_size - offset - entry_size);
+                 removed_bytes += entry_size;
                }
 
              /* Remove this relocation.  */
@@ -4750,20 +4775,45 @@ text_action_add_literal (text_action_list *l,
 }
 
 
-static bfd_vma 
-offset_with_removed_text (text_action_list *action_list, bfd_vma offset)
+/* Find the total offset adjustment for the relaxations specified by
+   text_actions, beginning from a particular starting action.  This is
+   typically used from offset_with_removed_text to search an entire list of
+   actions, but it may also be called directly when adjusting adjacent offsets
+   so that each search may begin where the previous one left off.  */
+
+static int
+removed_by_actions (text_action **p_start_action,
+                   bfd_vma offset,
+                   bfd_boolean before_fill)
 {
   text_action *r;
   int removed = 0;
 
-  for (r = action_list->head; r && r->offset <= offset; r = r->next)
+  r = *p_start_action;
+  while (r)
     {
-      if (r->offset < offset
-         || (r->action == ta_fill && r->removed_bytes < 0))
-       removed += r->removed_bytes;
+      if (r->offset > offset)
+       break;
+
+      if (r->offset == offset
+         && (before_fill || r->action != ta_fill || r->removed_bytes >= 0))
+       break;
+
+      removed += r->removed_bytes;
+
+      r = r->next;
     }
 
-  return (offset - removed);
+  *p_start_action = r;
+  return removed;
+}
+
+
+static bfd_vma 
+offset_with_removed_text (text_action_list *action_list, bfd_vma offset)
+{
+  text_action *r = action_list->head;
+  return offset - removed_by_actions (&r, offset, FALSE);
 }
 
 
@@ -4780,20 +4830,6 @@ action_list_count (text_action_list *action_list)
 }
 
 
-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 *
@@ -5091,7 +5127,6 @@ struct reloc_bfd_fix_struct
   bfd_vma src_offset;
   unsigned src_type;                   /* Relocation type.  */
   
-  bfd *target_abfd;
   asection *target_sec;
   bfd_vma target_offset;
   bfd_boolean translated;
@@ -5104,7 +5139,6 @@ static reloc_bfd_fix *
 reloc_bfd_fix_init (asection *src_sec,
                    bfd_vma src_offset,
                    unsigned src_type,
-                   bfd *target_abfd,
                    asection *target_sec,
                    bfd_vma target_offset,
                    bfd_boolean translated)
@@ -5115,7 +5149,6 @@ reloc_bfd_fix_init (asection *src_sec,
   fix->src_sec = src_sec;
   fix->src_offset = src_offset;
   fix->src_type = src_type;
-  fix->target_abfd = target_abfd;
   fix->target_sec = target_sec;
   fix->target_offset = target_offset;
   fix->translated = translated;
@@ -5513,7 +5546,7 @@ extend_ebb_bounds_forward (ebb_t *ebb)
 
       new_entry = &ebb->ptbl[ebb->end_ptbl_idx + 1];
       if (((new_entry->flags & XTENSA_PROP_INSN) == 0)
-         || ((new_entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) != 0)
+         || ((new_entry->flags & XTENSA_PROP_NO_TRANSFORM) != 0)
          || ((the_entry->flags & XTENSA_PROP_ALIGN) != 0))
        break;
 
@@ -5586,7 +5619,7 @@ extend_ebb_bounds_backward (ebb_t *ebb)
 
       new_entry = &ebb->ptbl[ebb->start_ptbl_idx - 1];
       if ((new_entry->flags & XTENSA_PROP_INSN) == 0
-         || ((new_entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) != 0)
+         || ((new_entry->flags & XTENSA_PROP_NO_TRANSFORM) != 0)
          || ((new_entry->flags & XTENSA_PROP_ALIGN) != 0))
        return TRUE;
       if (new_entry->address + new_entry->size != the_entry->address)
@@ -5794,7 +5827,8 @@ static bfd_boolean compute_removed_literals
 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);
+  (const source_reloc *, int, const source_reloc *, int, asection *,
+   property_table_entry *, int);
 static bfd_boolean remove_dead_literal
   (bfd *, asection *, struct bfd_link_info *, Elf_Internal_Rela *,
    Elf_Internal_Rela *, source_reloc *, property_table_entry *, int); 
@@ -6452,7 +6486,7 @@ compute_text_actions (bfd *abfd,
          the_entry++;
        }
 
-      if (the_entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM)
+      if (the_entry->flags & XTENSA_PROP_NO_TRANSFORM)
          /* NO_REORDER is OK */
        continue;
 
@@ -6645,14 +6679,14 @@ compute_ebb_proposed_actions (ebb_constraint *ebb_table)
            goto decode_error;
 
          if ((entry->flags & XTENSA_PROP_INSN_NO_DENSITY) == 0
-             && (entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) == 0
+             && (entry->flags & XTENSA_PROP_NO_TRANSFORM) == 0
              && can_narrow_instruction (slotbuf, fmt, opcode) != 0)
            {
              /* Add an instruction narrow action.  */
              ebb_propose_action (ebb_table, EBB_NO_ALIGN, 0,
                                  ta_narrow_insn, offset, 0, FALSE);
            }
-         else if ((entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM) == 0
+         else if ((entry->flags & XTENSA_PROP_NO_TRANSFORM) == 0
                   && can_widen_instruction (slotbuf, fmt, opcode) != 0
                   && ! prev_instr_is_a_loop (ebb->contents,
                                              ebb->content_length, offset))
@@ -6897,6 +6931,8 @@ compute_ebb_actions (ebb_constraint *ebb_table)
                      bad_alignment = FALSE;
                      break;
                    }
+                 if (new_action->do_action)
+                   removed_bytes += new_action->removed_bytes;
                }
              if (!bad_alignment)
                {
@@ -7402,7 +7438,8 @@ compute_removed_literals (bfd *abfd,
       /* Check if the relocation was from an L32R that is being removed
         because a CALLX was converted to a direct CALL, and check if
         there are no other relocations to the literal.  */
-      if (is_removable_literal (rel, i, src_relocs, relax_info->src_count))
+      if (is_removable_literal (rel, i, src_relocs, relax_info->src_count, 
+                               sec, prop_table, ptblsize))
        {
          if (!remove_dead_literal (abfd, sec, link_info, internal_relocs,
                                    irel, rel, prop_table, ptblsize))
@@ -7486,12 +7523,22 @@ bfd_boolean
 is_removable_literal (const source_reloc *rel,
                      int i,
                      const source_reloc *src_relocs,
-                     int src_count)
+                     int src_count,
+                     asection *sec,
+                     property_table_entry *prop_table,
+                     int ptblsize)
 {
   const source_reloc *curr_rel;
+  property_table_entry *entry;
+
   if (!rel->is_null)
     return FALSE;
   
+  entry = elf_xtensa_find_property_entry (prop_table, ptblsize, 
+                                         sec->vma + rel->r_rel.target_offset);
+  if (entry && (entry->flags & XTENSA_PROP_NO_TRANSFORM))
+    return FALSE;
+
   for (++i; i < src_count; ++i)
     {
       curr_rel = &src_relocs[i];
@@ -7781,7 +7828,7 @@ coalesce_shared_literal (asection *sec,
 
   entry = elf_xtensa_find_property_entry
     (prop_table, ptblsize, sec->vma + rel->r_rel.target_offset);
-  if (entry && (entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM))
+  if (entry && (entry->flags & XTENSA_PROP_NO_TRANSFORM))
     return TRUE;
 
   /* Mark that the literal will be coalesced.  */
@@ -8187,7 +8234,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
              addend_displacement =
                new_reloc.target_offset + new_reloc.virtual_offset;
 
-             fix = reloc_bfd_fix_init (sec, source_offset, r_type, 0,
+             fix = reloc_bfd_fix_init (sec, source_offset, r_type,
                                        r_reloc_get_section (&new_reloc),
                                        addend_displacement, TRUE);
              add_fix (sec, fix);
@@ -8740,7 +8787,7 @@ move_literal (bfd *abfd,
 
       /* 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,
+      fix = reloc_bfd_fix_init (sec, offset, r_type,
                                r_reloc_get_section (r_rel),
                                r_rel->target_offset + r_rel->virtual_offset,
                                FALSE);
@@ -8840,12 +8887,13 @@ relax_property_section (bfd *abfd,
 {
   Elf_Internal_Rela *internal_relocs;
   bfd_byte *contents;
-  unsigned i, nexti;
+  unsigned i;
   bfd_boolean ok = TRUE;
   bfd_boolean is_full_prop_section;
   size_t last_zfill_target_offset = 0;
   asection *last_zfill_target_sec = NULL;
   bfd_size_type sec_size;
+  bfd_size_type entry_size;
 
   sec_size = bfd_get_section_limit (abfd, sec);
   internal_relocs = retrieve_internal_relocs (abfd, sec, 
@@ -8857,9 +8905,11 @@ relax_property_section (bfd *abfd,
       goto error_return;
     }
 
-  is_full_prop_section =
-    (   CONST_STRNEQ (sec->name, XTENSA_PROP_SEC_NAME)
-     || CONST_STRNEQ (sec->name, ".gnu.linkonce.prop."));
+  is_full_prop_section = xtensa_is_proptable_section (sec);
+  if (is_full_prop_section)
+    entry_size = 12;
+  else
+    entry_size = 8;
 
   if (internal_relocs)
     {
@@ -8888,12 +8938,8 @@ relax_property_section (bfd *abfd,
          size_p = &contents[irel->r_offset + 4];
          flags_p = NULL;
          if (is_full_prop_section)
-           {
-             flags_p = &contents[irel->r_offset + 8];
-             BFD_ASSERT (irel->r_offset + 12 <= sec_size);
-           }
-         else
-           BFD_ASSERT (irel->r_offset + 8 <= sec_size);
+           flags_p = &contents[irel->r_offset + 8];
+         BFD_ASSERT (irel->r_offset + entry_size <= sec_size);
 
          target_sec = r_reloc_get_section (&val.r_rel);
          target_relax_info = get_xtensa_relax_info (target_sec);
@@ -8903,14 +8949,16 @@ relax_property_section (bfd *abfd,
                  || target_relax_info->is_relaxable_asm_section ))
            {
              /* Translate the relocation's destination.  */
-             bfd_vma new_offset, new_end_offset;
+             bfd_vma old_offset = val.r_rel.target_offset;
+             bfd_vma new_offset;
              long old_size, new_size;
-
-             new_offset = offset_with_removed_text
-               (&target_relax_info->action_list, val.r_rel.target_offset);
+             text_action *act = target_relax_info->action_list.head;
+             new_offset = old_offset -
+               removed_by_actions (&act, old_offset, FALSE);
 
              /* Assert that we are not out of bounds.  */
              old_size = bfd_get_32 (abfd, size_p);
+             new_size = old_size;
 
              if (old_size == 0)
                {
@@ -8922,39 +8970,34 @@ relax_property_section (bfd *abfd,
                     offset before or after the fill address depending
                     on whether the expanding unreachable entry
                     preceeds it.  */
-                 if (last_zfill_target_sec
-                     && last_zfill_target_sec == target_sec
-                     && last_zfill_target_offset == val.r_rel.target_offset)
-                   new_end_offset = new_offset;
-                 else
+                 if (last_zfill_target_sec == 0
+                     || last_zfill_target_sec != target_sec
+                     || last_zfill_target_offset != old_offset)
                    {
-                     new_end_offset = new_offset;
-                     new_offset = offset_with_removed_text_before_fill
-                       (&target_relax_info->action_list,
-                        val.r_rel.target_offset);
+                     bfd_vma new_end_offset = new_offset;
+
+                     /* Recompute the new_offset, but this time don't
+                        include any fill inserted by relaxation.  */
+                     act = target_relax_info->action_list.head;
+                     new_offset = old_offset -
+                       removed_by_actions (&act, old_offset, TRUE);
 
                      /* If it is not unreachable and we have not yet
                         seen an unreachable at this address, place it
                         before the fill address.  */
-                     if (!flags_p
-                         || (bfd_get_32 (abfd, flags_p)
-                             & XTENSA_PROP_UNREACHABLE) == 0)
-                       new_end_offset = new_offset;
-                     else
+                     if (flags_p && (bfd_get_32 (abfd, flags_p)
+                                     & XTENSA_PROP_UNREACHABLE) != 0)
                        {
+                         new_size = new_end_offset - new_offset;
+
                          last_zfill_target_sec = target_sec;
-                         last_zfill_target_offset = val.r_rel.target_offset;
+                         last_zfill_target_offset = old_offset;
                        }
                    }
                }
              else
-               {
-                 new_end_offset = offset_with_removed_text_before_fill
-                   (&target_relax_info->action_list,
-                    val.r_rel.target_offset + old_size);
-               }
-
-             new_size = new_end_offset - new_offset;
+               new_size -=
+                   removed_by_actions (&act, old_offset + old_size, TRUE);
 
              if (new_size != old_size)
                {
@@ -8962,9 +9005,9 @@ relax_property_section (bfd *abfd,
                  pin_contents (sec, contents);
                }
 
-             if (new_offset != val.r_rel.target_offset)
+             if (new_offset != old_offset)
                {
-                 bfd_vma diff = new_offset - val.r_rel.target_offset;
+                 bfd_vma diff = new_offset - old_offset;
                  irel->r_addend += diff;
                  pin_internal_relocs (sec, internal_relocs);
                }
@@ -8977,74 +9020,105 @@ relax_property_section (bfd *abfd,
      reclaim the space in the output section, so we do this twice.  */
 
   if (internal_relocs && (!link_info->relocatable
-                         || strcmp (sec->name, XTENSA_LIT_SEC_NAME) == 0))
+                         || xtensa_is_littable_section (sec)))
     {
       Elf_Internal_Rela *last_irel = NULL;
+      Elf_Internal_Rela *irel, *next_rel, *rel_end;
       int removed_bytes = 0;
-      bfd_vma offset, last_irel_offset;
+      bfd_vma 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.
+      /* Walk over memory and relocations at the same time.
          This REQUIRES that the internal_relocs be sorted by offset.  */
       qsort (internal_relocs, sec->reloc_count, sizeof (Elf_Internal_Rela),
             internal_reloc_compare);
-      nexti = 0; /* Index into internal_relocs.  */
 
       pin_internal_relocs (sec, internal_relocs);
       pin_contents (sec, contents);
 
-      last_irel_offset = (bfd_vma) -1;
+      next_rel = internal_relocs;
+      rel_end = internal_relocs + sec->reloc_count;
+
       section_size = sec->size;
       BFD_ASSERT (section_size % entry_size == 0);
 
       for (offset = 0; offset < section_size; offset += entry_size)
        {
-         Elf_Internal_Rela *irel, *next_irel;
+         Elf_Internal_Rela *offset_rel, *extra_rel;
          bfd_vma bytes_to_remove, size, actual_offset;
-         bfd_boolean remove_this_irel;
+         bfd_boolean remove_this_rel;
          flagword flags;
 
-         irel = NULL;
-         next_irel = NULL;
-
-         /* Find the next two relocations (if there are that many left),
-            skipping over any R_XTENSA_NONE relocs.  On entry, "nexti" is
-            the starting reloc index.  After these two loops, "i"
-            is the index of the first non-NONE reloc past that starting
-            index, and "nexti" is the index for the next non-NONE reloc
-            after "i".  */
+         /* Find the first relocation for the entry at the current offset.
+            Adjust the offsets of any extra relocations for the previous
+            entry.  */
+         offset_rel = NULL;
+         if (next_rel)
+           {
+             for (irel = next_rel; irel < rel_end; irel++)
+               {
+                 if ((irel->r_offset == offset
+                      && ELF32_R_TYPE (irel->r_info) != R_XTENSA_NONE)
+                     || irel->r_offset > offset)
+                   {
+                     offset_rel = irel;
+                     break;
+                   }
+                 irel->r_offset -= removed_bytes;
+               }
+           }
 
-         for (i = nexti; i < sec->reloc_count; i++)
+         /* Find the next relocation (if there are any left).  */
+         extra_rel = NULL;
+         if (offset_rel)
            {
-             if (ELF32_R_TYPE (internal_relocs[i].r_info) != R_XTENSA_NONE)
+             for (irel = offset_rel + 1; irel < rel_end; irel++)
                {
-                 irel = &internal_relocs[i];
-                 break;
+                 if (ELF32_R_TYPE (irel->r_info) != R_XTENSA_NONE)
+                   {
+                     extra_rel = irel;
+                     break;
+                   }
                }
-             internal_relocs[i].r_offset -= removed_bytes;
            }
 
-         for (nexti = i + 1; nexti < sec->reloc_count; nexti++)
+         /* Check if there are relocations on the current entry.  There
+            should usually be a relocation on the offset field.  If there
+            are relocations on the size or flags, then we can't optimize
+            this entry.  Also, find the next relocation to examine on the
+            next iteration.  */
+         if (offset_rel)
            {
-             if (ELF32_R_TYPE (internal_relocs[nexti].r_info)
-                 != R_XTENSA_NONE)
+             if (offset_rel->r_offset >= offset + entry_size)
                {
-                 next_irel = &internal_relocs[nexti];
-                 break;
+                 next_rel = offset_rel;
+                 /* There are no relocations on the current entry, but we
+                    might still be able to remove it if the size is zero.  */
+                 offset_rel = NULL;
+               }
+             else if (offset_rel->r_offset > offset
+                      || (extra_rel
+                          && extra_rel->r_offset < offset + entry_size))
+               {
+                 /* There is a relocation on the size or flags, so we can't
+                    do anything with this entry.  Continue with the next.  */
+                 next_rel = offset_rel;
+                 continue;
+               }
+             else
+               {
+                 BFD_ASSERT (offset_rel->r_offset == offset);
+                 offset_rel->r_offset -= removed_bytes;
+                 next_rel = offset_rel + 1;
                }
-             internal_relocs[nexti].r_offset -= removed_bytes;
            }
+         else
+           next_rel = NULL;
 
-         remove_this_irel = FALSE;
+         remove_this_rel = FALSE;
          bytes_to_remove = 0;
          actual_offset = offset - removed_bytes;
          size = bfd_get_32 (abfd, &contents[actual_offset + 4]);
@@ -9054,90 +9128,64 @@ relax_property_section (bfd *abfd,
          else
            flags = predef_flags;
 
-         /* Check that the irels are sorted by offset,
-            with only one per address.  */
-         BFD_ASSERT (!irel || (int) irel->r_offset > (int) last_irel_offset); 
-         BFD_ASSERT (!next_irel || next_irel->r_offset > irel->r_offset);
-
-         /* Make sure there aren't relocs on the size or flag fields.  */
-         if ((irel && irel->r_offset == offset + 4)
-             || (is_full_prop_section 
-                 && irel && irel->r_offset == offset + 8))
-           {
-             irel->r_offset -= removed_bytes;
-             last_irel_offset = irel->r_offset;
-           }
-         else if (next_irel && (next_irel->r_offset == offset + 4
-                                || (is_full_prop_section 
-                                    && next_irel->r_offset == offset + 8)))
-           {
-             nexti += 1;
-             irel->r_offset -= removed_bytes;
-             next_irel->r_offset -= removed_bytes;
-             last_irel_offset = next_irel->r_offset;
-           }
-         else if (size == 0 && (flags & XTENSA_PROP_ALIGN) == 0
-                  && (flags & XTENSA_PROP_UNREACHABLE) == 0)
+         if (size == 0
+             && (flags & XTENSA_PROP_ALIGN) == 0
+             && (flags & XTENSA_PROP_UNREACHABLE) == 0)
            {
              /* Always remove entries with zero size and no alignment.  */
              bytes_to_remove = entry_size;
-             if (irel && irel->r_offset == offset)
-               {
-                 remove_this_irel = TRUE;
-
-                 irel->r_offset -= removed_bytes;
-                 last_irel_offset = irel->r_offset;
-               }
+             if (offset_rel)
+               remove_this_rel = TRUE;
            }
-         else if (irel && irel->r_offset == offset)
+         else if (offset_rel
+                  && ELF32_R_TYPE (offset_rel->r_info) == R_XTENSA_32)
            {
-             if (ELF32_R_TYPE (irel->r_info) == R_XTENSA_32)
+             if (last_irel)
                {
-                 if (last_irel)
+                 flagword old_flags;
+                 bfd_vma old_size =
+                   bfd_get_32 (abfd, &contents[last_irel->r_offset + 4]);
+                 bfd_vma old_address =
+                   (last_irel->r_addend
+                    + bfd_get_32 (abfd, &contents[last_irel->r_offset]));
+                 bfd_vma new_address =
+                   (offset_rel->r_addend
+                    + bfd_get_32 (abfd, &contents[actual_offset]));
+                 if (is_full_prop_section) 
+                   old_flags = bfd_get_32
+                     (abfd, &contents[last_irel->r_offset + 8]);
+                 else
+                   old_flags = predef_flags;
+
+                 if ((ELF32_R_SYM (offset_rel->r_info)
+                      == ELF32_R_SYM (last_irel->r_info))
+                     && old_address + old_size == new_address
+                     && old_flags == flags
+                     && (old_flags & XTENSA_PROP_INSN_BRANCH_TARGET) == 0
+                     && (old_flags & XTENSA_PROP_INSN_LOOP_TARGET) == 0)
                    {
-                     flagword old_flags;
-                     bfd_vma old_size =
-                       bfd_get_32 (abfd, &contents[last_irel->r_offset + 4]);
-                     bfd_vma old_address =
-                       (last_irel->r_addend
-                        + bfd_get_32 (abfd, &contents[last_irel->r_offset]));
-                     bfd_vma new_address =
-                       (irel->r_addend
-                        + bfd_get_32 (abfd, &contents[actual_offset]));
-                     if (is_full_prop_section) 
-                       old_flags = bfd_get_32
-                         (abfd, &contents[last_irel->r_offset + 8]);
-                     else
-                       old_flags = predef_flags;
-
-                     if ((ELF32_R_SYM (irel->r_info)
-                          == ELF32_R_SYM (last_irel->r_info))
-                         && old_address + old_size == new_address
-                         && old_flags == flags
-                         && (old_flags & XTENSA_PROP_INSN_BRANCH_TARGET) == 0
-                         && (old_flags & XTENSA_PROP_INSN_LOOP_TARGET) == 0)
-                       {
-                         /* Fix the old size.  */
-                         bfd_put_32 (abfd, old_size + size,
-                                     &contents[last_irel->r_offset + 4]);
-                         bytes_to_remove = entry_size;
-                         remove_this_irel = TRUE;
-                       }
-                     else
-                       last_irel = irel;
+                     /* Fix the old size.  */
+                     bfd_put_32 (abfd, old_size + size,
+                                 &contents[last_irel->r_offset + 4]);
+                     bytes_to_remove = entry_size;
+                     remove_this_rel = TRUE;
                    }
                  else
-                   last_irel = irel;
+                   last_irel = offset_rel;
                }
-
-             irel->r_offset -= removed_bytes;
-             last_irel_offset = irel->r_offset;
+             else
+               last_irel = offset_rel;
            }
 
-         if (remove_this_irel)
+         if (remove_this_rel)
            {
-             irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE);
-             irel->r_offset -= bytes_to_remove;
+             offset_rel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE);
+             /* In case this is the last entry, move the relocation offset
+                to the previous entry, if there is one.  */
+             if (offset_rel->r_offset >= bytes_to_remove)
+               offset_rel->r_offset -= bytes_to_remove;
+             else
+               offset_rel->r_offset = 0;
            }
 
          if (bytes_to_remove != 0)
@@ -9152,6 +9200,10 @@ relax_property_section (bfd *abfd,
 
       if (removed_bytes)
        {
+         /* Fix up any extra relocations on the last entry.  */
+         for (irel = next_rel; irel < rel_end; irel++)
+           irel->r_offset -= removed_bytes;
+
          /* Clear the removed bytes.  */
          memset (&contents[section_size - removed_bytes], 0, removed_bytes);
 
@@ -9208,19 +9260,14 @@ relax_section_symbols (bfd *abfd, asection *sec)
 
       if (isym->st_shndx == sec_shndx)
        {
-         bfd_vma new_address = offset_with_removed_text
-           (&relax_info->action_list, isym->st_value);
-         bfd_vma new_size = isym->st_size;
+         text_action *act = relax_info->action_list.head;
+         bfd_vma orig_addr = isym->st_value;
 
-         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 -= removed_by_actions (&act, orig_addr, FALSE);
 
-         isym->st_value = new_address;
-         isym->st_size = new_size;
+         if (ELF32_ST_TYPE (isym->st_info) == STT_FUNC)
+           isym->st_size -=
+             removed_by_actions (&act, orig_addr + isym->st_size, FALSE);
        }
     }
 
@@ -9238,20 +9285,15 @@ relax_section_symbols (bfd *abfd, asection *sec)
           || sym_hash->root.type == bfd_link_hash_defweak)
          && sym_hash->root.u.def.section == sec)
        {
-         bfd_vma new_address = offset_with_removed_text
-           (&relax_info->action_list, sym_hash->root.u.def.value);
-         bfd_vma new_size = sym_hash->size;
+         text_action *act = relax_info->action_list.head;
+         bfd_vma orig_addr = sym_hash->root.u.def.value;
 
-         if (sym_hash->type == STT_FUNC)
-           {
-             bfd_vma new_end = offset_with_removed_text
-               (&relax_info->action_list,
-                sym_hash->root.u.def.value + sym_hash->size);
-             new_size = new_end - new_address;
-           }
+         sym_hash->root.u.def.value -=
+           removed_by_actions (&act, orig_addr, FALSE);
 
-         sym_hash->root.u.def.value = new_address;
-         sym_hash->size = new_size;
+         if (sym_hash->type == STT_FUNC)
+           sym_hash->size -=
+             removed_by_actions (&act, orig_addr + sym_hash->size, FALSE);
        }
     }
 
@@ -9526,20 +9568,23 @@ pcrel_reloc_fits (xtensa_opcode opc,
 }
 
 
-static int linkonce_len = sizeof (".gnu.linkonce.") - 1;
-
 static bfd_boolean 
 xtensa_is_property_section (asection *sec)
 {
-  if (CONST_STRNEQ (sec->name, XTENSA_INSN_SEC_NAME)
-      || CONST_STRNEQ (sec->name, XTENSA_LIT_SEC_NAME)
-      || CONST_STRNEQ (sec->name, XTENSA_PROP_SEC_NAME))
+  if (xtensa_is_insntable_section (sec)
+      || xtensa_is_littable_section (sec)
+      || xtensa_is_proptable_section (sec))
     return TRUE;
 
-  if (strncmp (".gnu.linkonce.", sec->name, linkonce_len) == 0
-      && (CONST_STRNEQ (&sec->name[linkonce_len], "x.")
-         || CONST_STRNEQ (&sec->name[linkonce_len], "p.")
-         || CONST_STRNEQ (&sec->name[linkonce_len], "prop.")))
+  return FALSE;
+}
+
+
+static bfd_boolean 
+xtensa_is_insntable_section (asection *sec)
+{
+  if (CONST_STRNEQ (sec->name, XTENSA_INSN_SEC_NAME)
+      || CONST_STRNEQ (sec->name, ".gnu.linkonce.x."))
     return TRUE;
 
   return FALSE;
@@ -9549,12 +9594,19 @@ xtensa_is_property_section (asection *sec)
 static bfd_boolean 
 xtensa_is_littable_section (asection *sec)
 {
-  if (CONST_STRNEQ (sec->name, XTENSA_LIT_SEC_NAME))
+  if (CONST_STRNEQ (sec->name, XTENSA_LIT_SEC_NAME)
+      || CONST_STRNEQ (sec->name, ".gnu.linkonce.p."))
     return TRUE;
 
-  if (strncmp (".gnu.linkonce.", sec->name, linkonce_len) == 0
-      && sec->name[linkonce_len] == 'p'
-      && sec->name[linkonce_len + 1] == '.')
+  return FALSE;
+}
+
+
+static bfd_boolean 
+xtensa_is_proptable_section (asection *sec)
+{
+  if (CONST_STRNEQ (sec->name, XTENSA_PROP_SEC_NAME)
+      || CONST_STRNEQ (sec->name, ".gnu.linkonce.prop."))
     return TRUE;
 
   return FALSE;
@@ -9611,6 +9663,8 @@ match_section_group (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
 }
 
 
+static int linkonce_len = sizeof (".gnu.linkonce.") - 1;
+
 asection *
 xtensa_get_property_section (asection *sec, const char *base_name)
 {
@@ -9685,15 +9739,14 @@ xtensa_get_property_section (asection *sec, const char *base_name)
 flagword
 xtensa_get_property_predef_flags (asection *sec)
 {
-  if (CONST_STRNEQ (sec->name, XTENSA_INSN_SEC_NAME)
-      || CONST_STRNEQ (sec->name, ".gnu.linkonce.x."))
+  if (xtensa_is_insntable_section (sec))
     return (XTENSA_PROP_INSN
-           | XTENSA_PROP_INSN_NO_TRANSFORM
+           | XTENSA_PROP_NO_TRANSFORM
            | XTENSA_PROP_INSN_NO_REORDER);
 
   if (xtensa_is_littable_section (sec))
     return (XTENSA_PROP_LITERAL
-           | XTENSA_PROP_INSN_NO_TRANSFORM
+           | XTENSA_PROP_NO_TRANSFORM
            | XTENSA_PROP_INSN_NO_REORDER);
 
   return 0;
@@ -9746,6 +9799,11 @@ xtensa_callback_required_dependence (bfd *abfd,
       (*callback) (sec, sec_size, sgotplt, 0, closure);
     }
 
+  /* Only ELF files are supported for Xtensa.  Check here to avoid a segfault
+     when building uclibc, which runs "ld -b binary /dev/null".  */
+  if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+    return ok;
+
   internal_relocs = retrieve_internal_relocs (abfd, sec, 
                                              link_info->keep_memory);
   if (internal_relocs == NULL
@@ -9852,6 +9910,7 @@ static const struct bfd_elf_special_section elf_xtensa_special_sections[] =
 #define elf_backend_gc_sweep_hook           elf_xtensa_gc_sweep_hook
 #define elf_backend_grok_prstatus           elf_xtensa_grok_prstatus
 #define elf_backend_grok_psinfo                     elf_xtensa_grok_psinfo
+#define elf_backend_hide_symbol                     elf_xtensa_hide_symbol
 #define elf_backend_object_p                elf_xtensa_object_p
 #define elf_backend_reloc_type_class        elf_xtensa_reloc_type_class
 #define elf_backend_relocate_section        elf_xtensa_relocate_section