diff options
author | Alan Modra <amodra@gmail.com> | 2010-06-25 05:20:57 +0000 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2010-06-25 05:20:57 +0000 |
commit | ba761f19f5658feaff39b69c0bdc84b8716cd3f9 (patch) | |
tree | 16f9055ee52f67720a5b6b08bbb90d2b24ce6419 /bfd | |
parent | bded3693aef88c180260497e078c57d5e86a9ea4 (diff) | |
download | fsf-binutils-gdb-ba761f19f5658feaff39b69c0bdc84b8716cd3f9.zip fsf-binutils-gdb-ba761f19f5658feaff39b69c0bdc84b8716cd3f9.tar.gz fsf-binutils-gdb-ba761f19f5658feaff39b69c0bdc84b8716cd3f9.tar.bz2 |
include/elf/
* ppc64.h (R_PPC64_LO_DS_OPT): Define.
bfd/
* elf64-ppc.c (toc_skip_enum): Define.
(ppc64_elf_edit_toc): Use two low bits of skip array as markers.
Optimize largetoc sequences.
(adjust_toc_syms): Update for skip array change.
(ppc64_elf_relocate_section): Handle R_PPC64_LO_DS_OPT.
ld/
* emultempl/ppc64elf.em (prelim_size_sections): New function.
(ppc_before_allocation): Use it. Size sections before toc edit too.
Diffstat (limited to 'bfd')
-rw-r--r-- | bfd/ChangeLog | 8 | ||||
-rw-r--r-- | bfd/elf64-ppc.c | 188 |
2 files changed, 177 insertions, 19 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index f1365e55..0153119 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,5 +1,13 @@ 2010-06-25 Alan Modra <amodra@gmail.com> + * elf64-ppc.c (toc_skip_enum): Define. + (ppc64_elf_edit_toc): Use two low bits of skip array as markers. + Optimize largetoc sequences. + (adjust_toc_syms): Update for skip array change. + (ppc64_elf_relocate_section): Handle R_PPC64_LO_DS_OPT. + +2010-06-25 Alan Modra <amodra@gmail.com> + * elf64-ppc.c (is_static_defined): New function. (get_tls_mask, ppc_type_of_stub): Use it here. (ppc64_elf_edit_opd): Ensure we only attempt to edit ppc64 input. diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index fbc7600..116d419 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -7846,6 +7846,8 @@ struct adjust_toc_info bfd_boolean global_toc_syms; }; +enum toc_skip_enum { ref_from_discarded = 1, can_optimize = 2 }; + static bfd_boolean adjust_toc_syms (struct elf_link_hash_entry *h, void *inf) { @@ -7874,13 +7876,13 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf) else i = eh->elf.root.u.def.value >> 3; - if (toc_inf->skip[i] == (unsigned long) -1) + if ((toc_inf->skip[i] & (ref_from_discarded | can_optimize)) != 0) { (*_bfd_error_handler) (_("%s defined on removed toc entry"), eh->elf.root.root.string); do ++i; - while (toc_inf->skip[i] == (unsigned long) -1); + while ((toc_inf->skip[i] & (ref_from_discarded | can_optimize)) != 0); eh->elf.root.u.def.value = (bfd_vma) i << 3; } @@ -8001,13 +8003,87 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) goto error_ret; } - skip[val >> 3] = 1; + skip[val >> 3] = ref_from_discarded; } if (elf_section_data (sec)->relocs != relstart) free (relstart); } + /* For largetoc loads of address constants, we can convert + . addis rx,2,addr@got@ha + . ld ry,addr@got@l(rx) + to + . addis rx,2,addr@toc@ha + . addi ry,rx,addr@toc@l + when addr is within 2G of the toc pointer. This then means + that the word storing "addr" in the toc is no longer needed. */ + + if (!ppc64_elf_tdata (ibfd)->has_small_toc_reloc + && toc->output_section->rawsize < (bfd_vma) 1 << 31 + && toc->reloc_count != 0) + { + /* Read toc relocs. */ + relstart = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL, + info->keep_memory); + if (relstart == NULL) + goto error_ret; + + for (rel = relstart; rel < relstart + toc->reloc_count; ++rel) + { + enum elf_ppc64_reloc_type r_type; + unsigned long r_symndx; + asection *sym_sec; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; + bfd_vma val, addr; + + r_type = ELF64_R_TYPE (rel->r_info); + if (r_type != R_PPC64_ADDR64) + continue; + + r_symndx = ELF64_R_SYM (rel->r_info); + if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms, + r_symndx, ibfd)) + goto error_ret; + + if (!SYMBOL_REFERENCES_LOCAL (info, h)) + continue; + + if (h != NULL) + val = h->root.u.def.value; + else + val = sym->st_value; + val += rel->r_addend; + val += sym_sec->output_section->vma + sym_sec->output_offset; + + /* We don't yet know the exact toc pointer value, but we + know it will be somewhere in the toc section. Don't + optimize if the difference from any possible toc + pointer is outside [ff..f80008000, 7fff7fff]. */ + addr = toc->output_section->vma + TOC_BASE_OFF; + if (val - addr + (bfd_vma) 0x80008000 >= (bfd_vma) 1 << 32) + continue; + + addr = toc->output_section->vma + toc->output_section->rawsize; + if (val - addr + (bfd_vma) 0x80008000 >= (bfd_vma) 1 << 32) + continue; + + if (skip == NULL) + { + skip = bfd_zmalloc (sizeof (*skip) * (toc->size + 15) / 8); + if (skip == NULL) + goto error_ret; + } + + skip[rel->r_offset >> 3] + |= can_optimize | ((rel - relstart) << 2); + } + + if (elf_section_data (toc)->relocs != relstart) + free (relstart); + } + if (skip == NULL) continue; @@ -8100,12 +8176,37 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) if (val >= toc->size) continue; + if ((skip[val >> 3] & can_optimize) != 0) + { + bfd_vma off; + unsigned char opc; + + switch (r_type) + { + case R_PPC64_TOC16_HA: + break; + + case R_PPC64_TOC16_LO_DS: + off = rel->r_offset + (bfd_big_endian (ibfd) ? -2 : 3); + if (!bfd_get_section_contents (ibfd, sec, &opc, off, 1)) + return FALSE; + if ((opc & (0x3f << 2)) == (58u << 2)) + break; + /* Fall thru */ + + default: + /* Wrong sort of reloc, or not a ld. We may + as well clear ref_from_discarded too. */ + skip[val >> 3] = 0; + } + } + /* For the toc section, we only mark as used if this entry itself isn't unused. */ if (sec == toc && !used[val >> 3] && (used[rel->r_offset >> 3] - || !skip[rel->r_offset >> 3])) + || !(skip[rel->r_offset >> 3] & ref_from_discarded))) /* Do all the relocs again, to catch reference chains. */ repeat = 1; @@ -8127,13 +8228,15 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) { if (*keep) { - *drop = 0; + *drop &= ~ref_from_discarded; + if ((*drop & can_optimize) != 0) + some_unused = 1; last = 0; } else if (*drop) { some_unused = 1; - last = 1; + last = ref_from_discarded; } else *drop = last; @@ -8145,6 +8248,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) { bfd_byte *contents, *src; unsigned long off; + bfd_boolean local_toc_syms = FALSE; /* Shuffle the toc contents, and at the same time convert the skip array from booleans into offsets. */ @@ -8157,11 +8261,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) src < contents + toc->size; src += 8, ++drop) { - if (*drop) - { - *drop = (unsigned long) -1; - off += 8; - } + if ((*drop & (can_optimize | ref_from_discarded)) != 0) + off += 8; else if (off != 0) { *drop = off; @@ -8172,7 +8273,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) toc->rawsize = toc->size; toc->size = src - contents - off; - /* Adjust addends for relocs against the toc section sym. */ + /* Adjust addends for relocs against the toc section sym, + and optimize any accesses we can. */ for (sec = ibfd->sections; sec != NULL; sec = sec->next) { if (sec->reloc_count == 0 @@ -8214,13 +8316,50 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) r_symndx, ibfd)) goto error_ret; - if (sym_sec != toc || h != NULL || sym->st_value != 0) + if (sym_sec != toc) continue; - val = rel->r_addend; + if (h != NULL) + val = h->root.u.def.value; + else + { + val = sym->st_value; + if (val != 0) + local_toc_syms = TRUE; + } + + val += rel->r_addend; if (val > toc->rawsize) val = toc->rawsize; + else if ((skip[val >> 3] & ref_from_discarded) != 0) + continue; + else if ((skip[val >> 3] & can_optimize) != 0) + { + Elf_Internal_Rela *tocrel + = elf_section_data (toc)->relocs + (skip[val >> 3] >> 2); + unsigned long tsym = ELF64_R_SYM (tocrel->r_info); + + switch (r_type) + { + case R_PPC64_TOC16_HA: + rel->r_info = ELF64_R_INFO (tsym, R_PPC64_TOC16_HA); + break; + + case R_PPC64_TOC16_LO_DS: + rel->r_info = ELF64_R_INFO (tsym, R_PPC64_LO_DS_OPT); + break; + + default: + abort (); + } + rel->r_addend = tocrel->r_addend; + elf_section_data (sec)->relocs = relstart; + continue; + } + + if (h != NULL || sym->st_value != 0) + continue; rel->r_addend -= skip[val >> 3]; elf_section_data (sec)->relocs = relstart; @@ -8232,7 +8371,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) /* We shouldn't have local or global symbols defined in the TOC, but handle them anyway. */ - if (local_syms != NULL) + if (local_toc_syms) { Elf_Internal_Sym *sym; @@ -8249,14 +8388,14 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) else i = sym->st_value >> 3; - if (skip[sym->st_value >> 3] == (unsigned long) -1) + if ((skip[i] & (ref_from_discarded | can_optimize)) != 0) { (*_bfd_error_handler) (_("%s defined on removed toc entry"), bfd_elf_sym_name (ibfd, symtab_hdr, sym, NULL)); do ++i; - while (skip[i] == (unsigned long) -1); + while ((skip[i] & (ref_from_discarded | can_optimize))); sym->st_value = (bfd_vma) i << 3; } @@ -8289,7 +8428,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) /* Remove unused toc relocs, and adjust those we keep. */ wrel = relstart; for (rel = relstart; rel < relstart + toc->reloc_count; ++rel) - if (skip[rel->r_offset >> 3] != (unsigned long) -1) + if ((skip[rel->r_offset >> 3] + & (ref_from_discarded | can_optimize)) == 0) { wrel->r_offset = rel->r_offset - skip[rel->r_offset >> 3]; wrel->r_info = rel->r_info; @@ -11256,7 +11396,7 @@ ppc64_elf_action_discarded (asection *sec) return _bfd_elf_default_action_discarded (sec); } -/* REL points to a low-part reloc on a bigtoc instruction sequence. +/* 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. */ @@ -11565,6 +11705,16 @@ ppc64_elf_relocate_section (bfd *output_bfd, default: break; + case R_PPC64_LO_DS_OPT: + insn = bfd_get_32 (output_bfd, contents + rel->r_offset - d_offset); + if ((insn & (0x3f << 26)) != 58u << 26) + abort (); + insn += (14u << 26) - (58u << 26); + bfd_put_32 (output_bfd, insn, contents + rel->r_offset - d_offset); + r_type = R_PPC64_TOC16_LO; + rel->r_info = ELF64_R_INFO (r_symndx, r_type); + break; + case R_PPC64_TOC16: case R_PPC64_TOC16_LO: case R_PPC64_TOC16_DS: |