aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf64-ppc.c
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2010-08-05 14:37:30 +0000
committerAlan Modra <amodra@gmail.com>2010-08-05 14:37:30 +0000
commitd983b7a0c394e35ab90cb7b92de98963e3996ef1 (patch)
treed0c16c6b5e3f14560d08f30b302c6802c7250c34 /bfd/elf64-ppc.c
parent50e8a0d53b037c640e313d57ddbe88db8eeea5bb (diff)
downloadgdb-d983b7a0c394e35ab90cb7b92de98963e3996ef1.zip
gdb-d983b7a0c394e35ab90cb7b92de98963e3996ef1.tar.gz
gdb-d983b7a0c394e35ab90cb7b92de98963e3996ef1.tar.bz2
* elf64-ppc.c (ha_reloc_match): Allow matches to other than r2.
(ppc64_elf_relocate_section): Nop out high part insn of large toc code sequence when the high part of offset is zero.
Diffstat (limited to 'bfd/elf64-ppc.c')
-rw-r--r--bfd/elf64-ppc.c65
1 files changed, 55 insertions, 10 deletions
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index f2c50d5..b6ef660 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -11397,12 +11397,14 @@ ppc64_elf_action_discarded (asection *sec)
/* REL points to a low-part reloc on a largetoc instruction sequence.
Find the matching high-part reloc instruction and verify that it
- is addis REG,r2,x. If so, return a pointer to the high-part reloc. */
+ is addis REG,x,imm. If so, set *REG to x and return a pointer to
+ the high-part reloc. */
static const Elf_Internal_Rela *
ha_reloc_match (const Elf_Internal_Rela *relocs,
const Elf_Internal_Rela *rel,
- unsigned int reg,
+ unsigned int *reg,
+ bfd_boolean match_addend,
const bfd *input_bfd,
const bfd_byte *contents)
{
@@ -11434,14 +11436,17 @@ ha_reloc_match (const Elf_Internal_Rela *relocs,
while (--rel >= relocs)
if (rel->r_info == r_info_ha
- && rel->r_addend == r_addend)
+ && (!match_addend
+ || rel->r_addend == r_addend))
{
const bfd_byte *p = contents + (rel->r_offset & ~3);
unsigned int insn = bfd_get_32 (input_bfd, p);
- if ((insn & ((0x3f << 26) | (0x1f << 16)))
- == ((15u << 26) | (2 << 16)) /* addis rt,r2,x */
- && (insn & (0x1f << 21)) == (reg << 21))
- return rel;
+ if ((insn & (0x3f << 26)) == (15u << 26) /* addis rt,x,imm */
+ && (insn & (0x1f << 21)) == (*reg << 21))
+ {
+ *reg = (insn >> 16) & 0x1f;
+ return rel;
+ }
break;
}
return NULL;
@@ -11494,7 +11499,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
Elf_Internal_Rela outrel;
bfd_byte *loc;
struct got_entry **local_got_ents;
+ unsigned char *ha_opt;
bfd_vma TOCstart;
+ bfd_boolean no_ha_opt;
bfd_boolean ret = TRUE;
bfd_boolean is_opd;
/* Disabled until we sort out how ld should choose 'y' vs 'at'. */
@@ -11520,6 +11527,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
symtab_hdr = &elf_symtab_hdr (input_bfd);
sym_hashes = elf_sym_hashes (input_bfd);
is_opd = ppc64_elf_section_data (input_section)->sec_type == sec_opd;
+ ha_opt = NULL;
+ no_ha_opt = FALSE;
rel = relocs;
relend = relocs + input_section->reloc_count;
@@ -12945,7 +12954,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_GOT_DTPREL16_HA:
case R_PPC64_GOT16_HA:
case R_PPC64_TOC16_HA:
- /* For now we don't nop out the first instruction. */
+ /* nop is done later. */
break;
case R_PPC64_GOT_TLSLD16_LO:
@@ -12980,12 +12989,31 @@ ppc64_elf_relocate_section (bfd *output_bfd,
&& ((insn & 3) == 0 || (insn & 3) == 3)))
{
unsigned int reg = (insn >> 16) & 0x1f;
- if (ha_reloc_match (relocs, rel, reg, input_bfd, contents))
+ const Elf_Internal_Rela *ha;
+ bfd_boolean match_addend;
+
+ match_addend = (sym != NULL
+ && ELF_ST_TYPE (sym->st_info) == STT_SECTION);
+ ha = ha_reloc_match (relocs, rel, &reg, match_addend,
+ input_bfd, contents);
+ if (ha != NULL)
{
insn &= ~(0x1f << 16);
- insn |= 2 << 16;
+ insn |= reg << 16;
bfd_put_32 (input_bfd, insn, p);
+ if (ha_opt == NULL)
+ {
+ ha_opt = bfd_zmalloc (input_section->reloc_count);
+ if (ha_opt == NULL)
+ return FALSE;
+ }
+ ha_opt[ha - relocs] = 1;
}
+ else
+ /* If we don't find a matching high part insn,
+ something is fishy. Refuse to nop any high
+ part insn in this section. */
+ no_ha_opt = TRUE;
}
}
break;
@@ -13143,6 +13171,23 @@ ppc64_elf_relocate_section (bfd *output_bfd,
}
}
+ if (ha_opt != NULL)
+ {
+ if (!no_ha_opt)
+ {
+ unsigned char *opt = ha_opt;
+ rel = relocs;
+ relend = relocs + input_section->reloc_count;
+ for (; rel < relend; opt++, rel++)
+ if (*opt != 0)
+ {
+ bfd_byte *p = contents + (rel->r_offset & ~3);
+ bfd_put_32 (input_bfd, NOP, p);
+ }
+ }
+ free (ha_opt);
+ }
+
/* If we're emitting relocations, then shortly after this function
returns, reloc offsets and addends for this section will be
adjusted. Worse, reloc symbol indices will be for the output