aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-ppc.c
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2015-04-01 09:59:46 +1030
committerAlan Modra <amodra@gmail.com>2015-04-07 23:00:07 +0930
commitd3e454b956b16d31998fec65a2508256e6357194 (patch)
tree8b6ee54cb49bcdd984c9df244ce6cfc225d7f496 /bfd/elf32-ppc.c
parentd9b67d9f4127191679c743d1f47d2421d6dcede7 (diff)
downloadgdb-d3e454b956b16d31998fec65a2508256e6357194.zip
gdb-d3e454b956b16d31998fec65a2508256e6357194.tar.gz
gdb-d3e454b956b16d31998fec65a2508256e6357194.tar.bz2
PowerPC non-PIC to PIC editing for protected var access
This is a linker-only solution to the incompatibility between shared library protected visibility variables and using .dynbss and copy relocs for non-PIC access to shared library variables. bfd/ * elf32-ppc.c (struct ppc_elf_link_hash_entry): Add has_addr16_ha and has_addr16_lo. Make has_sda_refs a bitfield. (ppc_elf_check_relocs): Set new flags. (ppc_elf_link_hash_table_create): Update default_params. (ppc_elf_adjust_dynamic_symbol): Clear protected_def in cases where we won't be making .dynbss entries or editing code. Set params->pic_fixup when we'll edit code for protected var access. (allocate_dynrelocs): Allocate got entry for edited code and discard dyn_relocs. (struct ppc_elf_relax_info): Add picfixup_size. (ppc_elf_relax_section): Rename struct one_fixup to struct one_branch_fixup. Rename fixups to branch_fixups. Size space for pic fixups. (ppc_elf_relocate_section): Edit non-PIC accessing protected visibility variables to PIC. Don't emit dyn_relocs for code we've edited. * elf32-ppc.h (struct ppc_elf_params): Add pic_fixup. ld/ * emultempl/ppc32elf.em: Handle --no-pic-fixup. (params): Init new field. (ppc_before_allocation): Enable relaxation for pic_fixup.
Diffstat (limited to 'bfd/elf32-ppc.c')
-rw-r--r--bfd/elf32-ppc.c253
1 files changed, 215 insertions, 38 deletions
diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c
index b96dbc8..663a871 100644
--- a/bfd/elf32-ppc.c
+++ b/bfd/elf32-ppc.c
@@ -3135,7 +3135,11 @@ struct ppc_elf_link_hash_entry
/* Nonzero if we have seen a small data relocation referring to this
symbol. */
- unsigned char has_sda_refs;
+ unsigned char has_sda_refs : 1;
+
+ /* Flag use of given relocations. */
+ unsigned char has_addr16_ha : 1;
+ unsigned char has_addr16_lo : 1;
};
#define ppc_elf_hash_entry(ent) ((struct ppc_elf_link_hash_entry *) (ent))
@@ -3259,7 +3263,7 @@ static struct bfd_link_hash_table *
ppc_elf_link_hash_table_create (bfd *abfd)
{
struct ppc_elf_link_hash_table *ret;
- static struct ppc_elf_params default_params = { PLT_OLD, 0, 1, 0, 0, 12 };
+ static struct ppc_elf_params default_params = { PLT_OLD, 0, 1, 0, 0, 12, 0 };
ret = bfd_zmalloc (sizeof (struct ppc_elf_link_hash_table));
if (ret == NULL)
@@ -4376,6 +4380,10 @@ ppc_elf_check_relocs (bfd *abfd,
/* We may need a copy reloc too. */
h->non_got_ref = 1;
h->pointer_equality_needed = 1;
+ if (r_type == R_PPC_ADDR16_HA)
+ ppc_elf_hash_entry (h)->has_addr16_ha = 1;
+ if (r_type == R_PPC_ADDR16_LO)
+ ppc_elf_hash_entry (h)->has_addr16_lo = 1;
}
goto dodyn;
@@ -5554,6 +5562,7 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
&& !readonly_dynrelocs (h))
h->non_got_ref = 0;
}
+ h->protected_def = 0;
return TRUE;
}
else
@@ -5581,12 +5590,34 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
For such cases we need not do anything here; the relocations will
be handled correctly by relocate_section. */
if (info->shared)
- return TRUE;
+ {
+ h->protected_def = 0;
+ return TRUE;
+ }
/* If there are no references to this symbol that do not use the
GOT, we don't need to generate a copy reloc. */
if (!h->non_got_ref)
- return TRUE;
+ {
+ h->protected_def = 0;
+ return TRUE;
+ }
+
+ /* Protected variables do not work with .dynbss. The copy in
+ .dynbss won't be used by the shared library with the protected
+ definition for the variable. Editing to PIC, or text relocations
+ are preferable to an incorrect program. */
+ if (h->protected_def)
+ {
+ if (ELIMINATE_COPY_RELOCS
+ && ppc_elf_hash_entry (h)->has_addr16_ha
+ && ppc_elf_hash_entry (h)->has_addr16_lo
+ && htab->params->pic_fixup == 0
+ && info->disable_target_specific_optimizations <= 1)
+ htab->params->pic_fixup = 1;
+ h->non_got_ref = 0;
+ return TRUE;
+ }
/* If -z nocopyreloc was given, we won't generate them either. */
if (info->nocopyreloc)
@@ -5611,16 +5642,6 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
return TRUE;
}
- /* Protected variables do not work with .dynbss. The copy in
- .dynbss won't be used by the shared library with the protected
- definition for the variable. Text relocations are preferable
- to an incorrect program. */
- if (h->protected_def)
- {
- h->non_got_ref = 0;
- return TRUE;
- }
-
/* We must allocate the symbol in our .dynbss section, which will
become part of the .bss section of the executable. There will be
an entry for this symbol in the .dynsym section. The dynamic
@@ -5929,7 +5950,13 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
}
eh = (struct ppc_elf_link_hash_entry *) h;
- if (eh->elf.got.refcount > 0)
+ if (eh->elf.got.refcount > 0
+ || (ELIMINATE_COPY_RELOCS
+ && !eh->elf.def_regular
+ && eh->elf.protected_def
+ && eh->has_addr16_ha
+ && eh->has_addr16_lo
+ && htab->params->pic_fixup > 0))
{
bfd_boolean dyn;
unsigned int need;
@@ -6072,7 +6099,11 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
dynamic. */
if (!h->non_got_ref
- && !h->def_regular)
+ && !h->def_regular
+ && !(h->protected_def
+ && eh->has_addr16_ha
+ && eh->has_addr16_lo
+ && htab->params->pic_fixup > 0))
{
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
@@ -6713,6 +6744,7 @@ static const int stub_entry[] =
struct ppc_elf_relax_info
{
unsigned int workaround_size;
+ unsigned int picfixup_size;
};
/* This function implements long branch trampolines, and the ppc476
@@ -6726,9 +6758,9 @@ ppc_elf_relax_section (bfd *abfd,
struct bfd_link_info *link_info,
bfd_boolean *again)
{
- struct one_fixup
+ struct one_branch_fixup
{
- struct one_fixup *next;
+ struct one_branch_fixup *next;
asection *tsec;
/* Final link, can use the symbol offset. For a
relocatable link we use the symbol's index. */
@@ -6741,12 +6773,12 @@ ppc_elf_relax_section (bfd *abfd,
Elf_Internal_Sym *isymbuf = NULL;
Elf_Internal_Rela *internal_relocs = NULL;
Elf_Internal_Rela *irel, *irelend = NULL;
- struct one_fixup *fixups = NULL;
+ struct one_branch_fixup *branch_fixups = NULL;
struct ppc_elf_relax_info *relax_info = NULL;
unsigned changes = 0;
bfd_boolean workaround_change;
struct ppc_elf_link_hash_table *htab;
- bfd_size_type trampbase, trampoff, newsize;
+ bfd_size_type trampbase, trampoff, newsize, picfixup_size;
asection *got2;
bfd_boolean maybe_pasted;
@@ -6778,7 +6810,8 @@ ppc_elf_relax_section (bfd *abfd,
|| isec->sec_info_type == SEC_INFO_TYPE_TARGET);
isec->sec_info_type = SEC_INFO_TYPE_TARGET;
- if (htab->params->ppc476_workaround)
+ if (htab->params->ppc476_workaround
+ || htab->params->pic_fixup > 0)
{
if (elf_section_data (isec)->sec_info == NULL)
{
@@ -6799,8 +6832,9 @@ ppc_elf_relax_section (bfd *abfd,
trampoff += 4;
symtab_hdr = &elf_symtab_hdr (abfd);
-
- if (htab->params->branch_trampolines)
+ picfixup_size = 0;
+ if (htab->params->branch_trampolines
+ || htab->params->pic_fixup > 0)
{
/* Get a copy of the native relocations. */
if (isec->reloc_count != 0)
@@ -6819,9 +6853,9 @@ ppc_elf_relax_section (bfd *abfd,
unsigned long r_type = ELF32_R_TYPE (irel->r_info);
bfd_vma toff, roff;
asection *tsec;
- struct one_fixup *f;
+ struct one_branch_fixup *f;
size_t insn_offset = 0;
- bfd_vma max_branch_offset, val;
+ bfd_vma max_branch_offset = 0, val;
bfd_byte *hit_addr;
unsigned long t0;
struct elf_link_hash_entry *h;
@@ -6842,6 +6876,11 @@ ppc_elf_relax_section (bfd *abfd,
max_branch_offset = 1 << 15;
break;
+ case R_PPC_ADDR16_HA:
+ if (htab->params->pic_fixup > 0)
+ break;
+ continue;
+
default:
continue;
}
@@ -6963,6 +7002,17 @@ ppc_elf_relax_section (bfd *abfd,
sym_type = h->type;
}
+ if (r_type == R_PPC_ADDR16_HA)
+ {
+ if (h != NULL
+ && !h->def_regular
+ && h->protected_def
+ && ppc_elf_hash_entry (h)->has_addr16_ha
+ && ppc_elf_hash_entry (h)->has_addr16_lo)
+ picfixup_size += 12;
+ continue;
+ }
+
/* The condition here under which we call find_plt_ent must
match that in relocate_section. If we call find_plt_ent here
but not in relocate_section, or vice versa, then the branch
@@ -7080,7 +7130,7 @@ ppc_elf_relax_section (bfd *abfd,
}
/* Look for an existing fixup to this address. */
- for (f = fixups; f ; f = f->next)
+ for (f = branch_fixups; f ; f = f->next)
if (f->tsec == tsec && f->toff == toff)
break;
@@ -7125,11 +7175,11 @@ ppc_elf_relax_section (bfd *abfd,
/* Record the fixup so we don't do it again this section. */
f = bfd_malloc (sizeof (*f));
- f->next = fixups;
+ f->next = branch_fixups;
f->tsec = tsec;
f->toff = toff;
f->trampoff = trampoff;
- fixups = f;
+ branch_fixups = f;
trampoff += size;
changes++;
@@ -7179,10 +7229,10 @@ ppc_elf_relax_section (bfd *abfd,
}
}
- while (fixups != NULL)
+ while (branch_fixups != NULL)
{
- struct one_fixup *f = fixups;
- fixups = fixups->next;
+ struct one_branch_fixup *f = branch_fixups;
+ branch_fixups = branch_fixups->next;
free (f);
}
}
@@ -7219,7 +7269,15 @@ ppc_elf_relax_section (bfd *abfd,
newsize = trampoff + relax_info->workaround_size;
}
- if (changes || workaround_change)
+ if (htab->params->pic_fixup > 0)
+ {
+ picfixup_size -= relax_info->picfixup_size;
+ if (picfixup_size != 0)
+ relax_info->picfixup_size += picfixup_size;
+ newsize += relax_info->picfixup_size;
+ }
+
+ if (changes != 0 || picfixup_size != 0 || workaround_change)
isec->size = newsize;
if (isymbuf != NULL
@@ -7246,6 +7304,7 @@ ppc_elf_relax_section (bfd *abfd,
}
}
+ changes += picfixup_size;
if (changes != 0)
{
/* Append sufficient NOP relocs so we can write out relocation
@@ -7280,10 +7339,10 @@ ppc_elf_relax_section (bfd *abfd,
return TRUE;
error_return:
- while (fixups != NULL)
+ while (branch_fixups != NULL)
{
- struct one_fixup *f = fixups;
- fixups = fixups->next;
+ struct one_branch_fixup *f = branch_fixups;
+ branch_fixups = branch_fixups->next;
free (f);
}
if (isymbuf != NULL && (unsigned char *) isymbuf != symtab_hdr->contents)
@@ -7599,6 +7658,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
bfd_boolean ret = TRUE;
bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
bfd_boolean is_vxworks_tls;
+ unsigned int picfixup_size = 0;
+ struct ppc_elf_relax_info *relax_info = NULL;
#ifdef DEBUG
_bfd_error_handler ("ppc_elf_relocate_section called for %B section %A, "
@@ -7623,6 +7684,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
is_vxworks_tls = (htab->is_vxworks && info->shared
&& !strcmp (input_section->output_section->name,
".tls_vars"));
+ if (input_section->sec_info_type == SEC_INFO_TYPE_TARGET)
+ relax_info = elf_section_data (input_section)->sec_info;
rel = relocs;
relend = relocs + input_section->reloc_count;
for (; rel < relend; rel++)
@@ -7980,6 +8043,114 @@ ppc_elf_relocate_section (bfd *output_bfd,
}
}
+ if (ELIMINATE_COPY_RELOCS
+ && h != NULL
+ && !h->def_regular
+ && h->protected_def
+ && ppc_elf_hash_entry (h)->has_addr16_ha
+ && ppc_elf_hash_entry (h)->has_addr16_lo
+ && htab->params->pic_fixup > 0)
+ {
+ /* Convert lis;addi or lis;load/store accessing a protected
+ variable defined in a shared library to PIC. */
+ unsigned int insn;
+
+ if (r_type == R_PPC_ADDR16_HA)
+ {
+ insn = bfd_get_32 (output_bfd,
+ contents + rel->r_offset - d_offset);
+ if ((insn & (0x3f << 26)) == (15u << 26)
+ && (insn & (0x1f << 16)) == 0 /* lis */)
+ {
+ bfd_byte *p;
+ bfd_vma off;
+ bfd_vma got_addr;
+
+ p = (contents + input_section->size
+ - relax_info->workaround_size
+ - relax_info->picfixup_size
+ + picfixup_size);
+ off = (p - contents) - (rel->r_offset - d_offset);
+ if (off > 0x1fffffc || (off & 3) != 0)
+ info->callbacks->einfo
+ (_("%P: %H: fixup branch overflow\n"),
+ input_bfd, input_section, rel->r_offset);
+
+ bfd_put_32 (output_bfd, B | off,
+ contents + rel->r_offset - d_offset);
+ got_addr = (htab->got->output_section->vma
+ + htab->got->output_offset
+ + (h->got.offset & ~1));
+ rel->r_info = ELF32_R_INFO (0, R_PPC_ADDR16_HA);
+ rel->r_addend = got_addr;
+ rel->r_offset = (p - contents) + d_offset;
+ insn &= ~0xffff;
+ insn |= ((unsigned int )(got_addr + 0x8000) >> 16) & 0xffff;
+ bfd_put_32 (output_bfd, insn, p);
+
+ /* Convert lis to lwz, loading address from GOT. */
+ insn &= ~0xffff;
+ insn ^= (32u ^ 15u) << 26;
+ insn |= (insn & (0x1f << 21)) >> 5;
+ insn |= got_addr & 0xffff;
+ bfd_put_32 (output_bfd, insn, p + 4);
+
+ bfd_put_32 (output_bfd, B | ((-4 - off) & 0x3ffffff), p + 8);
+ picfixup_size += 12;
+
+ /* Use one of the spare relocs, so --emit-relocs
+ output is reasonable. */
+ memmove (rel + 1, rel, (relend - rel - 1) * sizeof (*rel));
+ rel++;
+ rel->r_info = ELF32_R_INFO (0, R_PPC_ADDR16_LO);
+ rel->r_offset += 4;
+
+ /* Continue on as if we had a got reloc, to output
+ dynamic reloc. */
+ r_type = R_PPC_GOT16_LO;
+ }
+ else
+ info->callbacks->einfo
+ (_("%P: %H: error: %s with unexpected instruction %x\n"),
+ input_bfd, input_section, rel->r_offset,
+ "R_PPC_ADDR16_HA", insn);
+ }
+ else if (r_type == R_PPC_ADDR16_LO)
+ {
+ insn = bfd_get_32 (output_bfd,
+ contents + rel->r_offset - d_offset);
+ if ((insn & (0x3f << 26)) == 14u << 26 /* addi */
+ || (insn & (0x3f << 26)) == 32u << 26 /* lwz */
+ || (insn & (0x3f << 26)) == 34u << 26 /* lbz */
+ || (insn & (0x3f << 26)) == 36u << 26 /* stw */
+ || (insn & (0x3f << 26)) == 38u << 26 /* stb */
+ || (insn & (0x3f << 26)) == 40u << 26 /* lhz */
+ || (insn & (0x3f << 26)) == 42u << 26 /* lha */
+ || (insn & (0x3f << 26)) == 44u << 26 /* sth */
+ || (insn & (0x3f << 26)) == 46u << 26 /* lmw */
+ || (insn & (0x3f << 26)) == 47u << 26 /* stmw */
+ || (insn & (0x3f << 26)) == 48u << 26 /* lfs */
+ || (insn & (0x3f << 26)) == 50u << 26 /* lfd */
+ || (insn & (0x3f << 26)) == 52u << 26 /* stfs */
+ || (insn & (0x3f << 26)) == 54u << 26 /* stfd */
+ || ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
+ && (insn & 3) != 1)
+ || ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
+ && ((insn & 3) == 0 || (insn & 3) == 3)))
+ {
+ /* Arrange to apply the reloc addend, if any. */
+ relocation = 0;
+ unresolved_reloc = FALSE;
+ rel->r_info = ELF32_R_INFO (0, r_type);
+ }
+ else
+ info->callbacks->einfo
+ (_("%P: %H: error: %s with unexpected instruction %x\n"),
+ input_bfd, input_section, rel->r_offset,
+ "R_PPC_ADDR16_LO", insn);
+ }
+ }
+
ifunc = NULL;
if (!htab->is_vxworks)
{
@@ -8324,6 +8495,10 @@ ppc_elf_relocate_section (bfd *output_bfd,
}
}
+ /* If here for a picfixup, we're done. */
+ if (r_type != ELF32_R_TYPE (rel->r_info))
+ continue;
+
relocation = (htab->got->output_section->vma
+ htab->got->output_offset
+ off
@@ -8471,7 +8646,11 @@ ppc_elf_relocate_section (bfd *output_bfd,
&& h != NULL
&& h->dynindx != -1
&& !h->non_got_ref
- && !h->def_regular))
+ && !h->def_regular
+ && !(h->protected_def
+ && ppc_elf_hash_entry (h)->has_addr16_ha
+ && ppc_elf_hash_entry (h)->has_addr16_lo
+ && htab->params->pic_fixup > 0)))
{
int skip;
bfd_byte *loc;
@@ -9296,11 +9475,9 @@ ppc_elf_relocate_section (bfd *output_bfd,
|| (input_section->output_section->alignment_power
>= htab->params->pagesize_p2)))
{
- struct ppc_elf_relax_info *relax_info;
bfd_vma start_addr, end_addr, addr;
bfd_vma pagesize = (bfd_vma) 1 << htab->params->pagesize_p2;
- relax_info = elf_section_data (input_section)->sec_info;
if (relax_info->workaround_size != 0)
{
bfd_byte *p;