aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf64-ppc.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elf64-ppc.c')
-rw-r--r--bfd/elf64-ppc.c278
1 files changed, 150 insertions, 128 deletions
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index c1029df..2e9d35a 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -2954,7 +2954,9 @@ struct _ppc64_elf_section_data
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
{
/* 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;
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);
if (opd_sym_map == NULL)
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);
if (s != NULL && s != sec)
- opd_sym_map[rel->r_offset / 8] = s;
+ opd_sym_map[OPD_NDX (rel->r_offset)] = s;
}
}
/* Fall through. */
@@ -6343,7 +6345,7 @@ ppc64_elf_gc_mark_hook (asection *sec,
{
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);
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)
{
/* This entry has been deleted. */
@@ -7587,6 +7589,21 @@ dec_dynrel_count (bfd_vma r_info,
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
only remove those associated with functions in discarded link-once
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_Shdr *symtab_hdr;
Elf_Internal_Sym *local_syms;
- bfd_vma offset;
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;
if (!is_ppc64_elf (ibfd))
@@ -7641,12 +7657,13 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
info->keep_memory);
if (relstart == NULL)
return FALSE;
+ qsort (relstart, sec->reloc_count, sizeof (*relstart), sort_r_offset);
/* First run through the relocs to check they are sane, and to
determine whether we need to edit this opd section. */
need_edit = FALSE;
+ broken = FALSE;
need_pad = sec;
- offset = 0;
relend = relstart + sec->reloc_count;
for (rel = relstart; rel < relend; )
{
@@ -7655,13 +7672,14 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
asection *sym_sec;
struct elf_link_hash_entry *h;
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
point. */
- if (rel->r_offset != offset
- || rel + 1 >= relend
- || (rel + 1)->r_offset != offset + 8)
+ offset = rel->r_offset;
+ if (rel + 1 == relend
+ || rel[1].r_offset != offset + 8)
{
/* If someone messes with .opd alignment then after a
"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:
(*_bfd_error_handler)
(_("%B: .opd is not a regular array of opd entries"), ibfd);
- need_edit = FALSE;
+ broken = TRUE;
break;
}
@@ -7681,7 +7699,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
(*_bfd_error_handler)
(_("%B: unexpected reloc type %u in .opd section"),
ibfd, r_type);
- need_edit = FALSE;
+ broken = TRUE;
break;
}
@@ -7702,7 +7720,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
(*_bfd_error_handler)
(_("%B: undefined sym `%s' in .opd section"),
ibfd, sym_name);
- need_edit = FALSE;
+ broken = TRUE;
break;
}
@@ -7718,39 +7736,33 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
need_edit = TRUE;
rel += 2;
- if (rel == relend
- || (rel + 1 == relend && rel->r_offset == offset + 16))
+ if (rel + 1 == relend
+ || (rel + 2 < relend
+ && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC))
+ ++rel;
+
+ if (rel == relend)
{
if (sec->size == offset + 24)
{
need_pad = NULL;
break;
}
- if (rel == relend && sec->size == offset + 16)
+ if (sec->size == offset + 16)
{
cnt_16b++;
break;
}
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
&& ELF64_R_TYPE (rel[0].r_info) == R_PPC64_ADDR64
&& ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOC)
{
- offset += 16;
- cnt_16b++;
- }
- else if (rel + 2 < relend
- && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_ADDR64
- && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC)
- {
- offset += 24;
- rel += 1;
+ if (rel[0].r_offset == offset + 16)
+ cnt_16b++;
+ else if (rel[0].r_offset != offset + 24)
+ goto broken_opd;
}
else
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;
- if (need_edit || add_aux_fields)
+ if (!broken && (need_edit || add_aux_fields))
{
Elf_Internal_Rela *write_rel;
Elf_Internal_Shdr *rel_hdr;
bfd_byte *rptr, *wptr;
bfd_byte *new_contents;
- bfd_boolean skip;
- long opd_ent_size;
bfd_size_type amt;
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->adjust = bfd_zalloc (sec->owner, amt);
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);
if (new_contents == NULL)
return FALSE;
- need_pad = FALSE;
+ need_pad = NULL;
}
wptr = new_contents;
rptr = sec->contents;
-
write_rel = relstart;
- skip = FALSE;
- offset = 0;
- opd_ent_size = 0;
- for (rel = relstart; rel < relend; rel++)
+ for (rel = relstart; rel < relend; )
{
unsigned long r_symndx;
asection *sym_sec;
struct elf_link_hash_entry *h;
+ struct ppc_link_hash_entry *fdh = NULL;
Elf_Internal_Sym *sym;
+ long opd_ent_size;
+ Elf_Internal_Rela *next_rel;
+ bfd_boolean skip;
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 (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;
-
- /* 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))
+ if (sec->size == rel->r_offset + 16)
opd_ent_size = 16;
+ }
+ else if (next_rel->r_offset == rel->r_offset + 16)
+ opd_ent_size = 16;
- if (h != NULL
- && h->root.root.string[0] == '.')
- {
- fdh = lookup_fdh ((struct ppc_link_hash_entry *) h, htab);
- if (fdh != NULL
- && fdh->elf.root.type != bfd_link_hash_defined
- && fdh->elf.root.type != bfd_link_hash_defweak)
- fdh = NULL;
- }
+ if (h != NULL
+ && h->root.root.string[0] == '.')
+ {
+ fdh = lookup_fdh ((struct ppc_link_hash_entry *) h, htab);
+ if (fdh != NULL
+ && fdh->elf.root.type != bfd_link_hash_defined
+ && fdh->elf.root.type != bfd_link_hash_defweak)
+ fdh = NULL;
+ }
- skip = (sym_sec->owner != ibfd
- || sym_sec->output_section == bfd_abs_section_ptr);
- if (skip)
+ skip = (sym_sec->owner != ibfd
+ || sym_sec->output_section == bfd_abs_section_ptr);
+ if (skip)
+ {
+ if (fdh != NULL && sym_sec->owner == ibfd)
{
- 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;
+ /* Arrange for the function descriptor sym
+ to be dropped. */
+ fdh->elf.root.u.def.value = 0;
+ fdh->elf.root.u.def.section = sym_sec;
}
- else
- {
- /* We'll be keeping this opd entry. */
+ opd->adjust[OPD_NDX (rel->r_offset)] = -1;
- 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;
- }
+ 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;
- /* 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;
- }
+ if (++rel == next_rel)
+ break;
- if (skip)
- {
- if (!NO_OPD_RELOCS
- && !info->relocatable
- && !dec_dynrel_count (rel->r_info, sec, info,
- NULL, h, sym))
- goto error_ret;
+ 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
{
+ /* 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
- new opd entries. While we're at it, we may as well
- remove redundant relocs. */
- rel->r_offset += opd->adjust[(offset - opd_ent_size) / 8];
- if (write_rel != rel)
- memcpy (write_rel, rel, sizeof (*rel));
- ++write_rel;
+ new opd entries. */
+ for ( ; rel != next_rel; ++rel)
+ {
+ rel->r_offset += adjust;
+ if (write_rel != rel)
+ memcpy (write_rel, rel, sizeof (*rel));
+ ++write_rel;
+ }
}
+
+ rptr += opd_ent_size;
}
sec->size = wptr - new_contents;
@@ -11589,7 +11610,7 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
{
long adjust;
- adjust = opd->adjust[sym->st_value / 8];
+ adjust = opd->adjust[OPD_NDX (sym->st_value)];
if (adjust == -1)
/* Assume deleted functions won't ever be called. */
continue;
@@ -12165,7 +12186,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
if (hash == NULL && opd->adjust != NULL)
{
- long adjust = opd->adjust[sym_value / 8];
+ long adjust = opd->adjust[OPD_NDX (sym_value)];
if (adjust == -1)
continue;
code_value += adjust;
@@ -13077,7 +13098,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
opd = get_opd_info (sec);
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)
relocation = 0;
else
@@ -14844,7 +14866,7 @@ ppc64_elf_output_symbol_hook (struct bfd_link_info *info,
if (!info->relocatable)
value -= input_sec->output_section->vma;
- adjust = opd->adjust[value / 8];
+ adjust = opd->adjust[OPD_NDX (value)];
if (adjust == -1)
return 2;