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
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)
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);
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;
}
+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. */
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));
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)
/* 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. */
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:
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;
}
}
-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);
}
}
-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 *
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_init (asection *src_sec,
bfd_vma src_offset,
unsigned src_type,
- bfd *target_abfd,
asection *target_sec,
bfd_vma target_offset,
bfd_boolean translated)
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;
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;
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)
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);
the_entry++;
}
- if (the_entry->flags & XTENSA_PROP_INSN_NO_TRANSFORM)
+ if (the_entry->flags & XTENSA_PROP_NO_TRANSFORM)
/* NO_REORDER is OK */
continue;
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))
bad_alignment = FALSE;
break;
}
+ if (new_action->do_action)
+ removed_bytes += new_action->removed_bytes;
}
if (!bad_alignment)
{
/* 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))
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];
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. */
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);
/* 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);
|| 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)
{
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)
{
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);
}
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);
}
}
|| 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);
}
}
{
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;
(*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
#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