ppc64_elf_edit_opd revamp

This patch sorts .opd relocs (see pr17666) and allows .opd sections
with a mix of 16 and 24 byte entries to be edited.

	* elf64-ppc.c (OPD_NDX): Define.  Use throughout for sizing/indexing
	_opd_sec_data array, halving required memory.
	(sort_r_offset): New function.
	(ppc64_elf_edit_opd): Sort incoming relocs.  Accept .opd
	sections with a mix of 16 and 24 byte OPD entries.  Don't
	attempt to honour --non-overlapping-opd for .opd sections with
	unexpected relocs.  Simplify opd entry size calculations by
	first finding the reloc for the next entry.  Make edit loop
	handle one opd entry per iteration, with an inner loop
	handling relocs per entry.
This commit is contained in:
Alan Modra 2014-12-02 15:09:16 +10:30
parent d5552aabd6
commit 51aecdc532
2 changed files with 165 additions and 130 deletions

View File

@ -1,3 +1,16 @@
2014-12-02 Alan Modra <amodra@gmail.com>
* elf64-ppc.c (OPD_NDX): Define. Use throughout for sizing/indexing
_opd_sec_data array, halving required memory.
(sort_r_offset): New function.
(ppc64_elf_edit_opd): Sort incoming relocs. Accept .opd
sections with a mix of 16 and 24 byte OPD entries. Don't
attempt to honour --non-overlapping-opd for .opd sections with
unexpected relocs. Simplify opd entry size calculations by
first finding the reloc for the next entry. Make edit loop
handle one opd entry per iteration, with an inner loop
handling relocs per entry.
2014-12-01 Nick Clifton <nickc@redhat.com> 2014-12-01 Nick Clifton <nickc@redhat.com>
PR binutils/17512 PR binutils/17512

View File

