static bfd_boolean relax_section (bfd *, asection *, struct bfd_link_info *);
static bfd_boolean translate_section_fixes (asection *);
static bfd_boolean translate_reloc_bfd_fix (reloc_bfd_fix *);
-static void translate_reloc (const r_reloc *, r_reloc *);
+static asection *translate_reloc (const r_reloc *, r_reloc *, asection *);
static void shrink_dynamic_reloc_sections
(struct bfd_link_info *, bfd *, asection *, Elf_Internal_Rela *);
static bfd_boolean move_literal
if (relax_info->is_relaxable_literal_section
|| relax_info->is_relaxable_asm_section)
{
+ pin_internal_relocs (sec, internal_relocs);
+
if (r_type != R_XTENSA_NONE
&& find_removed_literal (&relax_info->removed_list,
irel->r_offset))
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;
}
|| target_relax_info->is_relaxable_asm_section))
{
r_reloc new_reloc;
- reloc_bfd_fix *fix;
- bfd_vma addend_displacement;
-
- translate_reloc (&r_rel, &new_reloc);
+ target_sec = translate_reloc (&r_rel, &new_reloc, target_sec);
if (r_type == R_XTENSA_DIFF8
|| r_type == R_XTENSA_DIFF16
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,
- r_reloc_get_section (&new_reloc),
- addend_displacement, TRUE);
- add_fix (sec, fix);
+ else
+ {
+ /* If the relocation still references a section in the same
+ input file, modify the relocation directly instead of
+ adding a "fix" record. */
+ if (target_sec->owner == abfd)
+ {
+ unsigned r_symndx = ELF32_R_SYM (new_reloc.rela.r_info);
+ irel->r_info = ELF32_R_INFO (r_symndx, r_type);
+ irel->r_addend = new_reloc.rela.r_addend;
+ pin_internal_relocs (sec, internal_relocs);
+ }
+ else
+ {
+ bfd_vma addend_displacement;
+ reloc_bfd_fix *fix;
+
+ addend_displacement =
+ new_reloc.target_offset + new_reloc.virtual_offset;
+ fix = reloc_bfd_fix_init (sec, source_offset, r_type,
+ target_sec,
+ addend_displacement, TRUE);
+ add_fix (sec, fix);
+ }
+ }
}
-
- pin_internal_relocs (sec, internal_relocs);
}
}
/* Fix up a relocation to take account of removed literals. */
-static void
-translate_reloc (const r_reloc *orig_rel, r_reloc *new_rel)
+static asection *
+translate_reloc (const r_reloc *orig_rel, r_reloc *new_rel, asection *sec)
{
- asection *sec;
xtensa_relax_info *relax_info;
removed_literal *removed;
- bfd_vma new_offset, target_offset, removed_bytes;
+ bfd_vma target_offset, base_offset;
+ text_action *act;
*new_rel = *orig_rel;
if (!r_reloc_is_defined (orig_rel))
- return;
- sec = r_reloc_get_section (orig_rel);
+ return sec ;
relax_info = get_xtensa_relax_info (sec);
- BFD_ASSERT (relax_info);
-
- if (!relax_info->is_relaxable_literal_section
- && !relax_info->is_relaxable_asm_section)
- return;
+ BFD_ASSERT (relax_info && (relax_info->is_relaxable_literal_section
+ || relax_info->is_relaxable_asm_section));
target_offset = orig_rel->target_offset;
if (!relax_info
|| (!relax_info->is_relaxable_literal_section
&& !relax_info->is_relaxable_asm_section))
- return;
+ return sec;
}
target_offset = new_rel->target_offset;
}
- /* ...and the target address may have been moved within its section. */
- new_offset = offset_with_removed_text (&relax_info->action_list,
- target_offset);
+ /* Find the base offset of the reloc symbol, excluding any addend from the
+ reloc or from the section contents (for a partial_inplace reloc). Then
+ find the adjusted values of the offsets due to relaxation. The base
+ offset is needed to determine the change to the reloc's addend; the reloc
+ addend should not be adjusted due to relaxations located before the base
+ offset. */
+
+ base_offset = r_reloc_get_target_offset (new_rel) - new_rel->rela.r_addend;
+ act = relax_info->action_list.head;
+ if (base_offset <= target_offset)
+ {
+ int base_removed = removed_by_actions (&act, base_offset, FALSE);
+ int addend_removed = removed_by_actions (&act, target_offset, FALSE);
+ new_rel->target_offset = target_offset - base_removed - addend_removed;
+ new_rel->rela.r_addend -= addend_removed;
+ }
+ else
+ {
+ /* Handle a negative addend. The base offset comes first. */
+ int tgt_removed = removed_by_actions (&act, target_offset, FALSE);
+ int addend_removed = removed_by_actions (&act, base_offset, FALSE);
+ new_rel->target_offset = target_offset - tgt_removed;
+ new_rel->rela.r_addend += addend_removed;
+ }
- /* Modify the offset and addend. */
- removed_bytes = target_offset - new_offset;
- new_rel->target_offset = new_offset;
- new_rel->rela.r_addend -= removed_bytes;
+ return sec;
}