X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=bfd%2Felf64-ppc.c;h=543a7272b286e4c48d273dda03f9cc38b44584a2;hb=fbd9ad907dc6a283dc1bec51ecd91355ac866949;hp=c6a238f91bf98d0941d770797f7a870d537a4cef;hpb=f961d9dde20987ad0b69b6f3722ff5e29e2979c7;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index c6a238f91b..543a7272b2 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -1,6 +1,6 @@ /* PowerPC64-specific support for 64-bit ELF. Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, - 2009, 2010 Free Software Foundation, Inc. + 2009, 2010, 2011 Free Software Foundation, Inc. Written by Linus Nordberg, Swox AB , based on elf32-ppc.c by Ian Lance Taylor. Largely rewritten by Alan Modra. @@ -61,6 +61,7 @@ static bfd_vma opd_entry_value #define TARGET_BIG_SYM bfd_elf64_powerpc_vec #define TARGET_BIG_NAME "elf64-powerpc" #define ELF_ARCH bfd_arch_powerpc +#define ELF_TARGET_ID PPC64_ELF_DATA #define ELF_MACHINE_CODE EM_PPC64 #define ELF_MAXPAGESIZE 0x10000 #define ELF_COMMONPAGESIZE 0x1000 @@ -78,12 +79,13 @@ static bfd_vma opd_entry_value #define bfd_elf64_mkobject ppc64_elf_mkobject #define bfd_elf64_bfd_reloc_type_lookup ppc64_elf_reloc_type_lookup -#define bfd_elf64_bfd_reloc_name_lookup ppc64_elf_reloc_name_lookup +#define bfd_elf64_bfd_reloc_name_lookup ppc64_elf_reloc_name_lookup #define bfd_elf64_bfd_merge_private_bfd_data ppc64_elf_merge_private_bfd_data #define bfd_elf64_new_section_hook ppc64_elf_new_section_hook #define bfd_elf64_bfd_link_hash_table_create ppc64_elf_link_hash_table_create #define bfd_elf64_bfd_link_hash_table_free ppc64_elf_link_hash_table_free #define bfd_elf64_get_synthetic_symtab ppc64_elf_get_synthetic_symtab +#define bfd_elf64_bfd_link_just_syms ppc64_elf_link_just_syms #define elf_backend_object_p ppc64_elf_object_p #define elf_backend_grok_prstatus ppc64_elf_grok_prstatus @@ -2592,6 +2594,10 @@ struct ppc64_elf_obj_tdata /* A copy of relocs before they are modified for --emit-relocs. */ Elf_Internal_Rela *opd_relocs; + + /* Nonzero if this bfd has small toc/got relocs, ie. that expect + the reloc to be in the range -32768 to 32767. */ + unsigned int has_small_toc_reloc; }; #define ppc64_elf_tdata(bfd) \ @@ -2647,7 +2653,7 @@ ppc64_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12); /* pr_pid */ - elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 32); + elf_tdata (abfd)->core_lwpid = bfd_get_32 (abfd, note->descdata + 32); /* pr_reg */ offset = 112; @@ -3311,8 +3317,9 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd, { if (sec->vma > ent) break; - if ((sec->flags & SEC_ALLOC) == 0 - || (sec->flags & SEC_LOAD) == 0) + /* SEC_LOAD may not be set if SEC is from a separate debug + info file. */ + if ((sec->flags & SEC_ALLOC) == 0) break; if ((sec->flags & SEC_CODE) != 0) s->section = sec; @@ -3784,6 +3791,7 @@ struct ppc_link_hash_table unsigned int do_multi_toc:1; unsigned int multi_toc_needed:1; unsigned int second_toc_pass:1; + unsigned int do_toc_opt:1; /* Set on error. */ unsigned int stub_error:1; @@ -3810,16 +3818,13 @@ struct ppc_link_hash_table /* Nonzero if this section has any toc or got relocs. */ #define has_toc_reloc sec_flg2 -/* Nonzero if this section has small toc/got relocs, ie. that expect - the reloc to be in the range -32768 to 32767. */ -#define has_small_toc_reloc sec_flg3 - /* Nonzero if this section has a call to another section that uses the toc or got. */ -#define makes_toc_func_call sec_flg4 +#define makes_toc_func_call sec_flg3 /* Recursion protection when determining above flag. */ -#define call_check_in_progress sec_flg5 +#define call_check_in_progress sec_flg4 +#define call_check_done sec_flg5 /* Get the ppc64 ELF linker hash table from a link_info structure. */ @@ -4557,7 +4562,7 @@ make_fdh (struct bfd_link_info *info, function type. */ static bfd_boolean -ppc64_elf_add_symbol_hook (bfd *ibfd ATTRIBUTE_UNUSED, +ppc64_elf_add_symbol_hook (bfd *ibfd, struct bfd_link_info *info, Elf_Internal_Sym *isym, const char **name ATTRIBUTE_UNUSED, @@ -4565,12 +4570,19 @@ ppc64_elf_add_symbol_hook (bfd *ibfd ATTRIBUTE_UNUSED, asection **sec, bfd_vma *value ATTRIBUTE_UNUSED) { + if ((ibfd->flags & DYNAMIC) == 0 + && ELF_ST_BIND (isym->st_info) == STB_GNU_UNIQUE) + elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE; + if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) - elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE; + { + if ((ibfd->flags & DYNAMIC) == 0) + elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE; + } else if (ELF_ST_TYPE (isym->st_info) == STT_FUNC) ; else if (*sec != NULL - && strcmp (bfd_get_section_name (ibfd, *sec), ".opd") == 0) + && strcmp ((*sec)->name, ".opd") == 0) isym->st_info = ELF_ST_INFO (ELF_ST_BIND (isym->st_info), STT_FUNC); return TRUE; @@ -4739,6 +4751,25 @@ ppc64_elf_as_needed_cleanup (bfd *ibfd ATTRIBUTE_UNUSED, return TRUE; } +/* If --just-symbols against a final linked binary, then assume we need + toc adjusting stubs when calling functions defined there. */ + +static void +ppc64_elf_link_just_syms (asection *sec, struct bfd_link_info *info) +{ + if ((sec->flags & SEC_CODE) != 0 + && (sec->owner->flags & (EXEC_P | DYNAMIC)) != 0 + && is_ppc64_elf (sec->owner)) + { + asection *got = bfd_get_section_by_name (sec->owner, ".got"); + if (got != NULL + && got->size >= elf_backend_got_header_size + && bfd_get_section_by_name (sec->owner, ".opd") != NULL) + sec->has_toc_reloc = 1; + } + _bfd_elf_link_just_syms (sec, info); +} + static struct plt_entry ** update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr, unsigned long r_symndx, bfd_vma r_addend, int tls_type) @@ -4839,7 +4870,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, { struct ppc_link_hash_table *htab; Elf_Internal_Shdr *symtab_hdr; - struct elf_link_hash_entry **sym_hashes, **sym_hashes_end; + struct elf_link_hash_entry **sym_hashes; const Elf_Internal_Rela *rel; const Elf_Internal_Rela *rel_end; asection *sreloc; @@ -4869,15 +4900,10 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, dottga = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr", FALSE, FALSE, TRUE); symtab_hdr = &elf_symtab_hdr (abfd); - sym_hashes = elf_sym_hashes (abfd); - sym_hashes_end = (sym_hashes - + symtab_hdr->sh_size / sizeof (Elf64_External_Sym) - - symtab_hdr->sh_info); - sreloc = NULL; opd_sym_map = NULL; - if (strcmp (bfd_get_section_name (abfd, sec), ".opd") == 0) + if (strcmp (sec->name, ".opd") == 0) { /* Garbage collection needs some extra help with .opd sections. We don't want to necessarily keep everything referenced by @@ -5023,7 +5049,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, || r_type == R_PPC64_GOT16_DS) { htab->do_multi_toc = 1; - sec->has_small_toc_reloc = 1; + ppc64_elf_tdata (abfd)->has_small_toc_reloc = 1; } if (ppc64_elf_tdata (abfd)->got == NULL @@ -5124,7 +5150,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_PPC64_TOC16: case R_PPC64_TOC16_DS: htab->do_multi_toc = 1; - sec->has_small_toc_reloc = 1; + ppc64_elf_tdata (abfd)->has_small_toc_reloc = 1; case R_PPC64_TOC16_LO: case R_PPC64_TOC16_HI: case R_PPC64_TOC16_HA: @@ -5464,9 +5490,12 @@ opd_entry_value (asection *opd_sec, /* No relocs implies we are linking a --just-symbols object. */ if (opd_sec->reloc_count == 0) { - if (!bfd_get_section_contents (opd_bfd, opd_sec, &val, offset, 8)) + char buf[8]; + + if (!bfd_get_section_contents (opd_bfd, opd_sec, buf, offset, 8)) return (bfd_vma) -1; + val = bfd_get_64 (opd_bfd, buf); if (code_sec != NULL) { asection *sec, *likely = NULL; @@ -5561,6 +5590,17 @@ opd_entry_value (asection *opd_sec, return val; } +/* Return true if symbol is defined in a regular object file. */ + +static bfd_boolean +is_static_defined (struct elf_link_hash_entry *h) +{ + return ((h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && h->root.u.def.section != NULL + && h->root.u.def.section->output_section != NULL); +} + /* If FDH is a function descriptor symbol, return the associated code entry symbol if it is defined. Return NULL otherwise. */ @@ -6678,6 +6718,7 @@ get_tls_mask (unsigned char **tls_maskp, if ((*tls_maskp != NULL && **tls_maskp != 0) || sec == NULL + || ppc64_elf_section_data (sec) == NULL || ppc64_elf_section_data (sec)->sec_type != sec_toc) return 1; @@ -6699,10 +6740,7 @@ get_tls_mask (unsigned char **tls_maskp, *toc_addend = ppc64_elf_section_data (sec)->u.toc.add[off / 8]; if (!get_sym_h (&h, &sym, &sec, tls_maskp, locsymsp, r_symndx, ibfd)) return 0; - if ((h == NULL - || ((h->root.type == bfd_link_hash_defined - || h->root.type == bfd_link_hash_defweak) - && !h->def_dynamic)) + if ((h == NULL || is_static_defined (h)) && (next_r == -1 || next_r == -2)) return 1 - next_r; return 1; @@ -6913,12 +6951,14 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping) Elf_Internal_Rela *relstart, *rel, *relend; Elf_Internal_Shdr *symtab_hdr; Elf_Internal_Sym *local_syms; - struct elf_link_hash_entry **sym_hashes; bfd_vma offset; struct _opd_sec_data *opd; bfd_boolean need_edit, add_aux_fields; bfd_size_type cnt_16b = 0; + if (!is_ppc64_elf (ibfd)) + continue; + sec = bfd_get_section_by_name (ibfd, ".opd"); if (sec == NULL || sec->size == 0) continue; @@ -6935,7 +6975,6 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping) local_syms = NULL; symtab_hdr = &elf_symtab_hdr (ibfd); - sym_hashes = elf_sym_hashes (ibfd); /* Read the relocations. */ relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL, @@ -7062,6 +7101,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping) if (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; @@ -7241,9 +7281,8 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping) /* Fudge the header 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 - = sec->reloc_count * elf_section_data (sec)->rel_hdr.sh_entsize; - BFD_ASSERT (elf_section_data (sec)->rel_hdr2 == NULL); + rel_hdr = _bfd_elf_single_rel_hdr (sec); + rel_hdr->sh_size = sec->reloc_count * rel_hdr->sh_entsize; some_edited = TRUE; } else if (elf_section_data (sec)->relocs != relstart) @@ -7370,7 +7409,7 @@ ppc64_elf_tls_setup (struct bfd_link_info *info, _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr, opt_fd->dynstr_index); if (!bfd_elf_link_record_dynamic_symbol (info, opt_fd)) - return FALSE; + return NULL; } htab->tls_get_addr_fd = (struct ppc_link_hash_entry *) opt_fd; tga = &htab->tls_get_addr->elf; @@ -7439,6 +7478,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) bfd *ibfd; asection *sec; struct ppc_link_hash_table *htab; + unsigned char *toc_ref; int pass; if (info->relocatable || !info->executable) @@ -7448,23 +7488,25 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) if (htab == NULL) return FALSE; - for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) - { - Elf_Internal_Sym *locsyms = NULL; - asection *toc = bfd_get_section_by_name (ibfd, ".toc"); - unsigned char *toc_ref = NULL; - - /* Look at all the sections for this file. Make two passes over - the relocs. On the first pass, mark toc entries involved - with tls relocs, and check that tls relocs involved in - setting up a tls_get_addr call are indeed followed by such a - call. If they are not, exclude them from the optimizations - done on the second pass. */ - for (pass = 0; pass < 2; ++pass) + /* Make two passes over the relocs. On the first pass, mark toc + entries involved with tls relocs, and check that tls relocs + involved in setting up a tls_get_addr call are indeed followed by + such a call. If they are not, we can't do any tls optimization. + On the second pass twiddle tls_mask flags to notify + relocate_section that optimization can be done, and adjust got + and plt refcounts. */ + toc_ref = NULL; + for (pass = 0; pass < 2; ++pass) + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + Elf_Internal_Sym *locsyms = NULL; + asection *toc = bfd_get_section_by_name (ibfd, ".toc"); + for (sec = ibfd->sections; sec != NULL; sec = sec->next) if (sec->has_tls_reloc && !bfd_is_abs_section (sec->output_section)) { Elf_Internal_Rela *relstart, *rel, *relend; + bfd_boolean found_tls_get_addr_arg = 0; /* Read the relocations. */ relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL, @@ -7486,6 +7528,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) bfd_boolean ok_tprel, is_local; long toc_ref_index = 0; int expecting_tls_get_addr = 0; + bfd_boolean ret = FALSE; r_symndx = ELF64_R_SYM (rel->r_info); if (!get_sym_h (&h, &sym, &sym_sec, &tls_mask, &locsyms, @@ -7500,7 +7543,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) && (elf_symtab_hdr (ibfd).contents != (unsigned char *) locsyms)) free (locsyms); - return FALSE; + return ret; } if (h != NULL) @@ -7511,7 +7554,10 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) else if (h->root.type == bfd_link_hash_undefweak) value = 0; else - continue; + { + found_tls_get_addr_arg = 0; + continue; + } } else /* Symbols referenced by TLS relocs must be of type @@ -7538,11 +7584,34 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) } r_type = ELF64_R_TYPE (rel->r_info); + /* If this section has old-style __tls_get_addr calls + without marker relocs, then check that each + __tls_get_addr call reloc is preceded by a reloc + that conceivably belongs to the __tls_get_addr arg + setup insn. If we don't find matching arg setup + relocs, don't do any tls optimization. */ + if (pass == 0 + && sec->has_tls_get_addr_call + && h != NULL + && (h == &htab->tls_get_addr->elf + || h == &htab->tls_get_addr_fd->elf) + && !found_tls_get_addr_arg + && is_branch_reloc (r_type)) + { + info->callbacks->minfo (_("%C __tls_get_addr lost arg, " + "TLS optimization disabled\n"), + ibfd, sec, rel->r_offset); + ret = TRUE; + goto err_free_rel; + } + + found_tls_get_addr_arg = 0; switch (r_type) { case R_PPC64_GOT_TLSLD16: case R_PPC64_GOT_TLSLD16_LO: expecting_tls_get_addr = 1; + found_tls_get_addr_arg = 1; /* Fall thru */ case R_PPC64_GOT_TLSLD16_HI: @@ -7562,6 +7631,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: expecting_tls_get_addr = 1; + found_tls_get_addr_arg = 1; /* Fall thru */ case R_PPC64_GOT_TLSGD16_HI: @@ -7590,11 +7660,14 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) } continue; - case R_PPC64_TOC16: - case R_PPC64_TOC16_LO: - case R_PPC64_TLS: case R_PPC64_TLSGD: case R_PPC64_TLSLD: + found_tls_get_addr_arg = 1; + /* Fall thru */ + + case R_PPC64_TLS: + case R_PPC64_TOC16: + case R_PPC64_TOC16_LO: if (sym_sec == NULL || sym_sec != toc) continue; @@ -7603,18 +7676,17 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) case of R_PPC64_TLS, and after checking for tls_get_addr for the TOC16 relocs. */ if (toc_ref == NULL) - { - toc_ref = bfd_zmalloc (toc->size / 8); - if (toc_ref == NULL) - goto err_free_rel; - } + toc_ref = bfd_zmalloc (toc->output_section->rawsize / 8); + if (toc_ref == NULL) + goto err_free_rel; + if (h != NULL) value = h->root.u.def.value; else value = sym->st_value; value += rel->r_addend; BFD_ASSERT (value < toc->size && value % 8 == 0); - toc_ref_index = value / 8; + toc_ref_index = (value + toc->output_offset) / 8; if (r_type == R_PPC64_TLS || r_type == R_PPC64_TLSGD || r_type == R_PPC64_TLSLD) @@ -7635,7 +7707,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) if (pass == 0 || sec != toc || toc_ref == NULL - || !toc_ref[rel->r_offset / 8]) + || !toc_ref[(rel->r_offset + toc->output_offset) / 8]) continue; if (ok_tprel) { @@ -7650,7 +7722,7 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) if (pass == 0 || sec != toc || toc_ref == NULL - || !toc_ref[rel->r_offset / 8]) + || !toc_ref[(rel->r_offset + toc->output_offset) / 8]) continue; if (rel + 1 < relend && (rel[1].r_info @@ -7702,8 +7774,13 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) rel, ibfd); if (retval == 0) goto err_free_rel; - if (retval > 1 && toc_tls != NULL) - toc_ref[toc_ref_index] = 1; + if (toc_tls != NULL) + { + if ((*toc_tls & (TLS_GD | TLS_LD)) != 0) + found_tls_get_addr_arg = 1; + if (retval > 1) + toc_ref[toc_ref_index] = 1; + } } continue; } @@ -7714,9 +7791,12 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) /* Uh oh, we didn't find the expected call. We could just mark this symbol to exclude it from tls optimization but it's safer to skip - the entire section. */ - sec->has_tls_reloc = 0; - break; + the entire optimization. */ + info->callbacks->minfo (_("%C arg lost __tls_get_addr, " + "TLS optimization disabled\n"), + ibfd, sec, rel->r_offset); + ret = TRUE; + goto err_free_rel; } if (expecting_tls_get_addr && htab->tls_get_addr != NULL) @@ -7802,18 +7882,18 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) free (relstart); } - if (toc_ref != NULL) - free (toc_ref); + if (locsyms != NULL + && (elf_symtab_hdr (ibfd).contents != (unsigned char *) locsyms)) + { + if (!info->keep_memory) + free (locsyms); + else + elf_symtab_hdr (ibfd).contents = (unsigned char *) locsyms; + } + } - if (locsyms != NULL - && (elf_symtab_hdr (ibfd).contents != (unsigned char *) locsyms)) - { - if (!info->keep_memory) - free (locsyms); - else - elf_symtab_hdr (ibfd).contents = (unsigned char *) locsyms; - } - } + if (toc_ref != NULL) + free (toc_ref); return TRUE; } @@ -7830,11 +7910,14 @@ 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) { struct ppc_link_hash_entry *eh; struct adjust_toc_info *toc_inf = (struct adjust_toc_info *) inf; + unsigned long i; if (h->root.type == bfd_link_hash_indirect) return TRUE; @@ -7852,16 +7935,22 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf) if (eh->elf.root.u.def.section == toc_inf->toc) { - unsigned long skip = toc_inf->skip[eh->elf.root.u.def.value >> 3]; - if (skip != (unsigned long) -1) - eh->elf.root.u.def.value -= skip; + if (eh->elf.root.u.def.value > toc_inf->toc->rawsize) + i = toc_inf->toc->rawsize >> 3; else + i = eh->elf.root.u.def.value >> 3; + + if ((toc_inf->skip[i] & (ref_from_discarded | can_optimize)) != 0) { (*_bfd_error_handler) - (_("%s defined in removed toc entry"), eh->elf.root.root.string); - eh->elf.root.u.def.section = &bfd_abs_section; - eh->elf.root.u.def.value = 0; + (_("%s defined on removed toc entry"), eh->elf.root.root.string); + do + ++i; + while ((toc_inf->skip[i] & (ref_from_discarded | can_optimize)) != 0); + eh->elf.root.u.def.value = (bfd_vma) i << 3; } + + eh->elf.root.u.def.value -= toc_inf->skip[i]; eh->adjust_done = 1; } else if (strcmp (eh->elf.root.u.def.section->name, ".toc") == 0) @@ -7878,19 +7967,23 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) { bfd *ibfd; struct adjust_toc_info toc_inf; + struct ppc_link_hash_table *htab = ppc_hash_table (info); + htab->do_toc_opt = 1; toc_inf.global_toc_syms = TRUE; for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) { asection *toc, *sec; Elf_Internal_Shdr *symtab_hdr; Elf_Internal_Sym *local_syms; - struct elf_link_hash_entry **sym_hashes; - Elf_Internal_Rela *relstart, *rel; + Elf_Internal_Rela *relstart, *rel, *toc_relocs; unsigned long *skip, *drop; unsigned char *used; unsigned char *keep, last, some_unused; + if (!is_ppc64_elf (ibfd)) + continue; + toc = bfd_get_section_by_name (ibfd, ".toc"); if (toc == NULL || toc->size == 0 @@ -7898,9 +7991,9 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) || elf_discarded_section (toc)) continue; + toc_relocs = NULL; local_syms = NULL; symtab_hdr = &elf_symtab_hdr (ibfd); - sym_hashes = elf_sym_hashes (ibfd); /* Look at sections dropped from the final link. */ skip = NULL; @@ -7968,18 +8061,101 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) if (skip == NULL) { - skip = bfd_zmalloc (sizeof (*skip) * (toc->size + 7) / 8); + skip = bfd_zmalloc (sizeof (*skip) * (toc->size + 15) / 8); if (skip == NULL) 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. */ + toc_relocs = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL, + info->keep_memory); + if (toc_relocs == NULL) + goto error_ret; + + for (rel = toc_relocs; rel < toc_relocs + 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 (sym_sec == NULL + || elf_discarded_section (sym_sec)) + continue; + + if (!SYMBOL_CALLS_LOCAL (info, h)) + continue; + + if (h != NULL) + { + if (h->type == STT_GNU_IFUNC) + continue; + val = h->root.u.def.value; + } + else + { + if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) + continue; + 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 - toc_relocs) << 2); + } + } + if (skip == NULL) continue; @@ -7994,6 +8170,9 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) && relstart != NULL && elf_section_data (sec)->relocs != relstart) free (relstart); + if (toc_relocs != NULL + && elf_section_data (toc)->relocs != toc_relocs) + free (toc_relocs); if (skip != NULL) free (skip); return FALSE; @@ -8018,7 +8197,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) || (sec->flags & SEC_DEBUGGING) != 0) continue; - relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL, TRUE); + relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL, + info->keep_memory); if (relstart == NULL) goto error_ret; @@ -8071,12 +8251,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; @@ -8084,6 +8289,9 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) used[val >> 3] = 1; } while (repeat); + + if (elf_section_data (sec)->relocs != relstart) + free (relstart); } /* Merge the used and skip arrays. Assume that TOC @@ -8095,13 +8303,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; @@ -8113,6 +8323,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) { bfd_byte *contents, *src; unsigned long off; + Elf_Internal_Sym *sym; + bfd_boolean local_toc_syms = FALSE; /* Shuffle the toc contents, and at the same time convert the skip array from booleans into offsets. */ @@ -8125,52 +8337,20 @@ 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; memcpy (src - off, src, 8); } } + *drop = off; toc->rawsize = toc->size; toc->size = src - contents - off; - if (toc->reloc_count != 0) - { - Elf_Internal_Rela *wrel; - bfd_size_type sz; - - /* Read toc relocs. */ - relstart = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL, - TRUE); - if (relstart == NULL) - goto error_ret; - - /* 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) - { - wrel->r_offset = rel->r_offset - skip[rel->r_offset >> 3]; - wrel->r_info = rel->r_info; - wrel->r_addend = rel->r_addend; - ++wrel; - } - else if (!dec_dynrel_count (rel->r_info, toc, info, - &local_syms, NULL, NULL)) - goto error_ret; - - toc->reloc_count = wrel - relstart; - sz = elf_section_data (toc)->rel_hdr.sh_entsize; - elf_section_data (toc)->rel_hdr.sh_size = toc->reloc_count * sz; - BFD_ASSERT (elf_section_data (toc)->rel_hdr2 == NULL); - } - - /* 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 @@ -8178,7 +8358,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) continue; relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL, - TRUE); + info->keep_memory); if (relstart == NULL) goto error_ret; @@ -8188,7 +8368,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) unsigned long r_symndx; asection *sym_sec; struct elf_link_hash_entry *h; - Elf_Internal_Sym *sym; + bfd_vma val; r_type = ELF64_R_TYPE (rel->r_info); switch (r_type) @@ -8211,41 +8391,92 @@ 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; + + 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 + = 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[rel->r_addend >> 3]; + rel->r_addend -= skip[val >> 3]; + elf_section_data (sec)->relocs = relstart; } + + if (elf_section_data (sec)->relocs != relstart) + free (relstart); } /* We shouldn't have local or global symbols defined in the TOC, but handle them anyway. */ if (local_syms != NULL) - { - Elf_Internal_Sym *sym; + for (sym = local_syms; + sym < local_syms + symtab_hdr->sh_info; + ++sym) + if (sym->st_value != 0 + && bfd_section_from_elf_index (ibfd, sym->st_shndx) == toc) + { + unsigned long i; - for (sym = local_syms; - sym < local_syms + symtab_hdr->sh_info; - ++sym) - if (sym->st_value != 0 - && bfd_section_from_elf_index (ibfd, sym->st_shndx) == toc) - { - if (skip[sym->st_value >> 3] != (unsigned long) -1) - sym->st_value -= skip[sym->st_value >> 3]; - else - { + if (sym->st_value > toc->rawsize) + i = toc->rawsize >> 3; + else + i = sym->st_value >> 3; + + if ((skip[i] & (ref_from_discarded | can_optimize)) != 0) + { + if (local_toc_syms) (*_bfd_error_handler) - (_("%s defined in removed toc entry"), - bfd_elf_sym_name (ibfd, symtab_hdr, sym, - NULL)); - sym->st_value = 0; - sym->st_shndx = SHN_ABS; - } - symtab_hdr->contents = (unsigned char *) local_syms; - } - } + (_("%s defined on removed toc entry"), + bfd_elf_sym_name (ibfd, symtab_hdr, sym, NULL)); + do + ++i; + while ((skip[i] & (ref_from_discarded | can_optimize))); + sym->st_value = (bfd_vma) i << 3; + } + + sym->st_value -= skip[i]; + symtab_hdr->contents = (unsigned char *) local_syms; + } - /* Finally, adjust any global syms defined in the toc. */ + /* Adjust any global syms defined in this toc input section. */ if (toc_inf.global_toc_syms) { toc_inf.toc = toc; @@ -8254,7 +8485,44 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) elf_link_hash_traverse (elf_hash_table (info), adjust_toc_syms, &toc_inf); } + + if (toc->reloc_count != 0) + { + Elf_Internal_Shdr *rel_hdr; + Elf_Internal_Rela *wrel; + bfd_size_type sz; + + /* Remove unused toc relocs, and adjust those we keep. */ + if (toc_relocs == NULL) + toc_relocs = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL, + info->keep_memory); + if (toc_relocs == NULL) + goto error_ret; + + wrel = toc_relocs; + for (rel = toc_relocs; rel < toc_relocs + toc->reloc_count; ++rel) + 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; + wrel->r_addend = rel->r_addend; + ++wrel; + } + else if (!dec_dynrel_count (rel->r_info, toc, info, + &local_syms, NULL, NULL)) + goto error_ret; + + elf_section_data (toc)->relocs = toc_relocs; + toc->reloc_count = wrel - toc_relocs; + rel_hdr = _bfd_elf_single_rel_hdr (toc); + sz = rel_hdr->sh_entsize; + rel_hdr->sh_size = toc->reloc_count * sz; + } } + else if (toc_relocs != NULL + && elf_section_data (toc)->relocs != toc_relocs) + free (toc_relocs); if (local_syms != NULL && symtab_hdr->contents != (unsigned char *) local_syms) @@ -8270,6 +8538,16 @@ ppc64_elf_edit_toc (struct bfd_link_info *info) return TRUE; } +/* Return true iff input section I references the TOC using + instructions limited to +/-32k offsets. */ + +bfd_boolean +ppc64_elf_has_small_toc_reloc (asection *i) +{ + return (is_ppc64_elf (i->owner) + && ppc64_elf_tdata (i->owner)->has_small_toc_reloc); +} + /* Allocate space for one GOT entry. */ static void @@ -8306,6 +8584,26 @@ allocate_got (struct elf_link_hash_entry *h, } } +/* This function merges got entries in the same toc group. */ + +static void +merge_got_entries (struct got_entry **pent) +{ + struct got_entry *ent, *ent2; + + for (ent = *pent; ent != NULL; ent = ent->next) + if (!ent->is_indirect) + for (ent2 = ent->next; ent2 != NULL; ent2 = ent2->next) + if (!ent2->is_indirect + && ent2->addend == ent->addend + && ent2->tls_type == ent->tls_type + && elf_gp (ent2->owner) == elf_gp (ent->owner)) + { + ent2->is_indirect = TRUE; + ent2->got.ent = ent; + } +} + /* Allocate space in .plt, .got and associated reloc sections for dynamic relocs. */ @@ -8416,9 +8714,30 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) gent->tls_type = TLS_TLS | TLS_TPREL; } + /* Remove any list entry that won't generate a word in the GOT before + we call merge_got_entries. Otherwise we risk merging to empty + entries. */ pgent = &h->got.glist; while ((gent = *pgent) != NULL) if (gent->got.refcount > 0) + { + if ((gent->tls_type & TLS_LD) != 0 + && !h->def_dynamic) + { + ppc64_tlsld_got (gent->owner)->got.refcount += 1; + *pgent = gent->next; + } + else + pgent = &gent->next; + } + else + *pgent = gent->next; + + if (!htab->do_multi_toc) + merge_got_entries (&h->got.glist); + + for (gent = h->got.glist; gent != NULL; gent = gent->next) + if (!gent->is_indirect) { /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic, @@ -8432,22 +8751,11 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) return FALSE; } - if ((gent->tls_type & TLS_LD) != 0 - && !h->def_dynamic) - { - ppc64_tlsld_got (gent->owner)->got.refcount += 1; - *pgent = gent->next; - continue; - } - if (!is_ppc64_elf (gent->owner)) abort (); allocate_got (h, info, gent); - pgent = &gent->next; } - else - *pgent = gent->next; if (eh->dyn_relocs == NULL || (!htab->elf.dynamic_sections_created @@ -8587,6 +8895,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, asection *s; bfd_boolean relocs; bfd *ibfd; + struct got_entry *first_tlsld; htab = ppc_hash_table (info); if (htab == NULL) @@ -8722,25 +9031,39 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, sym dynamic relocs. */ elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, info); + first_tlsld = NULL; for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) { + struct got_entry *ent; + if (!is_ppc64_elf (ibfd)) continue; - if (ppc64_tlsld_got (ibfd)->got.refcount > 0) + ent = ppc64_tlsld_got (ibfd); + if (ent->got.refcount > 0) { - s = ppc64_elf_tdata (ibfd)->got; - ppc64_tlsld_got (ibfd)->got.offset = s->size; - ppc64_tlsld_got (ibfd)->owner = ibfd; - s->size += 16; - if (info->shared) + if (!htab->do_multi_toc && first_tlsld != NULL) { - asection *srel = ppc64_elf_tdata (ibfd)->relgot; - srel->size += sizeof (Elf64_External_Rela); + ent->is_indirect = TRUE; + ent->got.ent = first_tlsld; + } + else + { + if (first_tlsld == NULL) + first_tlsld = ent; + s = ppc64_elf_tdata (ibfd)->got; + ent->got.offset = s->size; + ent->owner = ibfd; + s->size += 16; + if (info->shared) + { + asection *srel = ppc64_elf_tdata (ibfd)->relgot; + srel->size += sizeof (Elf64_External_Rela); + } } } else - ppc64_tlsld_got (ibfd)->got.offset = (bfd_vma) -1; + ent->got.offset = (bfd_vma) -1; } /* We now have determined the sizes of the various dynamic sections. @@ -8763,7 +9086,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, /* Strip this section if we don't need it; see the comment below. */ } - else if (CONST_STRNEQ (bfd_get_section_name (dynobj, s), ".rela")) + else if (CONST_STRNEQ (s->name, ".rela")) { if (s->size != 0) { @@ -8928,13 +9251,15 @@ ppc_type_of_stub (asection *input_sec, struct ppc_link_hash_entry *fdh = h; if (h->oh != NULL && h->oh->is_func_descriptor) - fdh = ppc_follow_link (h->oh); + { + fdh = ppc_follow_link (h->oh); + *hash = fdh; + } for (ent = fdh->elf.plt.plist; ent != NULL; ent = ent->next) if (ent->addend == rel->r_addend && ent->plt.offset != (bfd_vma) -1) { - *hash = fdh; *plt_ent = ent; return ppc_stub_plt_call; } @@ -8943,12 +9268,8 @@ ppc_type_of_stub (asection *input_sec, either a defined function descriptor or a defined entry symbol in a regular object file, then it is pointless trying to make any other type of stub. */ - if (!((fdh->elf.root.type == bfd_link_hash_defined - || fdh->elf.root.type == bfd_link_hash_defweak) - && fdh->elf.root.u.def.section->output_section != NULL) - && !((h->elf.root.type == bfd_link_hash_defined - || h->elf.root.type == bfd_link_hash_defweak) - && h->elf.root.u.def.section->output_section != NULL)) + if (!is_static_defined (&fdh->elf) + && !is_static_defined (&h->elf)) return ppc_stub_none; } else if (elf_local_got_ents (input_sec->owner) != NULL) @@ -9135,9 +9456,13 @@ get_relocs (asection *sec, int count) if (relocs == NULL) return NULL; elfsec_data->relocs = relocs; - elfsec_data->rel_hdr.sh_size = (sec->reloc_count - * sizeof (Elf64_External_Rela)); - elfsec_data->rel_hdr.sh_entsize = sizeof (Elf64_External_Rela); + elfsec_data->rela.hdr = bfd_zalloc (sec->owner, + sizeof (Elf_Internal_Shdr)); + if (elfsec_data->rela.hdr == NULL) + return NULL; + elfsec_data->rela.hdr->sh_size = (sec->reloc_count + * sizeof (Elf64_External_Rela)); + elfsec_data->rela.hdr->sh_entsize = sizeof (Elf64_External_Rela); sec->reloc_count = 0; } relocs += sec->reloc_count; @@ -9145,6 +9470,37 @@ get_relocs (asection *sec, int count) return relocs; } +static bfd_vma +get_r2off (struct ppc_link_hash_table *htab, + struct ppc_stub_hash_entry *stub_entry) +{ + bfd_vma r2off = htab->stub_group[stub_entry->target_section->id].toc_off; + + if (r2off == 0) + { + /* Support linking -R objects. Get the toc pointer from the + opd entry. */ + char buf[8]; + asection *opd = stub_entry->h->elf.root.u.def.section; + bfd_vma opd_off = stub_entry->h->elf.root.u.def.value; + + if (strcmp (opd->name, ".opd") != 0 + || opd->reloc_count != 0) + { + (*_bfd_error_handler) (_("cannot find opd entry toc for %s"), + stub_entry->h->elf.root.root.string); + bfd_set_error (bfd_error_bad_value); + return 0; + } + if (!bfd_get_section_contents (opd->owner, opd, buf, opd_off + 8, 8)) + return 0; + r2off = bfd_get_64 (opd->owner, buf); + r2off -= elf_gp (stub_entry->id_sec->output_section->owner); + } + r2off -= htab->stub_group[stub_entry->id_sec->id].toc_off; + return r2off; +} + static bfd_boolean ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) { @@ -9189,10 +9545,13 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) size = 4; if (stub_entry->stub_type == ppc_stub_long_branch_r2off) { - bfd_vma r2off; + bfd_vma r2off = get_r2off (htab, stub_entry); - r2off = (htab->stub_group[stub_entry->target_section->id].toc_off - - htab->stub_group[stub_entry->id_sec->id].toc_off); + if (r2off == 0) + { + htab->stub_error = TRUE; + return FALSE; + } bfd_put_32 (htab->stub_bfd, STD_R2_40R1, loc); loc += 4; size = 12; @@ -9376,10 +9735,14 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) } else { - bfd_vma r2off; + bfd_vma r2off = get_r2off (htab, stub_entry); + + if (r2off == 0) + { + htab->stub_error = TRUE; + return FALSE; + } - r2off = (htab->stub_group[stub_entry->target_section->id].toc_off - - htab->stub_group[stub_entry->id_sec->id].toc_off); bfd_put_32 (htab->stub_bfd, STD_R2_40R1, loc); loc += 4; size = 20; @@ -9426,6 +9789,8 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) these checks could now disappear. */ if (fh->elf.root.type == bfd_link_hash_undefined) fh->elf.root.type = bfd_link_hash_undefweak; + /* Stop undo_symbol_twiddle changing it back to undefined. */ + fh->was_undefined = 0; } /* Now build the stub. */ @@ -9619,8 +9984,12 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) size = 4; if (stub_entry->stub_type == ppc_stub_long_branch_r2off) { - r2off = (htab->stub_group[stub_entry->target_section->id].toc_off - - htab->stub_group[stub_entry->id_sec->id].toc_off); + r2off = get_r2off (htab, stub_entry); + if (r2off == 0) + { + htab->stub_error = TRUE; + return FALSE; + } size = 12; if (PPC_HA (r2off) != 0) size = 16; @@ -9790,7 +10159,7 @@ bfd_boolean ppc64_elf_next_toc_section (struct bfd_link_info *info, asection *isec) { struct ppc_link_hash_table *htab = ppc_hash_table (info); - bfd_vma addr, off; + bfd_vma addr, off, limit; if (htab == NULL) return FALSE; @@ -9806,7 +10175,10 @@ ppc64_elf_next_toc_section (struct bfd_link_info *info, asection *isec) addr = isec->output_offset + isec->output_section->vma; off = addr - htab->toc_curr; - if (off + isec->size > 0x10000) + limit = 0x80008000; + if (ppc64_elf_tdata (isec->owner)->has_small_toc_reloc) + limit = 0x10000; + if (off + isec->size > limit) { addr = (htab->toc_first_sec->output_offset + htab->toc_first_sec->output_section->vma); @@ -9852,26 +10224,6 @@ ppc64_elf_next_toc_section (struct bfd_link_info *info, asection *isec) return TRUE; } -/* This function merges got entries in the same toc group. */ - -static void -merge_got_entries (struct got_entry **pent) -{ - struct got_entry *ent, *ent2; - - for (ent = *pent; ent != NULL; ent = ent->next) - if (!ent->is_indirect) - for (ent2 = ent->next; ent2 != NULL; ent2 = ent2->next) - if (!ent2->is_indirect - && ent2->addend == ent->addend - && ent2->tls_type == ent->tls_type - && elf_gp (ent2->owner) == elf_gp (ent->owner)) - { - ent2->is_indirect = TRUE; - ent2->got.ent = ent; - } -} - /* Called via elf_link_hash_traverse to merge GOT entries for global symbol H. */ @@ -9922,30 +10274,10 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info) htab->multi_toc_needed = htab->toc_curr != elf_gp (info->output_bfd); - /* Merge local got entries within a toc group. */ - for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) - { - struct got_entry **lgot_ents; - struct got_entry **end_lgot_ents; - Elf_Internal_Shdr *symtab_hdr; - bfd_size_type locsymcount; - - if (!is_ppc64_elf (ibfd)) - continue; - - lgot_ents = elf_local_got_ents (ibfd); - if (!lgot_ents) - continue; - - symtab_hdr = &elf_symtab_hdr (ibfd); - locsymcount = symtab_hdr->sh_info; - end_lgot_ents = lgot_ents + locsymcount; - - for (; lgot_ents < end_lgot_ents; ++lgot_ents) - merge_got_entries (lgot_ents); - } + if (!htab->do_multi_toc) + return FALSE; - /* And the same for global sym got entries. */ + /* Merge global sym got entries within a toc group. */ elf_link_hash_traverse (&htab->elf, merge_global_got, info); /* And tlsld_got. */ @@ -10033,23 +10365,22 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info) struct got_entry *ent; for (ent = *lgot_ents; ent != NULL; ent = ent->next) - if (!ent->is_indirect) - { - unsigned int num = 1; - ent->got.offset = s->size; - if ((ent->tls_type & *lgot_masks & TLS_GD) != 0) - num = 2; - s->size += num * 8; - if (info->shared) - srel->size += num * sizeof (Elf64_External_Rela); - else if ((*lgot_masks & PLT_IFUNC) != 0) - { - htab->reliplt->size - += num * sizeof (Elf64_External_Rela); - htab->got_reli_size - += num * sizeof (Elf64_External_Rela); - } - } + { + unsigned int num = 1; + ent->got.offset = s->size; + if ((ent->tls_type & *lgot_masks & TLS_GD) != 0) + num = 2; + s->size += num * 8; + if (info->shared) + srel->size += num * sizeof (Elf64_External_Rela); + else if ((*lgot_masks & PLT_IFUNC) != 0) + { + htab->reliplt->size + += num * sizeof (Elf64_External_Rela); + htab->got_reli_size + += num * sizeof (Elf64_External_Rela); + } + } } } @@ -10113,9 +10444,6 @@ ppc64_elf_finish_multitoc_partition (struct bfd_link_info *info) { struct ppc_link_hash_table *htab = ppc_hash_table (info); - if (htab == NULL) - return; - /* After the second pass, toc_curr tracks the TOC offset used for code sections below in ppc64_elf_next_input_section. */ htab->toc_curr = TOC_BASE_OFF; @@ -10132,10 +10460,10 @@ ppc64_elf_finish_multitoc_partition (struct bfd_link_info *info) static int toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec) { - Elf_Internal_Rela *relstart, *rel; - Elf_Internal_Sym *local_syms; int ret; - struct ppc_link_hash_table *htab; + + /* Mark this section as checked. */ + isec->call_check_done = 1; /* We know none of our code bearing sections will need toc stubs. */ if ((isec->flags & SEC_LINKER_CREATED) != 0) @@ -10147,179 +10475,189 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec) if (isec->output_section == NULL) return 0; - if (isec->reloc_count == 0) - return 0; - - relstart = _bfd_elf_link_read_relocs (isec->owner, isec, NULL, NULL, - info->keep_memory); - if (relstart == NULL) - return -1; - - /* Look for branches to outside of this section. */ - local_syms = NULL; ret = 0; - htab = ppc_hash_table (info); - if (htab == NULL) - return -1; - - for (rel = relstart; rel < relstart + isec->reloc_count; ++rel) + if (isec->reloc_count != 0) { - enum elf_ppc64_reloc_type r_type; - unsigned long r_symndx; - struct elf_link_hash_entry *h; - struct ppc_link_hash_entry *eh; - Elf_Internal_Sym *sym; - asection *sym_sec; - struct _opd_sec_data *opd; - bfd_vma sym_value; - bfd_vma dest; - - r_type = ELF64_R_TYPE (rel->r_info); - if (r_type != R_PPC64_REL24 - && r_type != R_PPC64_REL14 - && r_type != R_PPC64_REL14_BRTAKEN - && r_type != R_PPC64_REL14_BRNTAKEN) - continue; - - r_symndx = ELF64_R_SYM (rel->r_info); - if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms, r_symndx, - isec->owner)) - { - ret = -1; - break; - } - - /* Calls to dynamic lib functions go through a plt call stub - that uses r2. */ - eh = (struct ppc_link_hash_entry *) h; - if (eh != NULL - && (eh->elf.plt.plist != NULL - || (eh->oh != NULL - && ppc_follow_link (eh->oh)->elf.plt.plist != NULL))) - { - ret = 1; - break; - } + Elf_Internal_Rela *relstart, *rel; + Elf_Internal_Sym *local_syms; + struct ppc_link_hash_table *htab; - if (sym_sec == NULL) - /* Ignore other undefined symbols. */ - continue; + relstart = _bfd_elf_link_read_relocs (isec->owner, isec, NULL, NULL, + info->keep_memory); + if (relstart == NULL) + return -1; - /* Assume branches to other sections not included in the link need - stubs too, to cover -R and absolute syms. */ - if (sym_sec->output_section == NULL) - { - ret = 1; - break; - } + /* Look for branches to outside of this section. */ + local_syms = NULL; + htab = ppc_hash_table (info); + if (htab == NULL) + return -1; - if (h == NULL) - sym_value = sym->st_value; - else + for (rel = relstart; rel < relstart + isec->reloc_count; ++rel) { - if (h->root.type != bfd_link_hash_defined - && h->root.type != bfd_link_hash_defweak) - abort (); - sym_value = h->root.u.def.value; - } - sym_value += rel->r_addend; + enum elf_ppc64_reloc_type r_type; + unsigned long r_symndx; + struct elf_link_hash_entry *h; + struct ppc_link_hash_entry *eh; + Elf_Internal_Sym *sym; + asection *sym_sec; + struct _opd_sec_data *opd; + bfd_vma sym_value; + bfd_vma dest; + + r_type = ELF64_R_TYPE (rel->r_info); + if (r_type != R_PPC64_REL24 + && r_type != R_PPC64_REL14 + && r_type != R_PPC64_REL14_BRTAKEN + && r_type != R_PPC64_REL14_BRNTAKEN) + continue; - /* If this branch reloc uses an opd sym, find the code section. */ - opd = get_opd_info (sym_sec); - if (opd != NULL) - { - if (h == NULL && opd->adjust != NULL) + r_symndx = ELF64_R_SYM (rel->r_info); + if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms, r_symndx, + isec->owner)) { - long adjust; + ret = -1; + break; + } - adjust = opd->adjust[sym->st_value / 8]; - if (adjust == -1) - /* Assume deleted functions won't ever be called. */ - continue; - sym_value += adjust; + /* Calls to dynamic lib functions go through a plt call stub + that uses r2. */ + eh = (struct ppc_link_hash_entry *) h; + if (eh != NULL + && (eh->elf.plt.plist != NULL + || (eh->oh != NULL + && ppc_follow_link (eh->oh)->elf.plt.plist != NULL))) + { + ret = 1; + break; } - dest = opd_entry_value (sym_sec, sym_value, &sym_sec, NULL); - if (dest == (bfd_vma) -1) + if (sym_sec == NULL) + /* Ignore other undefined symbols. */ continue; - } - else - dest = (sym_value - + sym_sec->output_offset - + sym_sec->output_section->vma); - /* Ignore branch to self. */ - if (sym_sec == isec) - continue; + /* Assume branches to other sections not included in the + link need stubs too, to cover -R and absolute syms. */ + if (sym_sec->output_section == NULL) + { + ret = 1; + break; + } - /* If the called function uses the toc, we need a stub. */ - if (sym_sec->has_toc_reloc - || sym_sec->makes_toc_func_call) - { - ret = 1; - break; - } + if (h == NULL) + sym_value = sym->st_value; + else + { + if (h->root.type != bfd_link_hash_defined + && h->root.type != bfd_link_hash_defweak) + abort (); + sym_value = h->root.u.def.value; + } + sym_value += rel->r_addend; - /* Assume any branch that needs a long branch stub might in fact - need a plt_branch stub. A plt_branch stub uses r2. */ - else if (dest - (isec->output_offset - + isec->output_section->vma - + rel->r_offset) + (1 << 25) >= (2 << 25)) - { - ret = 1; - break; - } + /* If this branch reloc uses an opd sym, find the code section. */ + opd = get_opd_info (sym_sec); + if (opd != NULL) + { + if (h == NULL && opd->adjust != NULL) + { + long adjust; - /* If calling back to a section in the process of being tested, we - can't say for sure that no toc adjusting stubs are needed, so - don't return zero. */ - else if (sym_sec->call_check_in_progress) - ret = 2; + adjust = opd->adjust[sym->st_value / 8]; + if (adjust == -1) + /* Assume deleted functions won't ever be called. */ + continue; + sym_value += adjust; + } - /* Branches to another section that itself doesn't have any TOC - references are OK. Recursively call ourselves to check. */ - else if (sym_sec->id <= htab->top_id - && htab->stub_group[sym_sec->id].toc_off == 0) - { - int recur; + dest = opd_entry_value (sym_sec, sym_value, &sym_sec, NULL); + if (dest == (bfd_vma) -1) + continue; + } + else + dest = (sym_value + + sym_sec->output_offset + + sym_sec->output_section->vma); - /* Mark current section as indeterminate, so that other - sections that call back to current won't be marked as - known. */ - isec->call_check_in_progress = 1; - recur = toc_adjusting_stub_needed (info, sym_sec); - isec->call_check_in_progress = 0; + /* Ignore branch to self. */ + if (sym_sec == isec) + continue; - if (recur < 0) + /* If the called function uses the toc, we need a stub. */ + if (sym_sec->has_toc_reloc + || sym_sec->makes_toc_func_call) { - /* An error. Exit. */ - ret = -1; + ret = 1; break; } - else if (recur <= 1) + + /* Assume any branch that needs a long branch stub might in fact + need a plt_branch stub. A plt_branch stub uses r2. */ + else if (dest - (isec->output_offset + + isec->output_section->vma + + rel->r_offset) + (1 << 25) >= (2 << 25)) + { + ret = 1; + break; + } + + /* If calling back to a section in the process of being + tested, we can't say for sure that no toc adjusting stubs + are needed, so don't return zero. */ + else if (sym_sec->call_check_in_progress) + ret = 2; + + /* Branches to another section that itself doesn't have any TOC + references are OK. Recursively call ourselves to check. */ + else if (!sym_sec->call_check_done) { - /* Known result. Mark as checked and set section flag. */ - htab->stub_group[sym_sec->id].toc_off = 1; + int recur; + + /* Mark current section as indeterminate, so that other + sections that call back to current won't be marked as + known. */ + isec->call_check_in_progress = 1; + recur = toc_adjusting_stub_needed (info, sym_sec); + isec->call_check_in_progress = 0; + if (recur != 0) { - sym_sec->makes_toc_func_call = 1; - ret = 1; - break; + ret = recur; + if (recur != 2) + break; } } - else - { - /* Unknown result. Continue checking. */ - ret = 2; - } + } + + if (local_syms != NULL + && (elf_symtab_hdr (isec->owner).contents + != (unsigned char *) local_syms)) + free (local_syms); + if (elf_section_data (isec)->relocs != relstart) + free (relstart); + } + + if ((ret & 1) == 0 + && isec->map_head.s != NULL + && (strcmp (isec->output_section->name, ".init") == 0 + || strcmp (isec->output_section->name, ".fini") == 0)) + { + if (isec->map_head.s->has_toc_reloc + || isec->map_head.s->makes_toc_func_call) + ret = 1; + else if (!isec->map_head.s->call_check_done) + { + int recur; + isec->call_check_in_progress = 1; + recur = toc_adjusting_stub_needed (info, isec->map_head.s); + isec->call_check_in_progress = 0; + if (recur != 0) + ret = recur; } } - if (local_syms != NULL - && (elf_symtab_hdr (isec->owner).contents != (unsigned char *) local_syms)) - free (local_syms); - if (elf_section_data (isec)->relocs != relstart) - free (relstart); + if (ret == 1) + isec->makes_toc_func_call = 1; return ret; } @@ -10365,23 +10703,78 @@ ppc64_elf_next_input_section (struct bfd_link_info *info, asection *isec) if (elf_gp (isec->owner) != 0) htab->toc_curr = elf_gp (isec->owner); } - else if (htab->stub_group[isec->id].toc_off == 0) + else { - int ret = toc_adjusting_stub_needed (info, isec); - if (ret < 0) + if (!isec->call_check_done + && toc_adjusting_stub_needed (info, isec) < 0) return FALSE; - else - isec->makes_toc_func_call = ret & 1; + /* If we make a local call from this section, ie. a branch + without a following nop, then we have no place to put a + toc restoring insn. We must use the same toc group as + the callee. + Testing makes_toc_func_call actually tests for *any* + calls to functions that need a good toc pointer. A more + precise test would be better, as this one will set + incorrect values for pasted .init/.fini fragments. + (Fixed later in check_pasted_section.) */ + if (isec->makes_toc_func_call + && elf_gp (isec->owner) != 0) + htab->toc_curr = elf_gp (isec->owner); } } /* Functions that don't use the TOC can belong in any TOC group. - Use the last TOC base. This happens to make _init and _fini - pasting work. */ + Use the last TOC base. */ htab->stub_group[isec->id].toc_off = htab->toc_curr; return TRUE; } +/* Check that all .init and .fini sections use the same toc, if they + have toc relocs. */ + +static bfd_boolean +check_pasted_section (struct bfd_link_info *info, const char *name) +{ + asection *o = bfd_get_section_by_name (info->output_bfd, name); + + if (o != NULL) + { + struct ppc_link_hash_table *htab = ppc_hash_table (info); + bfd_vma toc_off = 0; + asection *i; + + for (i = o->map_head.s; i != NULL; i = i->map_head.s) + if (i->has_toc_reloc) + { + if (toc_off == 0) + toc_off = htab->stub_group[i->id].toc_off; + else if (toc_off != htab->stub_group[i->id].toc_off) + return FALSE; + } + + if (toc_off == 0) + for (i = o->map_head.s; i != NULL; i = i->map_head.s) + if (i->makes_toc_func_call) + { + toc_off = htab->stub_group[i->id].toc_off; + break; + } + + /* Make sure the whole pasted function uses the same toc offset. */ + if (toc_off != 0) + for (i = o->map_head.s; i != NULL; i = i->map_head.s) + htab->stub_group[i->id].toc_off = toc_off; + } + return TRUE; +} + +bfd_boolean +ppc64_elf_check_init_fini (struct bfd_link_info *info) +{ + return (check_pasted_section (info, ".init") + & check_pasted_section (info, ".fini")); +} + /* See whether we can group stub sections together. Grouping stub sections may result in fewer stubs. More importantly, we need to put all .init* and .fini* stubs at the beginning of the .init or @@ -10430,7 +10823,8 @@ group_sections (struct ppc_link_hash_table *htab, curr = tail; total = tail->size; - big_sec = total > (ppc64_elf_section_data (tail)->has_14bit_branch + big_sec = total > (ppc64_elf_section_data (tail) != NULL + && ppc64_elf_section_data (tail)->has_14bit_branch ? stub14_group_size : stub_group_size); if (big_sec && !suppress_size_errors) (*_bfd_error_handler) (_("%B section %A exceeds stub group size"), @@ -10439,7 +10833,8 @@ group_sections (struct ppc_link_hash_table *htab, while ((prev = PREV_SEC (curr)) != NULL && ((total += curr->output_offset - prev->output_offset) - < (ppc64_elf_section_data (prev)->has_14bit_branch + < (ppc64_elf_section_data (prev) != NULL + && ppc64_elf_section_data (prev)->has_14bit_branch ? stub14_group_size : stub_group_size)) && htab->stub_group[prev->id].toc_off == curr_toc) curr = prev; @@ -10472,7 +10867,8 @@ group_sections (struct ppc_link_hash_table *htab, total = 0; while (prev != NULL && ((total += tail->output_offset - prev->output_offset) - < (ppc64_elf_section_data (prev)->has_14bit_branch + < (ppc64_elf_section_data (prev) != NULL + && ppc64_elf_section_data (prev)->has_14bit_branch ? stub14_group_size : stub_group_size)) && htab->stub_group[prev->id].toc_off == curr_toc) { @@ -11151,6 +11547,63 @@ ppc64_elf_action_discarded (asection *sec) return _bfd_elf_default_action_discarded (sec); } +/* 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,x,imm. If so, set *REG to x and return a pointer to + the high-part reloc. */ + +static const Elf_Internal_Rela * +ha_reloc_match (const Elf_Internal_Rela *relocs, + const Elf_Internal_Rela *rel, + unsigned int *reg, + bfd_boolean match_addend, + const bfd *input_bfd, + const bfd_byte *contents) +{ + enum elf_ppc64_reloc_type r_type, r_type_ha; + bfd_vma r_info_ha, r_addend; + + r_type = ELF64_R_TYPE (rel->r_info); + switch (r_type) + { + case R_PPC64_GOT_TLSLD16_LO: + case R_PPC64_GOT_TLSGD16_LO: + case R_PPC64_GOT_TPREL16_LO_DS: + case R_PPC64_GOT_DTPREL16_LO_DS: + case R_PPC64_GOT16_LO: + case R_PPC64_TOC16_LO: + r_type_ha = r_type + 2; + break; + case R_PPC64_GOT16_LO_DS: + r_type_ha = R_PPC64_GOT16_HA; + break; + case R_PPC64_TOC16_LO_DS: + r_type_ha = R_PPC64_TOC16_HA; + break; + default: + abort (); + } + r_info_ha = ELF64_R_INFO (ELF64_R_SYM (rel->r_info), r_type_ha); + r_addend = rel->r_addend; + + while (--rel >= relocs) + if (rel->r_info == r_info_ha + && (!match_addend + || rel->r_addend == r_addend)) + { + const bfd_byte *p = contents + (rel->r_offset & ~3); + unsigned int insn = bfd_get_32 (input_bfd, p); + if ((insn & (0x3f << 26)) == (15u << 26) /* addis rt,x,imm */ + && (insn & (0x1f << 21)) == (*reg << 21)) + { + *reg = (insn >> 16) & 0x1f; + return rel; + } + break; + } + return NULL; +} + /* The RELOCATE_SECTION function is called by the ELF backend linker to handle the relocations for a section. @@ -11198,7 +11651,9 @@ ppc64_elf_relocate_section (bfd *output_bfd, Elf_Internal_Rela outrel; bfd_byte *loc; struct got_entry **local_got_ents; + unsigned char *ha_opt; bfd_vma TOCstart; + bfd_boolean no_ha_opt; bfd_boolean ret = TRUE; bfd_boolean is_opd; /* Disabled until we sort out how ld should choose 'y' vs 'at'. */ @@ -11224,6 +11679,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, symtab_hdr = &elf_symtab_hdr (input_bfd); sym_hashes = elf_sym_hashes (input_bfd); is_opd = ppc64_elf_section_data (input_section)->sec_type == sec_opd; + ha_opt = NULL; + no_ha_opt = FALSE; rel = relocs; relend = relocs + input_section->reloc_count; @@ -11245,7 +11702,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, bfd_vma relocation; bfd_boolean unresolved_reloc; bfd_boolean warned; - unsigned long insn, mask; + unsigned int insn; + unsigned int mask; struct ppc_stub_hash_entry *stub_entry; bfd_vma max_br_offset; bfd_vma from; @@ -11312,16 +11770,10 @@ ppc64_elf_relocate_section (bfd *output_bfd, h = (struct ppc_link_hash_entry *) h_elf; if (sec != NULL && elf_discarded_section (sec)) - { - /* For relocs against symbols from removed linkonce sections, - or sections discarded by a linker script, we just want the - section contents zeroed. Avoid any special processing. */ - _bfd_clear_contents (ppc64_elf_howto_table[r_type], input_bfd, - contents + rel->r_offset); - rel->r_info = 0; - rel->r_addend = 0; - continue; - } + RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section, + rel, relend, + ppc64_elf_howto_table[r_type], + contents); if (info->relocatable) continue; @@ -11361,7 +11813,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, /* Check that tls relocs are used with tls syms, and non-tls relocs are used with non-tls syms. */ - if (r_symndx != 0 + if (r_symndx != STN_UNDEF && r_type != R_PPC64_NONE && (h == NULL || h->elf.root.type == bfd_link_hash_defined @@ -11407,6 +11859,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: @@ -11451,6 +11913,18 @@ ppc64_elf_relocate_section (bfd *output_bfd, } break; + case R_PPC64_GOT_TPREL16_HI: + case R_PPC64_GOT_TPREL16_HA: + if (tls_mask != 0 + && (tls_mask & TLS_TPREL) == 0) + { + rel->r_offset -= d_offset; + bfd_put_32 (output_bfd, NOP, contents + rel->r_offset); + r_type = R_PPC64_NONE; + rel->r_info = ELF64_R_INFO (r_symndx, r_type); + } + break; + case R_PPC64_GOT_TPREL16_DS: case R_PPC64_GOT_TPREL16_LO_DS: if (tls_mask != 0 @@ -11520,8 +11994,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, + R_PPC64_GOT_TPREL16_DS); else { - bfd_put_32 (output_bfd, NOP, contents + rel->r_offset); rel->r_offset -= d_offset; + bfd_put_32 (output_bfd, NOP, contents + rel->r_offset); r_type = R_PPC64_NONE; } rel->r_info = ELF64_R_INFO (r_symndx, r_type); @@ -11588,9 +12062,9 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (local_sections[r_symndx] == sec) break; if (r_symndx >= symtab_hdr->sh_info) - r_symndx = 0; + r_symndx = STN_UNDEF; rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET; - if (r_symndx != 0) + if (r_symndx != STN_UNDEF) rel->r_addend -= (local_syms[r_symndx].st_value + sec->output_offset + sec->output_section->vma); @@ -11696,9 +12170,9 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (local_sections[r_symndx] == sec) break; if (r_symndx >= symtab_hdr->sh_info) - r_symndx = 0; + r_symndx = STN_UNDEF; rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET; - if (r_symndx != 0) + if (r_symndx != STN_UNDEF) rel->r_addend -= (local_syms[r_symndx].st_value + sec->output_offset + sec->output_section->vma); @@ -11796,23 +12270,13 @@ ppc64_elf_relocate_section (bfd *output_bfd, linkage stubs needs to be followed by a nop, as the nop will be replaced with an instruction to restore the TOC base pointer. */ - stub_entry = NULL; fdh = h; if (h != NULL && h->oh != NULL && h->oh->is_func_descriptor) fdh = ppc_follow_link (h->oh); - if (((fdh != NULL - && fdh->elf.plt.plist != NULL) - || (sec != NULL - && sec->output_section != NULL - && sec->id <= htab->top_id - && (htab->stub_group[sec->id].toc_off - != htab->stub_group[input_section->id].toc_off)) - || (h == NULL - && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)) - && (stub_entry = ppc_get_stub_entry (input_section, sec, fdh, - rel, htab)) != NULL + stub_entry = ppc_get_stub_entry (input_section, sec, fdh, rel, htab); + if (stub_entry != NULL && (stub_entry->stub_type == ppc_stub_plt_call || stub_entry->stub_type == ppc_stub_plt_branch_r2off || stub_entry->stub_type == ppc_stub_long_branch_r2off)) @@ -11897,7 +12361,9 @@ ppc64_elf_relocate_section (bfd *output_bfd, unresolved_reloc = FALSE; } - if (stub_entry == NULL + if ((stub_entry == NULL + || stub_entry->stub_type == ppc_stub_long_branch + || stub_entry->stub_type == ppc_stub_plt_branch) && get_opd_info (sec) != NULL) { /* The branch destination is the value of the opd entry. */ @@ -11918,13 +12384,15 @@ ppc64_elf_relocate_section (bfd *output_bfd, + input_section->output_offset + input_section->output_section->vma); - if (stub_entry == NULL - && (relocation + addend - from + max_br_offset - >= 2 * max_br_offset) - && r_type != R_PPC64_ADDR14_BRTAKEN - && r_type != R_PPC64_ADDR14_BRNTAKEN) - stub_entry = ppc_get_stub_entry (input_section, sec, h, rel, - htab); + if (stub_entry != NULL + && (stub_entry->stub_type == ppc_stub_long_branch + || stub_entry->stub_type == ppc_stub_plt_branch) + && (r_type == R_PPC64_ADDR14_BRTAKEN + || r_type == R_PPC64_ADDR14_BRNTAKEN + || (relocation + addend - from + max_br_offset + < 2 * max_br_offset))) + /* Don't use the stub if this branch is in range. */ + stub_entry = NULL; if (stub_entry != NULL) { @@ -12058,7 +12526,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, &h->elf) || (info->shared - && SYMBOL_REFERENCES_LOCAL (info, &h->elf))) + && SYMBOL_CALLS_LOCAL (info, &h->elf))) /* This is actually a static link, or it is a -Bsymbolic link and the symbol is defined locally, or the symbol was forced to be local @@ -12246,7 +12714,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_TOC: /* Relocation value is TOC base. */ relocation = TOCstart; - if (r_symndx == 0) + if (r_symndx == STN_UNDEF) relocation += htab->stub_group[input_section->id].toc_off; else if (unresolved_reloc) ; @@ -12435,7 +12903,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (skip) memset (&outrel, 0, sizeof outrel); - else if (!SYMBOL_REFERENCES_LOCAL (info, &h->elf) + else if (!SYMBOL_CALLS_LOCAL (info, &h->elf) && !is_opd && r_type != R_PPC64_TOC) outrel.r_info = ELF64_R_INFO (h->elf.dynindx, r_type); @@ -12496,7 +12964,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, sym_name); ret = FALSE; } - else if (r_symndx == 0 || bfd_is_abs_section (sec)) + else if (r_symndx == STN_UNDEF || bfd_is_abs_section (sec)) ; else if (sec == NULL || sec->owner == NULL) { @@ -12603,6 +13071,100 @@ ppc64_elf_relocate_section (bfd *output_bfd, continue; } + /* Multi-instruction sequences that access the TOC can be + optimized, eg. addis ra,r2,0; addi rb,ra,x; + to nop; addi rb,r2,x; */ + switch (r_type) + { + default: + break; + + case R_PPC64_GOT_TLSLD16_HI: + case R_PPC64_GOT_TLSGD16_HI: + case R_PPC64_GOT_TPREL16_HI: + case R_PPC64_GOT_DTPREL16_HI: + case R_PPC64_GOT16_HI: + case R_PPC64_TOC16_HI: + /* These relocs would only be useful if building up an + offset to later add to r2, perhaps in an indexed + addressing mode instruction. Don't try to optimize. + Unfortunately, the possibility of someone building up an + offset like this or even with the HA relocs, means that + we need to check the high insn when optimizing the low + insn. */ + break; + + case R_PPC64_GOT_TLSLD16_HA: + case R_PPC64_GOT_TLSGD16_HA: + case R_PPC64_GOT_TPREL16_HA: + case R_PPC64_GOT_DTPREL16_HA: + case R_PPC64_GOT16_HA: + case R_PPC64_TOC16_HA: + /* nop is done later. */ + break; + + case R_PPC64_GOT_TLSLD16_LO: + case R_PPC64_GOT_TLSGD16_LO: + case R_PPC64_GOT_TPREL16_LO_DS: + case R_PPC64_GOT_DTPREL16_LO_DS: + case R_PPC64_GOT16_LO: + case R_PPC64_GOT16_LO_DS: + case R_PPC64_TOC16_LO: + case R_PPC64_TOC16_LO_DS: + if (htab->do_toc_opt && relocation + addend + 0x8000 < 0x10000) + { + bfd_byte *p = contents + (rel->r_offset & ~3); + insn = bfd_get_32 (input_bfd, p); + if ((insn & (0x3f << 26)) == 14u << 26 /* addi */ + || (insn & (0x3f << 26)) == 32u << 26 /* lwz */ + || (insn & (0x3f << 26)) == 34u << 26 /* lbz */ + || (insn & (0x3f << 26)) == 36u << 26 /* stw */ + || (insn & (0x3f << 26)) == 38u << 26 /* stb */ + || (insn & (0x3f << 26)) == 40u << 26 /* lhz */ + || (insn & (0x3f << 26)) == 42u << 26 /* lha */ + || (insn & (0x3f << 26)) == 44u << 26 /* sth */ + || (insn & (0x3f << 26)) == 46u << 26 /* lmw */ + || (insn & (0x3f << 26)) == 47u << 26 /* stmw */ + || (insn & (0x3f << 26)) == 48u << 26 /* lfs */ + || (insn & (0x3f << 26)) == 50u << 26 /* lfd */ + || (insn & (0x3f << 26)) == 52u << 26 /* stfs */ + || (insn & (0x3f << 26)) == 54u << 26 /* stfd */ + || ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */ + && (insn & 3) != 1) + || ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */ + && ((insn & 3) == 0 || (insn & 3) == 3))) + { + unsigned int reg = (insn >> 16) & 0x1f; + const Elf_Internal_Rela *ha; + bfd_boolean match_addend; + + match_addend = (sym != NULL + && ELF_ST_TYPE (sym->st_info) == STT_SECTION); + ha = ha_reloc_match (relocs, rel, ®, match_addend, + input_bfd, contents); + if (ha != NULL) + { + insn &= ~(0x1f << 16); + insn |= reg << 16; + bfd_put_32 (input_bfd, insn, p); + if (ha_opt == NULL) + { + ha_opt = bfd_zmalloc (input_section->reloc_count); + if (ha_opt == NULL) + return FALSE; + } + ha_opt[ha - relocs] = 1; + } + else + /* If we don't find a matching high part insn, + something is fishy. Refuse to nop any high + part insn in this section. */ + no_ha_opt = TRUE; + } + } + break; + } + /* Do any further special processing. */ switch (r_type) { @@ -12677,8 +13239,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (((relocation + addend) & mask) != 0) { (*_bfd_error_handler) - (_("%B: error: relocation %s not a multiple of %d"), - input_bfd, + (_("%B(%A+0x%lx): error: %s not a multiple of %u"), + input_bfd, input_section, (long) rel->r_offset, ppc64_elf_howto_table[r_type]->name, mask + 1); bfd_set_error (bfd_error_bad_value); @@ -12755,6 +13317,23 @@ ppc64_elf_relocate_section (bfd *output_bfd, } } + if (ha_opt != NULL) + { + if (!no_ha_opt) + { + unsigned char *opt = ha_opt; + rel = relocs; + relend = relocs + input_section->reloc_count; + for (; rel < relend; opt++, rel++) + if (*opt != 0) + { + bfd_byte *p = contents + (rel->r_offset & ~3); + bfd_put_32 (input_bfd, NOP, p); + } + } + free (ha_opt); + } + /* If we're emitting relocations, then shortly after this function returns, reloc offsets and addends for this section will be adjusted. Worse, reloc symbol indices will be for the output @@ -13038,7 +13617,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd, && htab->brlt->reloc_count != 0 && !_bfd_elf_link_output_relocs (output_bfd, htab->brlt, - &elf_section_data (htab->brlt)->rel_hdr, + elf_section_data (htab->brlt)->rela.hdr, elf_section_data (htab->brlt)->relocs, NULL)) return FALSE; @@ -13047,7 +13626,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd, && htab->glink->reloc_count != 0 && !_bfd_elf_link_output_relocs (output_bfd, htab->glink, - &elf_section_data (htab->glink)->rel_hdr, + elf_section_data (htab->glink)->rela.hdr, elf_section_data (htab->glink)->relocs, NULL)) return FALSE;