@ -2954,7 +2954,9 @@ struct _ppc64_elf_section_data
union union
{ {
/* An array with one entry for each opd function descriptor. */ /* An array with one entry for each opd function descriptor,
and some spares since opd entries may be either 16 or 24 bytes. */
#define OPD_NDX(OFF) ((OFF) >> 4)
struct _opd_sec_data struct _opd_sec_data
{ {
/* Points to the function code section for local opd entries. */ /* Points to the function code section for local opd entries. */
@ -5005,7 +5007,7 @@ ppc64_elf_before_check_relocs (bfd *ibfd, struct bfd_link_info *info)
bfd_size_type amt; bfd_size_type amt;
asection **opd_sym_map; asection **opd_sym_map;
amt = opd->size * sizeof (*opd_sym_map) / 8; amt = OPD_NDX (opd->size) * sizeof (*opd_sym_map);
opd_sym_map = bfd_zalloc (ibfd, amt); opd_sym_map = bfd_zalloc (ibfd, amt);
if (opd_sym_map == NULL) if (opd_sym_map == NULL)
return FALSE; return FALSE;
@ -5676,7 +5678,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
s = bfd_section_from_elf_index (abfd, isym->st_shndx); s = bfd_section_from_elf_index (abfd, isym->st_shndx);
if (s != NULL && s != sec) if (s != NULL && s != sec)
opd_sym_map[rel->r_offset / 8] = s; opd_sym_map[OPD_NDX (rel->r_offset)] = s;
} }
} }
/* Fall through. */ /* Fall through. */
@ -6343,7 +6345,7 @@ ppc64_elf_gc_mark_hook (asection *sec,
{ {
rsec->gc_mark = 1; rsec->gc_mark = 1;
rsec = opd->func_sec[(sym->st_value + rel->r_addend) / 8]; rsec = opd->func_sec[OPD_NDX (sym->st_value + rel->r_addend)];
} }
} }
@ -7404,7 +7406,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
opd = get_opd_info (sym_sec); opd = get_opd_info (sym_sec);
if (opd != NULL && opd->adjust != NULL) if (opd != NULL && opd->adjust != NULL)
{ {
long adjust = opd->adjust[eh->elf.root.u.def.value / 8]; long adjust = opd->adjust[OPD_NDX (eh->elf.root.u.def.value)];
if (adjust == -1) if (adjust == -1)
{ {
/* This entry has been deleted. */ /* This entry has been deleted. */
@ -7587,6 +7589,21 @@ dec_dynrel_count (bfd_vma r_info,
return FALSE; return FALSE;
} }
/* qsort comparison function sorting relocs by r_offset. */
static int
sort_r_offset (const void *p, const void *q)
{
const Elf_Internal_Rela *a = (const Elf_Internal_Rela *) p;
const Elf_Internal_Rela *b = (const Elf_Internal_Rela *) q;
if (a->r_offset < b->r_offset)
return -1;
else if (a->r_offset > b->r_offset)
return 1;
return 0;
}
/* Remove unused Official Procedure Descriptor entries. Currently we /* Remove unused Official Procedure Descriptor entries. Currently we
only remove those associated with functions in discarded link-once only remove those associated with functions in discarded link-once
sections, or weakly defined functions that have been overridden. It sections, or weakly defined functions that have been overridden. It
@ -7611,9 +7628,8 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
Elf_Internal_Rela *relstart, *rel, *relend; Elf_Internal_Rela *relstart, *rel, *relend;
Elf_Internal_Shdr *symtab_hdr; Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Sym *local_syms; Elf_Internal_Sym *local_syms;
bfd_vma offset;
struct _opd_sec_data *opd; struct _opd_sec_data *opd;
bfd_boolean need_edit, add_aux_fields; bfd_boolean need_edit, add_aux_fields, broken;
bfd_size_type cnt_16b = 0; bfd_size_type cnt_16b = 0;
if (!is_ppc64_elf (ibfd)) if (!is_ppc64_elf (ibfd))
@ -7641,12 +7657,13 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
info->keep_memory); info->keep_memory);
if (relstart == NULL) if (relstart == NULL)
return FALSE; return FALSE;
qsort (relstart, sec->reloc_count, sizeof (*relstart), sort_r_offset);
/* First run through the relocs to check they are sane, and to /* First run through the relocs to check they are sane, and to
determine whether we need to edit this opd section. */ determine whether we need to edit this opd section. */
need_edit = FALSE; need_edit = FALSE;
broken = FALSE;
need_pad = sec; need_pad = sec;
offset = 0;
relend = relstart + sec->reloc_count; relend = relstart + sec->reloc_count;
for (rel = relstart; rel < relend; ) for (rel = relstart; rel < relend; )
{ {
@ -7655,13 +7672,14 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
asection *sym_sec; asection *sym_sec;
struct elf_link_hash_entry *h; struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym; Elf_Internal_Sym *sym;
bfd_vma offset;
/* .opd contains a regular array of 16 or 24 byte entries. We're /* .opd contains an array of 16 or 24 byte entries. We're
only interested in the reloc pointing to a function entry only interested in the reloc pointing to a function entry
point. */ point. */
if (rel->r_offset != offset offset = rel->r_offset;
|| rel + 1 >= relend if (rel + 1 == relend
|| (rel + 1)->r_offset != offset + 8) || rel[1].r_offset != offset + 8)
{ {
/* If someone messes with .opd alignment then after a /* If someone messes with .opd alignment then after a
"ld -r" we might have padding in the middle of .opd. "ld -r" we might have padding in the middle of .opd.
@ -7671,7 +7689,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
broken_opd: broken_opd:
(*_bfd_error_handler) (*_bfd_error_handler)
(_("%B: .opd is not a regular array of opd entries"), ibfd); (_("%B: .opd is not a regular array of opd entries"), ibfd);
need_edit = FALSE; broken = TRUE;
break; break;
} }
@ -7681,7 +7699,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
(*_bfd_error_handler) (*_bfd_error_handler)
(_("%B: unexpected reloc type %u in .opd section"), (_("%B: unexpected reloc type %u in .opd section"),
ibfd, r_type); ibfd, r_type);
need_edit = FALSE; broken = TRUE;
break; break;
} }
@ -7702,7 +7720,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
(*_bfd_error_handler) (*_bfd_error_handler)
(_("%B: undefined sym `%s' in .opd section"), (_("%B: undefined sym `%s' in .opd section"),
ibfd, sym_name); ibfd, sym_name);
need_edit = FALSE; broken = TRUE;
break; break;
} }
@ -7718,39 +7736,33 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
need_edit = TRUE; need_edit = TRUE;
rel += 2; rel += 2;
if (rel == relend if (rel + 1 == relend
|| (rel + 1 == relend && rel->r_offset == offset + 16)) || (rel + 2 < relend
&& ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC))
++rel;
if (rel == relend)
{ {
if (sec->size == offset + 24) if (sec->size == offset + 24)
{ {
need_pad = NULL; need_pad = NULL;
break; break;
} }
if (rel == relend && sec->size == offset + 16) if (sec->size == offset + 16)
{ {
cnt_16b++; cnt_16b++;
break; break;
} }
goto broken_opd; goto broken_opd;
} }
if (rel->r_offset == offset + 24)
offset += 24;
else if (rel->r_offset != offset + 16)
goto broken_opd;
else if (rel + 1 < relend else if (rel + 1 < relend
&& ELF64_R_TYPE (rel[0].r_info) == R_PPC64_ADDR64 && ELF64_R_TYPE (rel[0].r_info) == R_PPC64_ADDR64
&& ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOC) && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOC)
{ {
offset += 16; if (rel[0].r_offset == offset + 16)
cnt_16b++; cnt_16b++;
} else if (rel[0].r_offset != offset + 24)
else if (rel + 2 < relend goto broken_opd;
&& ELF64_R_TYPE (rel[1].r_info) == R_PPC64_ADDR64
&& ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC)
{
offset += 24;
rel += 1;
} }
else else
goto broken_opd; goto broken_opd;
@ -7758,18 +7770,16 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
add_aux_fields = htab->params->non_overlapping_opd && cnt_16b > 0; add_aux_fields = htab->params->non_overlapping_opd && cnt_16b > 0;
if (need_edit || add_aux_fields) if (!broken && (need_edit || add_aux_fields))
{ {
Elf_Internal_Rela *write_rel; Elf_Internal_Rela *write_rel;
Elf_Internal_Shdr *rel_hdr; Elf_Internal_Shdr *rel_hdr;
bfd_byte *rptr, *wptr; bfd_byte *rptr, *wptr;
bfd_byte *new_contents; bfd_byte *new_contents;
bfd_boolean skip;
long opd_ent_size;
bfd_size_type amt; bfd_size_type amt;
new_contents = NULL; new_contents = NULL;
amt = sec->size * sizeof (long) / 8; amt = OPD_NDX (sec->size) * sizeof (long);
opd = &ppc64_elf_section_data (sec)->u.opd; opd = &ppc64_elf_section_data (sec)->u.opd;
opd->adjust = bfd_zalloc (sec->owner, amt); opd->adjust = bfd_zalloc (sec->owner, amt);
if (opd->adjust == NULL) if (opd->adjust == NULL)
@ -7807,123 +7817,134 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
new_contents = bfd_malloc (sec->size + cnt_16b * 8); new_contents = bfd_malloc (sec->size + cnt_16b * 8);
if (new_contents == NULL) if (new_contents == NULL)
return FALSE; return FALSE;
need_pad = FALSE; need_pad = NULL;
} }
wptr = new_contents; wptr = new_contents;
rptr = sec->contents; rptr = sec->contents;
write_rel = relstart; write_rel = relstart;
skip = FALSE; for (rel = relstart; rel < relend; )
offset = 0;
opd_ent_size = 0;
for (rel = relstart; rel < relend; rel++)
{ {
unsigned long r_symndx; unsigned long r_symndx;
asection *sym_sec; asection *sym_sec;
struct elf_link_hash_entry *h; struct elf_link_hash_entry *h;
struct ppc_link_hash_entry *fdh = NULL;
Elf_Internal_Sym *sym; Elf_Internal_Sym *sym;
long opd_ent_size;
Elf_Internal_Rela *next_rel;
bfd_boolean skip;
r_symndx = ELF64_R_SYM (rel->r_info); r_symndx = ELF64_R_SYM (rel->r_info);
if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms, if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
r_symndx, ibfd)) r_symndx, ibfd))
goto error_ret; goto error_ret;
if (rel->r_offset == offset) next_rel = rel + 2;
if (next_rel + 1 == relend
|| (next_rel + 2 < relend
&& ELF64_R_TYPE (next_rel[2].r_info) == R_PPC64_TOC))
++next_rel;
/* See if the .opd entry is full 24 byte or
16 byte (with fd_aux entry overlapped with next
fd_func). */
opd_ent_size = 24;
if (next_rel == relend)
{ {
struct ppc_link_hash_entry *fdh = NULL; if (sec->size == rel->r_offset + 16)
/* See if the .opd entry is full 24 byte or
16 byte (with fd_aux entry overlapped with next
fd_func). */
opd_ent_size = 24;
if ((rel + 2 == relend && sec->size == offset + 16)
|| (rel + 3 < relend
&& rel[2].r_offset == offset + 16
&& rel[3].r_offset == offset + 24
&& ELF64_R_TYPE (rel[2].r_info) == R_PPC64_ADDR64
&& ELF64_R_TYPE (rel[3].r_info) == R_PPC64_TOC))
opd_ent_size = 16; opd_ent_size = 16;
}
else if (next_rel->r_offset == rel->r_offset + 16)
opd_ent_size = 16;
if (h != NULL if (h != NULL
&& h->root.root.string[0] == '.') && h->root.root.string[0] == '.')
{ {
fdh = lookup_fdh ((struct ppc_link_hash_entry *) h, htab); fdh = lookup_fdh ((struct ppc_link_hash_entry *) h, htab);
if (fdh != NULL if (fdh != NULL
&& fdh->elf.root.type != bfd_link_hash_defined && fdh->elf.root.type != bfd_link_hash_defined
&& fdh->elf.root.type != bfd_link_hash_defweak) && fdh->elf.root.type != bfd_link_hash_defweak)
fdh = NULL; fdh = NULL;
}
skip = (sym_sec->owner != ibfd
|| sym_sec->output_section == bfd_abs_section_ptr);
if (skip)
{
if (fdh != NULL && sym_sec->owner == ibfd)
{
/* Arrange for the function descriptor sym
to be dropped. */
fdh->elf.root.u.def.value = 0;
fdh->elf.root.u.def.section = sym_sec;
}
opd->adjust[rel->r_offset / 8] = -1;
}
else
{
/* We'll be keeping this opd entry. */
if (fdh != NULL)
{
/* Redefine the function descriptor symbol to
this location in the opd section. It is
necessary to update the value here rather
than using an array of adjustments as we do
for local symbols, because various places
in the generic ELF code use the value
stored in u.def.value. */
fdh->elf.root.u.def.value = wptr - new_contents;
fdh->adjust_done = 1;
}
/* Local syms are a bit tricky. We could
tweak them as they can be cached, but
we'd need to look through the local syms
for the function descriptor sym which we
don't have at the moment. So keep an
array of adjustments. */
opd->adjust[rel->r_offset / 8]
= (wptr - new_contents) - (rptr - sec->contents);
if (wptr != rptr)
memcpy (wptr, rptr, opd_ent_size);
wptr += opd_ent_size;
if (add_aux_fields && opd_ent_size == 16)
{
memset (wptr, '\0', 8);
wptr += 8;
}
}
rptr += opd_ent_size;
offset += opd_ent_size;
} }
skip = (sym_sec->owner != ibfd
|| sym_sec->output_section == bfd_abs_section_ptr);
if (skip) if (skip)
{ {
if (!NO_OPD_RELOCS if (fdh != NULL && sym_sec->owner == ibfd)
&& !info->relocatable {
&& !dec_dynrel_count (rel->r_info, sec, info, /* Arrange for the function descriptor sym
NULL, h, sym)) to be dropped. */
goto error_ret; fdh->elf.root.u.def.value = 0;
fdh->elf.root.u.def.section = sym_sec;
}
opd->adjust[OPD_NDX (rel->r_offset)] = -1;
if (NO_OPD_RELOCS || info->relocatable)
rel = next_rel;
else
while (1)
{
if (!dec_dynrel_count (rel->r_info, sec, info,
NULL, h, sym))
goto error_ret;
if (++rel == next_rel)
break;
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;
}
} }
else else
{ {
/* We'll be keeping this opd entry. */
long adjust;
if (fdh != NULL)
{
/* Redefine the function descriptor symbol to
this location in the opd section. It is
necessary to update the value here rather
than using an array of adjustments as we do
for local symbols, because various places
in the generic ELF code use the value
stored in u.def.value. */
fdh->elf.root.u.def.value = wptr - new_contents;
fdh->adjust_done = 1;
}
/* Local syms are a bit tricky. We could
tweak them as they can be cached, but
we'd need to look through the local syms
for the function descriptor sym which we
don't have at the moment. So keep an
array of adjustments. */
adjust = (wptr - new_contents) - (rptr - sec->contents);
opd->adjust[OPD_NDX (rel->r_offset)] = adjust;
if (wptr != rptr)
memcpy (wptr, rptr, opd_ent_size);
wptr += opd_ent_size;
if (add_aux_fields && opd_ent_size == 16)
{
memset (wptr, '\0', 8);
wptr += 8;
}
/* We need to adjust any reloc offsets to point to the /* We need to adjust any reloc offsets to point to the
new opd entries. While we're at it, we may as well new opd entries. */
remove redundant relocs. */ for ( ; rel != next_rel; ++rel)
rel->r_offset += opd->adjust[(offset - opd_ent_size) / 8]; {
if (write_rel != rel) rel->r_offset += adjust;
memcpy (write_rel, rel, sizeof (*rel)); if (write_rel != rel)
++write_rel; memcpy (write_rel, rel, sizeof (*rel));
++write_rel;
}
} }
rptr += opd_ent_size;
} }
sec->size = wptr - new_contents; sec->size = wptr - new_contents;
@ -11589,7 +11610,7 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
{ {
long adjust; long adjust;
adjust = opd->adjust[sym->st_value / 8]; adjust = opd->adjust[OPD_NDX (sym->st_value)];
if (adjust == -1) if (adjust == -1)
/* Assume deleted functions won't ever be called. */ /* Assume deleted functions won't ever be called. */
continue; continue;
@ -12165,7 +12186,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
if (hash == NULL && opd->adjust != NULL) if (hash == NULL && opd->adjust != NULL)
{ {
long adjust = opd->adjust[sym_value / 8]; long adjust = opd->adjust[OPD_NDX (sym_value)];
if (adjust == -1) if (adjust == -1)
continue; continue;
code_value += adjust; code_value += adjust;
@ -13077,7 +13098,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
opd = get_opd_info (sec); opd = get_opd_info (sec);
if (opd != NULL && opd->adjust != NULL) if (opd != NULL && opd->adjust != NULL)
{ {
long adjust = opd->adjust[(sym->st_value + rel->r_addend) / 8]; long adjust = opd->adjust[OPD_NDX (sym->st_value
+ rel->r_addend)];
if (adjust == -1) if (adjust == -1)
relocation = 0; relocation = 0;
else else
@ -14844,7 +14866,7 @@ ppc64_elf_output_symbol_hook (struct bfd_link_info *info,
if (!info->relocatable) if (!info->relocatable)
value -= input_sec->output_section->vma; value -= input_sec->output_section->vma;
adjust = opd->adjust[value / 8]; adjust = opd->adjust[OPD_NDX (value)];
if (adjust == -1) if (adjust == -1)
return 2; return 2;