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.
This commit is contained in:
Alan Modra
2010-06-25 05:20:57 +00:00
parent bded3693ae
commit ba761f19f5
6 changed files with 211 additions and 28 deletions

View File

@ -1,3 +1,11 @@
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> 2010-06-25 Alan Modra <amodra@gmail.com>
* elf64-ppc.c (is_static_defined): New function. * elf64-ppc.c (is_static_defined): New function.

View File

@ -7846,6 +7846,8 @@ struct adjust_toc_info
bfd_boolean global_toc_syms; bfd_boolean global_toc_syms;
}; };
enum toc_skip_enum { ref_from_discarded = 1, can_optimize = 2 };
static bfd_boolean static bfd_boolean
adjust_toc_syms (struct elf_link_hash_entry *h, void *inf) 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 else
i = eh->elf.root.u.def.value >> 3; 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) (*_bfd_error_handler)
(_("%s defined on removed toc entry"), eh->elf.root.root.string); (_("%s defined on removed toc entry"), eh->elf.root.root.string);
do do
++i; ++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; 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; goto error_ret;
} }
skip[val >> 3] = 1; skip[val >> 3] = ref_from_discarded;
} }
if (elf_section_data (sec)->relocs != relstart) if (elf_section_data (sec)->relocs != relstart)
free (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) if (skip == NULL)
continue; continue;
@ -8100,12 +8176,37 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
if (val >= toc->size) if (val >= toc->size)
continue; 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 /* For the toc section, we only mark as used if
this entry itself isn't unused. */ this entry itself isn't unused. */
if (sec == toc if (sec == toc
&& !used[val >> 3] && !used[val >> 3]
&& (used[rel->r_offset >> 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 /* Do all the relocs again, to catch reference
chains. */ chains. */
repeat = 1; repeat = 1;
@ -8127,13 +8228,15 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
{ {
if (*keep) if (*keep)
{ {
*drop = 0; *drop &= ~ref_from_discarded;
if ((*drop & can_optimize) != 0)
some_unused = 1;
last = 0; last = 0;
} }
else if (*drop) else if (*drop)
{ {
some_unused = 1; some_unused = 1;
last = 1; last = ref_from_discarded;
} }
else else
*drop = last; *drop = last;
@ -8145,6 +8248,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
{ {
bfd_byte *contents, *src; bfd_byte *contents, *src;
unsigned long off; unsigned long off;
bfd_boolean local_toc_syms = FALSE;
/* Shuffle the toc contents, and at the same time convert the /* Shuffle the toc contents, and at the same time convert the
skip array from booleans into offsets. */ skip array from booleans into offsets. */
@ -8157,11 +8261,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
src < contents + toc->size; src < contents + toc->size;
src += 8, ++drop) src += 8, ++drop)
{ {
if (*drop) if ((*drop & (can_optimize | ref_from_discarded)) != 0)
{ off += 8;
*drop = (unsigned long) -1;
off += 8;
}
else if (off != 0) else if (off != 0)
{ {
*drop = off; *drop = off;
@ -8172,7 +8273,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
toc->rawsize = toc->size; toc->rawsize = toc->size;
toc->size = src - contents - off; 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) for (sec = ibfd->sections; sec != NULL; sec = sec->next)
{ {
if (sec->reloc_count == 0 if (sec->reloc_count == 0
@ -8214,13 +8316,50 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
r_symndx, ibfd)) r_symndx, ibfd))
goto error_ret; goto error_ret;
if (sym_sec != toc || h != NULL || sym->st_value != 0) if (sym_sec != toc)
continue; 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) if (val > toc->rawsize)
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]; rel->r_addend -= skip[val >> 3];
elf_section_data (sec)->relocs = relstart; 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, /* We shouldn't have local or global symbols defined in the TOC,
but handle them anyway. */ but handle them anyway. */
if (local_syms != NULL) if (local_toc_syms)
{ {
Elf_Internal_Sym *sym; Elf_Internal_Sym *sym;
@ -8249,14 +8388,14 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
else else
i = sym->st_value >> 3; 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) (*_bfd_error_handler)
(_("%s defined on removed toc entry"), (_("%s defined on removed toc entry"),
bfd_elf_sym_name (ibfd, symtab_hdr, sym, NULL)); bfd_elf_sym_name (ibfd, symtab_hdr, sym, NULL));
do do
++i; ++i;
while (skip[i] == (unsigned long) -1); while ((skip[i] & (ref_from_discarded | can_optimize)));
sym->st_value = (bfd_vma) i << 3; 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. */ /* Remove unused toc relocs, and adjust those we keep. */
wrel = relstart; wrel = relstart;
for (rel = relstart; rel < relstart + toc->reloc_count; ++rel) 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_offset = rel->r_offset - skip[rel->r_offset >> 3];
wrel->r_info = rel->r_info; wrel->r_info = rel->r_info;
@ -11256,7 +11396,7 @@ ppc64_elf_action_discarded (asection *sec)
return _bfd_elf_default_action_discarded (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 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. */ 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: default:
break; 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:
case R_PPC64_TOC16_LO: case R_PPC64_TOC16_LO:
case R_PPC64_TOC16_DS: case R_PPC64_TOC16_DS:

View File

@ -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> 2010-06-15 Joseph Myers <joseph@codesourcery.com>
* tic6x-attrs.h: New. * tic6x-attrs.h: New.

View File

@ -140,6 +140,10 @@ START_RELOC_NUMBERS (elf_ppc64_reloc_type)
RELOC_NUMBER (R_PPC64_TLSGD, 107) RELOC_NUMBER (R_PPC64_TLSGD, 107)
RELOC_NUMBER (R_PPC64_TLSLD, 108) 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. */ /* Support STT_GNU_IFUNC plt calls. */
RELOC_NUMBER (R_PPC64_JMP_IREL, 247) RELOC_NUMBER (R_PPC64_JMP_IREL, 247)
RELOC_NUMBER (R_PPC64_IRELATIVE, 248) RELOC_NUMBER (R_PPC64_IRELATIVE, 248)

View File

@ -1,3 +1,8 @@
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> 2010-06-25 Alan Modra <amodra@gmail.com>
* emultempl/elf32.em (find_exp_assignment): Handle etree_provided. * emultempl/elf32.em (find_exp_assignment): Handle etree_provided.

View File

@ -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 static void
ppc_before_allocation (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 /* Size the sections. This is premature, but we want to know the
TLS segment layout so that certain optimizations can be done. */ TLS segment layout so that certain optimizations can be done. */
expld.phase = lang_mark_phase_enum; prelim_size_sections ();
expld.dataseg.phase = exp_dataseg_none;
one_lang_size_sections_pass (NULL, TRUE);
if (!ppc64_elf_tls_optimize (&link_info)) if (!ppc64_elf_tls_optimize (&link_info))
einfo ("%X%P: TLS problem %E\n"); einfo ("%X%P: TLS problem %E\n");
/* We must not cache anything from the preliminary sizing. */
lang_reset_memory_regions ();
} }
if (!no_toc_opt if (!no_toc_opt
&& !link_info.relocatable && !link_info.relocatable)
&& !ppc64_elf_edit_toc (&link_info)) {
einfo ("%X%P: can not edit %s %E\n", "toc"); prelim_size_sections ();
if (!ppc64_elf_edit_toc (&link_info))
einfo ("%X%P: can not edit %s %E\n", "toc");
}
if (!no_toc_sort) if (!no_toc_sort)
{ {