From ba761f19f5658feaff39b69c0bdc84b8716cd3f9 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Fri, 25 Jun 2010 05:20:57 +0000 Subject: [PATCH] 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. --- bfd/ChangeLog | 8 ++ bfd/elf64-ppc.c | 188 +++++++++++++++++++++++++++++++++++---- include/elf/ChangeLog | 4 + include/elf/ppc64.h | 4 + ld/ChangeLog | 5 ++ ld/emultempl/ppc64elf.em | 30 +++++-- 6 files changed, 211 insertions(+), 28 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index f1365e553d..0153119150 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,11 @@ +2010-06-25 Alan Modra + + * 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 * elf64-ppc.c (is_static_defined): New function. diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index fbc76002e9..116d4196b6 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: diff --git a/include/elf/ChangeLog b/include/elf/ChangeLog index 2442c50861..8a8e118cac 100644 --- a/include/elf/ChangeLog +++ b/include/elf/ChangeLog @@ -1,3 +1,7 @@ +2010-06-25 Alan Modra + + * ppc64.h (R_PPC64_LO_DS_OPT): Define. + 2010-06-15 Joseph Myers * tic6x-attrs.h: New. diff --git a/include/elf/ppc64.h b/include/elf/ppc64.h index dfd775da1c..a18edd68fc 100644 --- a/include/elf/ppc64.h +++ b/include/elf/ppc64.h @@ -140,6 +140,10 @@ START_RELOC_NUMBERS (elf_ppc64_reloc_type) RELOC_NUMBER (R_PPC64_TLSGD, 107) RELOC_NUMBER (R_PPC64_TLSLD, 108) +#ifndef RELOC_MACROS_GEN_FUNC +/* Fake relocation only used internally by ld. */ + RELOC_NUMBER (R_PPC64_LO_DS_OPT, 128) +#endif /* Support STT_GNU_IFUNC plt calls. */ RELOC_NUMBER (R_PPC64_JMP_IREL, 247) RELOC_NUMBER (R_PPC64_IRELATIVE, 248) diff --git a/ld/ChangeLog b/ld/ChangeLog index aadc7740fc..975edc04cb 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,8 @@ +2010-06-25 Alan Modra + + * emultempl/ppc64elf.em (prelim_size_sections): New function. + (ppc_before_allocation): Use it. Size sections before toc edit too. + 2010-06-25 Alan Modra * emultempl/elf32.em (find_exp_assignment): Handle etree_provided. diff --git a/ld/emultempl/ppc64elf.em b/ld/emultempl/ppc64elf.em index f8604a6715..46d0332a2b 100644 --- a/ld/emultempl/ppc64elf.em +++ b/ld/emultempl/ppc64elf.em @@ -226,6 +226,19 @@ sort_toc_sections (lang_statement_list_type *list, } } +static void +prelim_size_sections (void) +{ + if (expld.phase != lang_mark_phase_enum) + { + expld.phase = lang_mark_phase_enum; + expld.dataseg.phase = exp_dataseg_none; + one_lang_size_sections_pass (NULL, FALSE); + /* We must not cache anything from the preliminary sizing. */ + lang_reset_memory_regions (); + } +} + static void ppc_before_allocation (void) { @@ -240,21 +253,20 @@ ppc_before_allocation (void) { /* Size the sections. This is premature, but we want to know the TLS segment layout so that certain optimizations can be done. */ - expld.phase = lang_mark_phase_enum; - expld.dataseg.phase = exp_dataseg_none; - one_lang_size_sections_pass (NULL, TRUE); + prelim_size_sections (); if (!ppc64_elf_tls_optimize (&link_info)) einfo ("%X%P: TLS problem %E\n"); - - /* We must not cache anything from the preliminary sizing. */ - lang_reset_memory_regions (); } if (!no_toc_opt - && !link_info.relocatable - && !ppc64_elf_edit_toc (&link_info)) - einfo ("%X%P: can not edit %s %E\n", "toc"); + && !link_info.relocatable) + { + prelim_size_sections (); + + if (!ppc64_elf_edit_toc (&link_info)) + einfo ("%X%P: can not edit %s %E\n", "toc"); + } if (!no_toc_sort) { -- 2.34.1