From 3f764659c7b9bc8eb30920d37a55d3371d649d8a Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Thu, 19 Aug 2004 07:47:25 +0000 Subject: [PATCH] * elf64-ppc.c (ppc64_elf_edit_opd): Support 16 byte long .opd entries (where fd_aux overlaps next entry's fd_func). Add non_overlapping argument, use it. (ppc64_elf_check_relocs, ppc64_elf_gc_mark_hook, adjust_opd_syms, ppc64_elf_size_stubs, ppc64_elf_relocate_section, ppc64_elf_output_symbol_hook): Use address / 8 instead of address / 24 as indexes into opd_sym_map/opd_adjust array. * elf64-ppc.h (ppc64_elf_edit_opd): Adjust prototype. * emultempl/ppc64elf.em (non_overlapping_opd): New variable. (ppc_before_allocation): Pass it to ppc64_elf_edit_opd). (OPTION_NON_OVERLAPPING_OPD): Define. (PARSE_AND_LIST_OPTIONS, PARSE_AND_LIST_ARGS_CASES): Add --non-overlapping-opd option. --- bfd/ChangeLog | 11 +++ bfd/elf64-ppc.c | 163 ++++++++++++++++++++++++++++++++------- bfd/elf64-ppc.h | 2 +- ld/ChangeLog | 8 ++ ld/emultempl/ppc64elf.em | 15 +++- 5 files changed, 171 insertions(+), 28 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index ed6e667bde0..612359b4208 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,14 @@ +2004-08-19 Jakub Jelinek + + * elf64-ppc.c (ppc64_elf_edit_opd): Support 16 byte long .opd + entries (where fd_aux overlaps next entry's fd_func). + Add non_overlapping argument, use it. + (ppc64_elf_check_relocs, ppc64_elf_gc_mark_hook, adjust_opd_syms, + ppc64_elf_size_stubs, ppc64_elf_relocate_section, + ppc64_elf_output_symbol_hook): Use address / 8 instead of address / 24 + as indexes into opd_sym_map/opd_adjust array. + * elf64-ppc.h (ppc64_elf_edit_opd): Adjust prototype. + 2004-08-18 Alan Modra * elf64-ppc.c (func_desc_adjust): Give undefined dot-symbols a value diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index 33ffd3906c2..d44d8d3d452 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -4206,7 +4206,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, }; bfd_size_type amt; - amt = sec->size * sizeof (union opd_info) / 24; + amt = sec->size * sizeof (union opd_info) / 8; opd_sym_map = bfd_zalloc (abfd, amt); if (opd_sym_map == NULL) return FALSE; @@ -4513,7 +4513,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, if (s == NULL) return FALSE; else if (s != sec) - opd_sym_map[rel->r_offset / 24] = s; + opd_sym_map[rel->r_offset / 8] = s; } } /* Fall through. */ @@ -4892,7 +4892,7 @@ ppc64_elf_gc_mark_hook (asection *sec, if (!rsec->gc_mark) _bfd_elf_gc_mark (info, rsec, ppc64_elf_gc_mark_hook); - rsec = opd_sym_section[sym->st_value / 24]; + rsec = opd_sym_section[sym->st_value / 8]; } } @@ -5674,7 +5674,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED) opd_adjust = get_opd_info (sym_sec); if (opd_adjust != NULL) { - long adjust = opd_adjust[eh->elf.root.u.def.value / 24]; + long adjust = opd_adjust[eh->elf.root.u.def.value / 8]; if (adjust == -1) { /* This entry has been deleted. */ @@ -5705,10 +5705,12 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED) applications. */ bfd_boolean -ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info) +ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, + bfd_boolean non_overlapping) { bfd *ibfd; bfd_boolean some_edited = FALSE; + asection *need_pad = NULL; for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) { @@ -5720,13 +5722,14 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info) bfd_vma offset; bfd_size_type amt; long *opd_adjust; - bfd_boolean need_edit; + bfd_boolean need_edit, add_aux_fields; + bfd_size_type cnt_16b = 0; sec = bfd_get_section_by_name (ibfd, ".opd"); if (sec == NULL) continue; - amt = sec->size * sizeof (long) / 24; + amt = sec->size * sizeof (long) / 8; opd_adjust = get_opd_info (sec); if (opd_adjust == NULL) { @@ -5757,6 +5760,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info) /* First run through the relocs to check they are sane, and to determine whether we need to edit this opd section. */ need_edit = FALSE; + need_pad = sec; offset = 0; relend = relstart + sec->reloc_count; for (rel = relstart; rel < relend; ) @@ -5767,7 +5771,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info) struct elf_link_hash_entry *h; Elf_Internal_Sym *sym; - /* .opd contains a regular array of 24 byte entries. We're + /* .opd contains a regular 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 @@ -5779,6 +5783,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info) Also, there's nothing to prevent someone putting something silly in .opd with the assembler. No .opd optimization for them! */ + broken_opd: (*_bfd_error_handler) (_("%B: .opd is not a regular array of opd entries"), ibfd); need_edit = FALSE; @@ -5826,19 +5831,54 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info) || sym_sec->output_section == bfd_abs_section_ptr) need_edit = TRUE; - offset += 24; rel += 2; - /* Allow for the possibility of a reloc on the third word. */ - if (rel < relend - && rel->r_offset == offset - 8) - rel += 1; + if (rel == relend + || (rel + 1 == relend && rel->r_offset == offset + 16)) + { + if (sec->size == offset + 24) + { + need_pad = NULL; + break; + } + if (rel == relend && 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; + } + else + goto broken_opd; } - if (need_edit) + add_aux_fields = non_overlapping && cnt_16b > 0; + + if (need_edit || add_aux_fields) { Elf_Internal_Rela *write_rel; bfd_byte *rptr, *wptr; + bfd_byte *new_contents = NULL; bfd_boolean skip; + long opd_ent_size; /* This seems a waste of time as input .opd sections are all zeros as generated by gcc, but I suppose there's no reason @@ -5867,9 +5907,21 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info) wptr = sec->contents; rptr = sec->contents; + new_contents = sec->contents; + + if (add_aux_fields) + { + new_contents = bfd_malloc (sec->size + cnt_16b * 8); + if (new_contents == NULL) + return FALSE; + need_pad = FALSE; + wptr = new_contents; + } + write_rel = relstart; skip = FALSE; offset = 0; + opd_ent_size = 0; for (rel = relstart; rel < relend; rel++) { unsigned long r_symndx; @@ -5885,6 +5937,19 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info) if (rel->r_offset == offset) { 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)) + opd_ent_size = 16; + if (h != NULL && h->root.root.string[0] == '.') fdh = get_fdh ((struct ppc_link_hash_entry *) h, @@ -5901,7 +5966,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info) fdh->elf.root.u.def.value = 0; fdh->elf.root.u.def.section = sym_sec; } - opd_adjust[rel->r_offset / 24] = -1; + opd_adjust[rel->r_offset / 8] = -1; } else { @@ -5916,7 +5981,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info) 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 - sec->contents; + fdh->elf.root.u.def.value = wptr - new_contents; fdh->adjust_done = 1; } @@ -5926,14 +5991,20 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info) for the function descriptor sym which we don't have at the moment. So keep an array of adjustments. */ - opd_adjust[rel->r_offset / 24] = wptr - rptr; + opd_adjust[rel->r_offset / 8] + = (wptr - new_contents) - (rptr - sec->contents); if (wptr != rptr) - memcpy (wptr, rptr, 24); - wptr += 24; + 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 += 24; - offset += 24; + rptr += opd_ent_size; + offset += opd_ent_size; } if (skip) @@ -5971,15 +6042,21 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info) /* 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 += wptr - rptr; + rel->r_offset += opd_adjust[(offset - opd_ent_size) / 8]; if (write_rel != rel) memcpy (write_rel, rel, sizeof (*rel)); ++write_rel; } } - sec->size = wptr - sec->contents; + sec->size = wptr - new_contents; sec->reloc_count = write_rel - relstart; + if (add_aux_fields) + { + free (sec->contents); + sec->contents = new_contents; + } + /* Fudge the size too, as this is used later in elf_bfd_final_link if we are emitting relocs. */ elf_section_data (sec)->rel_hdr.sh_size @@ -6003,6 +6080,40 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info) if (some_edited) elf_link_hash_traverse (elf_hash_table (info), adjust_opd_syms, NULL); + /* If we are doing a final link and the last .opd entry is just 16 byte + long, add a 8 byte padding after it. */ + if (need_pad != NULL && !info->relocatable) + { + bfd_byte *p; + + if ((need_pad->flags & SEC_IN_MEMORY) == 0) + { + BFD_ASSERT (need_pad->size > 0); + + p = bfd_malloc (need_pad->size + 8); + if (p == NULL) + return FALSE; + + if (! bfd_get_section_contents (need_pad->owner, need_pad, + p, 0, need_pad->size)) + return FALSE; + + need_pad->contents = p; + need_pad->flags |= (SEC_IN_MEMORY | SEC_HAS_CONTENTS); + } + else + { + p = bfd_realloc (need_pad->contents, need_pad->size + 8); + if (p == NULL) + return FALSE; + + need_pad->contents = p; + } + + memset (need_pad->contents + need_pad->size, 0, 8); + need_pad->size += 8; + } + return TRUE; } @@ -7770,7 +7881,7 @@ ppc64_elf_size_stubs (bfd *output_bfd, if (hash == NULL) { - long adjust = opd_adjust[sym_value / 24]; + long adjust = opd_adjust[sym_value / 8]; if (adjust == -1) continue; sym_value += adjust; @@ -8308,7 +8419,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, opd_adjust = get_opd_info (sec); if (opd_adjust != NULL) { - long adjust = opd_adjust[(sym->st_value + rel->r_addend) / 24]; + long adjust = opd_adjust[(sym->st_value + rel->r_addend) / 8]; if (adjust == -1) relocation = 0; else @@ -9604,7 +9715,7 @@ ppc64_elf_output_symbol_hook (struct bfd_link_info *info, if (!info->relocatable) value -= input_sec->output_section->vma; - adjust = opd_adjust[value / 24]; + adjust = opd_adjust[value / 8]; if (adjust == -1) elfsym->st_value = 0; else diff --git a/bfd/elf64-ppc.h b/bfd/elf64-ppc.h index b1f4343a4ea..4a58bbe71b0 100644 --- a/bfd/elf64-ppc.h +++ b/bfd/elf64-ppc.h @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ void ppc64_elf_init_stub_bfd (bfd *, struct bfd_link_info *); bfd_boolean ppc64_elf_edit_opd - (bfd *, struct bfd_link_info *); + (bfd *, struct bfd_link_info *, bfd_boolean); asection *ppc64_elf_tls_setup (bfd *, struct bfd_link_info *); bfd_boolean ppc64_elf_tls_optimize diff --git a/ld/ChangeLog b/ld/ChangeLog index a8b8ba4793b..0bf150a124b 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,11 @@ +2004-08-19 Jakub Jelinek + + * emultempl/ppc64elf.em (non_overlapping_opd): New variable. + (ppc_before_allocation): Pass it to ppc64_elf_edit_opd). + (OPTION_NON_OVERLAPPING_OPD): Define. + (PARSE_AND_LIST_OPTIONS, PARSE_AND_LIST_ARGS_CASES): Add + --non-overlapping-opd option. + 2004-08-18 Alan Modra PR 338 diff --git a/ld/emultempl/ppc64elf.em b/ld/emultempl/ppc64elf.em index 264d63ebf1f..094a4fb55ed 100644 --- a/ld/emultempl/ppc64elf.em +++ b/ld/emultempl/ppc64elf.em @@ -51,6 +51,9 @@ static int emit_stub_syms = 0; static asection *toc_section = 0; +/* Whether to canonicalize .opd so that there are no overlapping + .opd entries. */ +static int non_overlapping_opd = 0; /* This is called before the input files are opened. We create a new fake input file to hold the stub sections. */ @@ -89,7 +92,7 @@ ppc_before_allocation (void) { if (stub_file != NULL) { - if (!ppc64_elf_edit_opd (output_bfd, &link_info)) + if (!ppc64_elf_edit_opd (output_bfd, &link_info, non_overlapping_opd)) { einfo ("%X%P: can not edit opd %E\n"); return; @@ -463,6 +466,7 @@ PARSE_AND_LIST_PROLOGUE=' #define OPTION_DOTSYMS (OPTION_STUBSYMS + 1) #define OPTION_NO_DOTSYMS (OPTION_DOTSYMS + 1) #define OPTION_NO_TLS_OPT (OPTION_NO_DOTSYMS + 1) +#define OPTION_NON_OVERLAPPING_OPD (OPTION_NO_TLS_OPT + 1) ' PARSE_AND_LIST_LONGOPTS=' @@ -471,6 +475,7 @@ PARSE_AND_LIST_LONGOPTS=' { "dotsyms", no_argument, NULL, OPTION_DOTSYMS }, { "no-dotsyms", no_argument, NULL, OPTION_NO_DOTSYMS }, { "no-tls-optimize", no_argument, NULL, OPTION_NO_TLS_OPT }, + { "non-overlapping-opd", no_argument, NULL, OPTION_NON_OVERLAPPING_OPD }, ' PARSE_AND_LIST_OPTIONS=' @@ -498,6 +503,10 @@ PARSE_AND_LIST_OPTIONS=' fprintf (file, _("\ --no-tls-optimize Don'\''t try to optimize TLS accesses.\n" )); + fprintf (file, _("\ + --non-overlapping-opd Canonicalize .opd, so that there are no overlapping\n\ + .opd entries.\n" + )); ' PARSE_AND_LIST_ARGS_CASES=' @@ -525,6 +534,10 @@ PARSE_AND_LIST_ARGS_CASES=' case OPTION_NO_TLS_OPT: notlsopt = 1; break; + + case OPTION_NON_OVERLAPPING_OPD: + non_overlapping_opd = 1; + break; ' # Put these extra ppc64elf routines in ld_${EMULATION_NAME}_emulation -- 2.30.2