aboutsummaryrefslogtreecommitdiff
path: root/bfd/elfxx-sparc.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@redhat.com>2011-10-19 00:32:25 +0000
committerDavid S. Miller <davem@redhat.com>2011-10-19 00:32:25 +0000
commitabd242a90836b535b2f98be634e16c70012a3c5d (patch)
treecc8a899c08c32ce0693497c6fc2f96264706d761 /bfd/elfxx-sparc.c
parent01b701aae6a81fc07a867fc3746587000aaa8c50 (diff)
downloadgdb-abd242a90836b535b2f98be634e16c70012a3c5d.zip
gdb-abd242a90836b535b2f98be634e16c70012a3c5d.tar.gz
gdb-abd242a90836b535b2f98be634e16c70012a3c5d.tar.bz2
Fix sparc TLS call relaxation when the delay slot sets up %o0.
bfd/ PR binutils/13301 * elfxx-sparc.c (sparc_elf_find_reloc_at_ofs): New function. (_bfd_sparc_elf_relocate_section): Always move the __tls_get_addr call delay slot instruction forward 4 bytes when performing relaxation. gold/ PR binutils/13301 * sparc.cc (Target_sparc::Relocate::reloc_adjust_addr_): New member to track relocation locations that have moved during TLS reloc optimizations. (Target_sparc::Relocate::Relocate): Initialize to NULL. (Target_sparc::Relocate::relocate): Adjust view down by 4 bytes if it matches reloc_adjust_addr_. (Target_sparc::Relocate::relocate_tls): Always move the __tls_get_addr call delay slot instruction forward 4 bytes when performing relaxation. ld/testsuite/ * ld-sparc/tlssunbin32.dd: Update for TLS call relaxation fix for PR 13301. * ld-sparc/tlssunbin64.dd: Likewise. * ld-sparc/tlssunpic32.dd: Likewise. * ld-sparc/tlssunpic64.dd: Likewise.
Diffstat (limited to 'bfd/elfxx-sparc.c')
-rw-r--r--bfd/elfxx-sparc.c36
1 files changed, 35 insertions, 1 deletions
diff --git a/bfd/elfxx-sparc.c b/bfd/elfxx-sparc.c
index 438b7f5..9a15124 100644
--- a/bfd/elfxx-sparc.c
+++ b/bfd/elfxx-sparc.c
@@ -1830,6 +1830,20 @@ _bfd_sparc_elf_gc_mark_hook (asection *sec,
return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
}
+static Elf_Internal_Rela *
+sparc_elf_find_reloc_at_ofs (Elf_Internal_Rela *rel,
+ Elf_Internal_Rela *relend,
+ bfd_vma offset)
+{
+ while (rel < relend)
+ {
+ if (rel->r_offset == offset)
+ return rel;
+ rel++;
+ }
+ return NULL;
+}
+
/* Update the got entry reference counts for the section being removed. */
bfd_boolean
_bfd_sparc_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
@@ -3676,6 +3690,7 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
if (! info->shared
|| (r_type == R_SPARC_TLS_GD_CALL && tls_type == GOT_TLS_IE))
{
+ Elf_Internal_Rela *rel2;
bfd_vma insn;
if (!info->shared && (h == NULL || h->dynindx == -1))
@@ -3711,7 +3726,26 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
continue;
}
- bfd_put_32 (output_bfd, 0x9001c008, contents + rel->r_offset);
+ /* We cannot just overwrite the delay slot instruction,
+ as it might be what puts the %o0 argument to
+ __tls_get_addr into place. So we have to transpose
+ the delay slot with the add we patch in. */
+ insn = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+ bfd_put_32 (output_bfd, insn,
+ contents + rel->r_offset);
+ bfd_put_32 (output_bfd, 0x9001c008,
+ contents + rel->r_offset + 4);
+
+ rel2 = rel;
+ while ((rel2 = sparc_elf_find_reloc_at_ofs (rel2 + 1, relend,
+ rel->r_offset + 4))
+ != NULL)
+ {
+ /* If the instruction we moved has a relocation attached to
+ it, adjust the offset so that it will apply to the correct
+ instruction. */
+ rel2->r_offset -= 4;
+ }
continue;
}