aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bfd/ChangeLog8
-rw-r--r--bfd/elf64-ppc.c188
-rw-r--r--include/elf/ChangeLog4
-rw-r--r--include/elf/ppc64.h4
-rw-r--r--ld/ChangeLog5
-rw-r--r--ld/emultempl/ppc64elf.em30
6 files changed, 211 insertions, 28 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:
diff --git a/include/elf/ChangeLog b/include/elf/ChangeLog
index 2442c50..8a8e118 100644
--- a/include/elf/ChangeLog
+++ b/include/elf/ChangeLog
@@ -1,3 +1,7 @@
+2010-06-25 Alan Modra <amodra@gmail.com>
+
+ * ppc64.h (R_PPC64_LO_DS_OPT): Define.
+
2010-06-15 Joseph Myers <joseph@codesourcery.com>
* tic6x-attrs.h: New.
diff --git a/include/elf/ppc64.h b/include/elf/ppc64.h
index dfd775d..a18edd6 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 aadc774..975edc0 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,5 +1,10 @@
2010-06-25 Alan Modra <amodra@gmail.com>
+ * emultempl/ppc64elf.em (prelim_size_sections): New function.
+ (ppc_before_allocation): Use it. Size sections before toc edit too.
+
+2010-06-25 Alan Modra <amodra@gmail.com>
+
* emultempl/elf32.em (find_exp_assignment): Handle etree_provided.
2010-06-20 Danny Smith <dannysmith@users.sourceforge.net>
diff --git a/ld/emultempl/ppc64elf.em b/ld/emultempl/ppc64elf.em
index f8604a6..46d0332 100644
--- a/ld/emultempl/ppc64elf.em
+++ b/ld/emultempl/ppc64elf.em
@@ -227,6 +227,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)
{
if (stub_file != NULL)
@@ -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)
{