From 51aecdc5320d1707e1f034f05a378e60aae71d18 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Tue, 2 Dec 2014 15:09:16 +1030 Subject: [PATCH] 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. --- bfd/ChangeLog | 13 +++ bfd/elf64-ppc.c | 282 ++++++++++++++++++++++++++---------------------- 2 files changed, 165 insertions(+), 130 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 0489b79b066..67b00707adf 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,16 @@ +2014-12-02 Alan Modra + + * 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 PR binutils/17512 diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index c1029df87c7..2e9d35a8868 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; - } - - 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; + 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) { - if (!NO_OPD_RELOCS - && !info->relocatable - && !dec_dynrel_count (rel->r_info, sec, info, - NULL, h, sym)) - goto error_ret; + 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[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 { + /* 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;