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>
* elf64-ppc.c (is_static_defined): New function.

View file

@ -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:

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>
* 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_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)

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>
* 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
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)
{