diff options
author | Alan Modra <amodra@gmail.com> | 2015-04-01 09:59:46 +1030 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2015-04-07 23:00:07 +0930 |
commit | d3e454b956b16d31998fec65a2508256e6357194 (patch) | |
tree | 8b6ee54cb49bcdd984c9df244ce6cfc225d7f496 /bfd/elf32-ppc.c | |
parent | d9b67d9f4127191679c743d1f47d2421d6dcede7 (diff) | |
download | gdb-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.c | 253 |
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; |