X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=bfd%2Felf64-ppc.c;h=2cdb0a265a14bb0f44eb1ae4eee7b9d166daafc6;hb=d4730f921aed32ae4f01e10b8dc399f09b64435b;hp=c3b211528fe3c3a17429b43ecb4f535dab637104;hpb=983bddc8525cca872c7a33492c1003e2ab744631;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index c3b211528f..2cdb0a265a 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -1,9 +1,9 @@ /* PowerPC64-specific support for 64-bit ELF. Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, - 2009 Free Software Foundation, Inc. + 2009, 2010 Free Software Foundation, Inc. Written by Linus Nordberg, Swox AB , based on elf32-ppc.c by Ian Lance Taylor. - Largely rewritten by Alan Modra + Largely rewritten by Alan Modra. This file is part of BFD, the Binary File Descriptor library. @@ -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 @@ -112,6 +113,7 @@ static bfd_vma opd_entry_value #define elf_backend_finish_dynamic_sections ppc64_elf_finish_dynamic_sections #define elf_backend_link_output_symbol_hook ppc64_elf_output_symbol_hook #define elf_backend_special_sections ppc64_elf_special_sections +#define elf_backend_post_process_headers _bfd_elf_set_osabi /* The name of the dynamic interpreter. This is put in the .interp section. */ @@ -1873,6 +1875,95 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ + HOWTO (R_PPC64_JMP_IREL, /* type */ + 0, /* rightshift */ + 0, /* size (0=byte, 1=short, 2=long, 4=64 bits) */ + 0, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + ppc64_elf_unhandled_reloc, /* special_function */ + "R_PPC64_JMP_IREL", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_PPC64_IRELATIVE, /* type */ + 0, /* rightshift */ + 4, /* size (0=byte, 1=short, 2=long, 4=64 bits) */ + 64, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_PPC64_IRELATIVE", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + ONES (64), /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* A 16 bit relative relocation. */ + HOWTO (R_PPC64_REL16, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_PPC64_REL16", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* A 16 bit relative relocation without overflow. */ + HOWTO (R_PPC64_REL16_LO, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_PPC64_REL16_LO", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* The high order 16 bits of a relative address. */ + HOWTO (R_PPC64_REL16_HI, /* type */ + 16, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_PPC64_REL16_HI", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* The high order 16 bits of a relative address, plus 1 if the contents of + the low 16 bits, treated as a signed number, is negative. */ + HOWTO (R_PPC64_REL16_HA, /* type */ + 16, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + ppc64_elf_ha_reloc, /* special_function */ + "R_PPC64_REL16_HA", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + /* GNU extension to record C++ vtable hierarchy. */ HOWTO (R_PPC64_GNU_VTINHERIT, /* type */ 0, /* rightshift */ @@ -2141,6 +2232,14 @@ ppc64_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, break; case BFD_RELOC_PPC64_DTPREL16_HIGHESTA: r = R_PPC64_DTPREL16_HIGHESTA; break; + case BFD_RELOC_16_PCREL: r = R_PPC64_REL16; + break; + case BFD_RELOC_LO16_PCREL: r = R_PPC64_REL16_LO; + break; + case BFD_RELOC_HI16_PCREL: r = R_PPC64_REL16_HI; + break; + case BFD_RELOC_HI16_S_PCREL: r = R_PPC64_REL16_HA; + break; case BFD_RELOC_VTABLE_INHERIT: r = R_PPC64_GNU_VTINHERIT; break; case BFD_RELOC_VTABLE_ENTRY: r = R_PPC64_GNU_VTENTRY; @@ -2428,6 +2527,54 @@ ppc64_elf_unhandled_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, return bfd_reloc_dangerous; } +/* Track GOT entries needed for a given symbol. We might need more + than one got entry per symbol. */ +struct got_entry +{ + struct got_entry *next; + + /* The symbol addend that we'll be placing in the GOT. */ + bfd_vma addend; + + /* Unlike other ELF targets, we use separate GOT entries for the same + symbol referenced from different input files. This is to support + automatic multiple TOC/GOT sections, where the TOC base can vary + from one input file to another. After partitioning into TOC groups + we merge entries within the group. + + Point to the BFD owning this GOT entry. */ + bfd *owner; + + /* Zero for non-tls entries, or TLS_TLS and one of TLS_GD, TLS_LD, + TLS_TPREL or TLS_DTPREL for tls entries. */ + unsigned char tls_type; + + /* Non-zero if got.ent points to real entry. */ + unsigned char is_indirect; + + /* Reference count until size_dynamic_sections, GOT offset thereafter. */ + union + { + bfd_signed_vma refcount; + bfd_vma offset; + struct got_entry *ent; + } got; +}; + +/* The same for PLT. */ +struct plt_entry +{ + struct plt_entry *next; + + bfd_vma addend; + + union + { + bfd_signed_vma refcount; + bfd_vma offset; + } plt; +}; + struct ppc64_elf_obj_tdata { struct elf_obj_tdata elf; @@ -2440,15 +2587,16 @@ struct ppc64_elf_obj_tdata on removed .opd entries to this section so that the sym is removed. */ asection *deleted_section; - /* TLS local dynamic got entry handling. Suppose for multiple GOT + /* TLS local dynamic got entry handling. Support for multiple GOT sections means we potentially need one of these for each input bfd. */ - union { - bfd_signed_vma refcount; - bfd_vma offset; - } tlsld_got; + struct got_entry tlsld_got; /* 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) \ @@ -2459,7 +2607,7 @@ struct ppc64_elf_obj_tdata #define is_ppc64_elf(bfd) \ (bfd_get_flavour (bfd) == bfd_target_elf_flavour \ - && elf_object_id (bfd) == PPC64_ELF_TDATA) + && elf_object_id (bfd) == PPC64_ELF_DATA) /* Override the generic function because we store some extras. */ @@ -2467,7 +2615,7 @@ static bfd_boolean ppc64_elf_mkobject (bfd *abfd) { return bfd_elf_allocate_object (abfd, sizeof (struct ppc64_elf_obj_tdata), - PPC64_ELF_TDATA); + PPC64_ELF_DATA); } /* Fix bad default arch selected for a 64 bit input bfd when the @@ -2504,7 +2652,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; @@ -3116,7 +3264,11 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd, p = relplt->relocation; for (i = 0; i < plt_count; i++, p++) - size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt"); + { + size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt"); + if (p->addend != 0) + size += sizeof ("+0x") - 1 + 16; + } } } @@ -3236,6 +3388,13 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd, len = strlen ((*p->sym_ptr_ptr)->name); memcpy (names, (*p->sym_ptr_ptr)->name, len); names += len; + if (p->addend != 0) + { + memcpy (names, "+0x", sizeof ("+0x") - 1); + names += sizeof ("+0x") - 1; + bfd_sprintf_vma (abfd, names, p->addend); + names += strlen (names); + } memcpy (names, "@plt", sizeof ("@plt")); names += sizeof ("@plt"); s++; @@ -3344,50 +3503,6 @@ struct ppc_dyn_relocs bfd_size_type pc_count; }; -/* Track GOT entries needed for a given symbol. We might need more - than one got entry per symbol. */ -struct got_entry -{ - struct got_entry *next; - - /* The symbol addend that we'll be placing in the GOT. */ - bfd_vma addend; - - /* Unlike other ELF targets, we use separate GOT entries for the same - symbol referenced from different input files. This is to support - automatic multiple TOC/GOT sections, where the TOC base can vary - from one input file to another. FIXME: After group_sections we - ought to merge entries within the group. - - Point to the BFD owning this GOT entry. */ - bfd *owner; - - /* Zero for non-tls entries, or TLS_TLS and one of TLS_GD, TLS_LD, - TLS_TPREL or TLS_DTPREL for tls entries. */ - char tls_type; - - /* Reference count until size_dynamic_sections, GOT offset thereafter. */ - union - { - bfd_signed_vma refcount; - bfd_vma offset; - } got; -}; - -/* The same for PLT. */ -struct plt_entry -{ - struct plt_entry *next; - - bfd_vma addend; - - union - { - bfd_signed_vma refcount; - bfd_vma offset; - } plt; -}; - /* Of those relocs that might be copied as dynamic relocs, this function selects those that must be copied when linking a shared library, even when the symbol is local. */ @@ -3518,6 +3633,7 @@ struct ppc_stub_hash_entry { /* The symbol table entry, if any, that this was derived from. */ struct ppc_link_hash_entry *h; + struct plt_entry *plt_ent; /* And the reloc addend that this was derived from. */ bfd_vma addend; @@ -3586,7 +3702,8 @@ struct ppc_link_hash_entry #define TLS_TLS 16 /* Any TLS reloc. */ #define TLS_EXPLICIT 32 /* Marks TOC section TLS relocs. */ #define TLS_TPRELGD 64 /* TPREL reloc resulting from GD->IE. */ - char tls_mask; +#define PLT_IFUNC 128 /* STT_GNU_IFUNC. */ + unsigned char tls_mask; }; /* ppc64 ELF linker hash table. */ @@ -3621,6 +3738,8 @@ struct ppc_link_hash_table /* Temp used when calculating TOC pointers. */ bfd_vma toc_curr; + bfd *toc_bfd; + asection *toc_first_sec; /* Highest input section id. */ int top_id; @@ -3638,6 +3757,8 @@ struct ppc_link_hash_table asection *got; asection *plt; asection *relplt; + asection *iplt; + asection *reliplt; asection *dynbss; asection *relbss; asection *glink; @@ -3649,6 +3770,9 @@ struct ppc_link_hash_table struct ppc_link_hash_entry *tls_get_addr; struct ppc_link_hash_entry *tls_get_addr_fd; + /* The size of reliplt used by got entry relocs. */ + bfd_size_type got_reli_size; + /* Statistics. */ unsigned long stub_count[ppc_stub_plt_call]; @@ -3658,9 +3782,14 @@ struct ppc_link_hash_table /* Set if we should emit symbols for stubs. */ unsigned int emit_stub_syms:1; + /* Set if __tls_get_addr optimization should not be done. */ + unsigned int no_tls_get_addr_opt:1; + /* Support for multiple toc sections. */ - unsigned int no_multi_toc:1; + 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; @@ -3677,14 +3806,29 @@ struct ppc_link_hash_table /* Rename some of the generic section flags to better document how they are used here. */ -#define has_toc_reloc has_gp_reloc -#define makes_toc_func_call need_finalize_relax -#define call_check_in_progress reloc_done + +/* Nonzero if this section has TLS related relocations. */ +#define has_tls_reloc sec_flg0 + +/* Nonzero if this section has a call to __tls_get_addr. */ +#define has_tls_get_addr_call sec_flg1 + +/* Nonzero if this section has any toc or got relocs. */ +#define has_toc_reloc sec_flg2 + +/* Nonzero if this section has a call to another section that uses + the toc or got. */ +#define makes_toc_func_call sec_flg3 + +/* Recursion protection when determining above flag. */ +#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. */ #define ppc_hash_table(p) \ - ((struct ppc_link_hash_table *) ((p)->hash)) + (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \ + == PPC64_ELF_DATA ? ((struct ppc_link_hash_table *) ((p)->hash)) : NULL) #define ppc_stub_hash_lookup(table, string, create, copy) \ ((struct ppc_stub_hash_entry *) \ @@ -3829,7 +3973,8 @@ ppc64_elf_link_hash_table_create (bfd *abfd) return NULL; if (!_bfd_elf_link_hash_table_init (&htab->elf, abfd, link_hash_newfunc, - sizeof (struct ppc_link_hash_entry))) + sizeof (struct ppc_link_hash_entry), + PPC64_ELF_DATA)) { free (htab); return NULL; @@ -3886,6 +4031,8 @@ ppc64_elf_init_stub_bfd (bfd *abfd, struct bfd_link_info *info) linker created stub bfd. This ensures that the GOT header is at the start of the output TOC section. */ htab = ppc_hash_table (info); + if (htab == NULL) + return; htab->stub_bfd = abfd; htab->elf.dynobj = abfd; } @@ -4045,6 +4192,8 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info) flagword flags; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; /* Create .sfpr for code to save and restore fp regs. */ flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY @@ -4062,6 +4211,21 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info) || ! bfd_set_section_alignment (dynobj, htab->glink, 3)) return FALSE; + flags = SEC_ALLOC | SEC_LINKER_CREATED; + htab->iplt = bfd_make_section_anyway_with_flags (dynobj, ".iplt", flags); + if (htab->iplt == NULL + || ! bfd_set_section_alignment (dynobj, htab->iplt, 3)) + return FALSE; + + flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY + | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED); + htab->reliplt = bfd_make_section_anyway_with_flags (dynobj, + ".rela.iplt", + flags); + if (htab->reliplt == NULL + || ! bfd_set_section_alignment (dynobj, htab->reliplt, 3)) + return FALSE; + /* Create branch lookup table for plt_branch stubs. */ flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED); @@ -4079,7 +4243,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info) htab->relbrlt = bfd_make_section_anyway_with_flags (dynobj, ".rela.branch_lt", flags); - if (!htab->relbrlt + if (htab->relbrlt == NULL || ! bfd_set_section_alignment (dynobj, htab->relbrlt, 3)) return FALSE; @@ -4098,6 +4262,8 @@ create_got_section (bfd *abfd, struct bfd_link_info *info) if (!is_ppc64_elf (abfd)) return FALSE; + if (htab == NULL) + return FALSE; if (!htab->got) { @@ -4139,6 +4305,9 @@ ppc64_elf_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) return FALSE; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + if (!htab->got) htab->got = bfd_get_section_by_name (dynobj, ".got"); htab->plt = bfd_get_section_by_name (dynobj, ".plt"); @@ -4154,6 +4323,29 @@ ppc64_elf_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) return TRUE; } +/* Follow indirect and warning symbol links. */ + +static inline struct bfd_link_hash_entry * +follow_link (struct bfd_link_hash_entry *h) +{ + while (h->type == bfd_link_hash_indirect + || h->type == bfd_link_hash_warning) + h = h->u.i.link; + return h; +} + +static inline struct elf_link_hash_entry * +elf_follow_link (struct elf_link_hash_entry *h) +{ + return (struct elf_link_hash_entry *) follow_link (&h->root); +} + +static inline struct ppc_link_hash_entry * +ppc_follow_link (struct ppc_link_hash_entry *h) +{ + return (struct ppc_link_hash_entry *) follow_link (&h->elf.root); +} + /* Merge PLT info on FROM with that on TO. */ static void @@ -4236,6 +4428,8 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info, edir->is_func |= eind->is_func; edir->is_func_descriptor |= eind->is_func_descriptor; edir->tls_mask |= eind->tls_mask; + if (eind->oh != NULL) + edir->oh = ppc_follow_link (eind->oh); /* If called to transfer flags for a weakdef during processing of elf_adjust_dynamic_symbol, don't copy NON_GOT_REF. @@ -4305,7 +4499,7 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info, hash entry FH. Link the entries via their OH fields. */ static struct ppc_link_hash_entry * -get_fdh (struct ppc_link_hash_entry *fh, struct ppc_link_hash_table *htab) +lookup_fdh (struct ppc_link_hash_entry *fh, struct ppc_link_hash_table *htab) { struct ppc_link_hash_entry *fdh = fh->oh; @@ -4315,16 +4509,16 @@ get_fdh (struct ppc_link_hash_entry *fh, struct ppc_link_hash_table *htab) fdh = (struct ppc_link_hash_entry *) elf_link_hash_lookup (&htab->elf, fd_name, FALSE, FALSE, FALSE); - if (fdh != NULL) - { - fdh->is_func_descriptor = 1; - fdh->oh = fh; - fh->is_func = 1; - fh->oh = fdh; - } + if (fdh == NULL) + return fdh; + + fdh->is_func_descriptor = 1; + fdh->oh = fh; + fh->is_func = 1; + fh->oh = fdh; } - return fdh; + return ppc_follow_link (fdh); } /* Make a fake function descriptor sym for the code sym FH. */ @@ -4366,16 +4560,23 @@ make_fdh (struct bfd_link_info *info, function type. */ static bfd_boolean -ppc64_elf_add_symbol_hook (bfd *ibfd ATTRIBUTE_UNUSED, - struct bfd_link_info *info ATTRIBUTE_UNUSED, +ppc64_elf_add_symbol_hook (bfd *ibfd, + struct bfd_link_info *info, Elf_Internal_Sym *isym, const char **name ATTRIBUTE_UNUSED, flagword *flags ATTRIBUTE_UNUSED, asection **sec, bfd_vma *value ATTRIBUTE_UNUSED) { - if (*sec != NULL - && strcmp (bfd_get_section_name (ibfd, *sec), ".opd") == 0) + if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) + { + if ((ibfd->flags & DYNAMIC) == 0) + elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE; + } + else if (ELF_ST_TYPE (isym->st_info) == STT_FUNC) + ; + else if (*sec != NULL + && strcmp ((*sec)->name, ".opd") == 0) isym->st_info = ELF_ST_INFO (ELF_ST_BIND (isym->st_info), STT_FUNC); return TRUE; @@ -4442,23 +4643,27 @@ add_symbol_adjust (struct ppc_link_hash_entry *eh, struct bfd_link_info *info) abort (); htab = ppc_hash_table (info); - fdh = get_fdh (eh, htab); - if (fdh == NULL - && !info->relocatable - && (eh->elf.root.type == bfd_link_hash_undefined - || eh->elf.root.type == bfd_link_hash_undefweak) - && eh->elf.ref_regular) - { - /* Make an undefweak function descriptor sym, which is enough to - pull in an --as-needed shared lib, but won't cause link - errors. Archives are handled elsewhere. */ - fdh = make_fdh (info, eh); - if (fdh == NULL) - return FALSE; - else - fdh->elf.ref_regular = 1; + if (htab == NULL) + return FALSE; + + fdh = lookup_fdh (eh, htab); + if (fdh == NULL) + { + if (!info->relocatable + && (eh->elf.root.type == bfd_link_hash_undefined + || eh->elf.root.type == bfd_link_hash_undefweak) + && eh->elf.ref_regular) + { + /* Make an undefweak function descriptor sym, which is enough to + pull in an --as-needed shared lib, but won't cause link + errors. Archives are handled elsewhere. */ + fdh = make_fdh (info, eh); + if (fdh == NULL) + return FALSE; + fdh->elf.ref_regular = 1; + } } - else if (fdh != NULL) + else { unsigned entry_vis = ELF_ST_VISIBILITY (eh->elf.other) - 1; unsigned descr_vis = ELF_ST_VISIBILITY (fdh->elf.other) - 1; @@ -4488,9 +4693,11 @@ ppc64_elf_process_dot_syms (bfd *ibfd, struct bfd_link_info *info) struct ppc_link_hash_table *htab; struct ppc_link_hash_entry **p, *eh; - htab = ppc_hash_table (info); if (!is_ppc64_elf (info->output_bfd)) return TRUE; + htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; if (is_ppc64_elf (ibfd)) { @@ -4529,29 +4736,37 @@ static bfd_boolean ppc64_elf_as_needed_cleanup (bfd *ibfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) { - ppc_hash_table (info)->dot_syms = NULL; + struct ppc_link_hash_table *htab = ppc_hash_table (info); + + if (htab == NULL) + return FALSE; + + htab->dot_syms = NULL; return TRUE; } -static bfd_boolean +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) { struct got_entry **local_got_ents = elf_local_got_ents (abfd); - char *local_got_tls_masks; + struct plt_entry **local_plt; + unsigned char *local_got_tls_masks; if (local_got_ents == NULL) { bfd_size_type size = symtab_hdr->sh_info; - size *= sizeof (*local_got_ents) + sizeof (*local_got_tls_masks); + size *= (sizeof (*local_got_ents) + + sizeof (*local_plt) + + sizeof (*local_got_tls_masks)); local_got_ents = bfd_zalloc (abfd, size); if (local_got_ents == NULL) - return FALSE; + return NULL; elf_local_got_ents (abfd) = local_got_ents; } - if ((tls_type & TLS_EXPLICIT) == 0) + if ((tls_type & (PLT_IFUNC | TLS_EXPLICIT)) == 0) { struct got_entry *ent; @@ -4570,23 +4785,26 @@ update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr, ent->addend = r_addend; ent->owner = abfd; ent->tls_type = tls_type; + ent->is_indirect = FALSE; ent->got.refcount = 0; local_got_ents[r_symndx] = ent; } ent->got.refcount += 1; } - local_got_tls_masks = (char *) (local_got_ents + symtab_hdr->sh_info); + local_plt = (struct plt_entry **) (local_got_ents + symtab_hdr->sh_info); + local_got_tls_masks = (unsigned char *) (local_plt + symtab_hdr->sh_info); local_got_tls_masks[r_symndx] |= tls_type; - return TRUE; + + return local_plt + r_symndx; } static bfd_boolean -update_plt_info (bfd *abfd, struct ppc_link_hash_entry *eh, bfd_vma addend) +update_plt_info (bfd *abfd, struct plt_entry **plist, bfd_vma addend) { struct plt_entry *ent; - for (ent = eh->elf.plt.plist; ent != NULL; ent = ent->next) + for (ent = *plist; ent != NULL; ent = ent->next) if (ent->addend == addend) break; if (ent == NULL) @@ -4595,19 +4813,28 @@ update_plt_info (bfd *abfd, struct ppc_link_hash_entry *eh, bfd_vma addend) ent = bfd_alloc (abfd, amt); if (ent == NULL) return FALSE; - ent->next = eh->elf.plt.plist; + ent->next = *plist; ent->addend = addend; ent->plt.refcount = 0; - eh->elf.plt.plist = ent; + *plist = ent; } ent->plt.refcount += 1; - eh->elf.needs_plt = 1; - if (eh->elf.root.root.string[0] == '.' - && eh->elf.root.root.string[1] != '\0') - eh->is_func = 1; return TRUE; } +static bfd_boolean +is_branch_reloc (enum elf_ppc64_reloc_type r_type) +{ + return (r_type == R_PPC64_REL24 + || r_type == R_PPC64_REL14 + || r_type == R_PPC64_REL14_BRTAKEN + || r_type == R_PPC64_REL14_BRNTAKEN + || r_type == R_PPC64_ADDR24 + || r_type == R_PPC64_ADDR14 + || r_type == R_PPC64_ADDR14_BRTAKEN + || r_type == R_PPC64_ADDR14_BRNTAKEN); +} + /* Look through the relocs for a section during the first phase, and calculate needed space in the global offset table, procedure linkage table, and dynamic reloc sections. */ @@ -4618,7 +4845,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; @@ -4640,20 +4867,18 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, BFD_ASSERT (is_ppc64_elf (abfd)); htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + tga = elf_link_hash_lookup (&htab->elf, "__tls_get_addr", FALSE, FALSE, TRUE); 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 @@ -4685,6 +4910,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, enum elf_ppc64_reloc_type r_type; int tls_type; struct _ppc64_elf_section_data *ppc64_sec; + struct plt_entry **ifunc; r_symndx = ELF64_R_SYM (rel->r_info); if (r_symndx < symtab_hdr->sh_info) @@ -4692,38 +4918,55 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, else { h = sym_hashes[r_symndx - symtab_hdr->sh_info]; - while (h->root.type == bfd_link_hash_indirect - || h->root.type == bfd_link_hash_warning) - h = (struct elf_link_hash_entry *) h->root.u.i.link; + h = elf_follow_link (h); } tls_type = 0; - r_type = ELF64_R_TYPE (rel->r_info); - if (h != NULL && (h == tga || h == dottga)) - switch (r_type) - { - default: - break; + ifunc = NULL; + if (h != NULL) + { + if (h->type == STT_GNU_IFUNC) + { + h->needs_plt = 1; + ifunc = &h->plt.plist; + } + } + else + { + Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->sym_cache, + abfd, r_symndx); + if (isym == NULL) + return FALSE; - case R_PPC64_REL24: - case R_PPC64_REL14: - case R_PPC64_REL14_BRTAKEN: - case R_PPC64_REL14_BRNTAKEN: - case R_PPC64_ADDR24: - case R_PPC64_ADDR14: - case R_PPC64_ADDR14_BRTAKEN: - case R_PPC64_ADDR14_BRNTAKEN: - if (rel != relocs - && (ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSGD - || ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSLD)) - /* We have a new-style __tls_get_addr call with a marker - reloc. */ - ; - else - /* Mark this section as having an old-style call. */ - sec->has_tls_get_addr_call = 1; - break; - } + if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) + { + ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx, + rel->r_addend, PLT_IFUNC); + if (ifunc == NULL) + return FALSE; + } + } + r_type = ELF64_R_TYPE (rel->r_info); + if (is_branch_reloc (r_type)) + { + if (h != NULL && (h == tga || h == dottga)) + { + if (rel != relocs + && (ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSGD + || ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSLD)) + /* We have a new-style __tls_get_addr call with a marker + reloc. */ + ; + else + /* Mark this section as having an old-style call. */ + sec->has_tls_get_addr_call = 1; + } + + /* STT_GNU_IFUNC symbols must have a PLT entry. */ + if (ifunc != NULL + && !update_plt_info (abfd, ifunc, rel->r_addend)) + return FALSE; + } switch (r_type) { @@ -4773,6 +5016,17 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_PPC64_GOT16_LO_DS: /* This symbol requires a global offset table entry. */ sec->has_toc_reloc = 1; + if (r_type == R_PPC64_GOT_TLSLD16 + || r_type == R_PPC64_GOT_TLSGD16 + || r_type == R_PPC64_GOT_TPREL16_DS + || r_type == R_PPC64_GOT_DTPREL16_DS + || r_type == R_PPC64_GOT16 + || r_type == R_PPC64_GOT16_DS) + { + htab->do_multi_toc = 1; + ppc64_elf_tdata (abfd)->has_small_toc_reloc = 1; + } + if (ppc64_elf_tdata (abfd)->got == NULL && !create_got_section (abfd, info)) return FALSE; @@ -4798,6 +5052,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, ent->addend = rel->r_addend; ent->owner = abfd; ent->tls_type = tls_type; + ent->is_indirect = FALSE; ent->got.refcount = 0; eh->elf.got.glist = ent; } @@ -4829,9 +5084,14 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, return FALSE; } else - if (!update_plt_info (abfd, (struct ppc_link_hash_entry *) h, - rel->r_addend)) - return FALSE; + { + if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend)) + return FALSE; + h->needs_plt = 1; + if (h->root.root.string[0] == '.' + && h->root.root.string[1] != '\0') + ((struct ppc_link_hash_entry *) h)->is_func = 1; + } break; /* The following relocations don't need to propagate the @@ -4856,11 +5116,19 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, break; /* Nor do these. */ + case R_PPC64_REL16: + case R_PPC64_REL16_LO: + case R_PPC64_REL16_HI: + case R_PPC64_REL16_HA: + break; + case R_PPC64_TOC16: + case R_PPC64_TOC16_DS: + htab->do_multi_toc = 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: - case R_PPC64_TOC16_DS: case R_PPC64_TOC16_LO_DS: sec->has_toc_reloc = 1; break; @@ -4914,13 +5182,16 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, /* Fall through. */ case R_PPC64_REL24: - if (h != NULL) + if (h != NULL && ifunc == NULL) { /* We may need a .plt entry if the function this reloc refers to is in a shared lib. */ - if (!update_plt_info (abfd, (struct ppc_link_hash_entry *) h, - rel->r_addend)) + if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend)) return FALSE; + h->needs_plt = 1; + if (h->root.root.string[0] == '.' + && h->root.root.string[1] != '\0') + ((struct ppc_link_hash_entry *) h)->is_func = 1; if (h == tga || h == dottga) sec->has_tls_reloc = 1; } @@ -5019,7 +5290,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, { if (h->root.root.string[0] == '.' && h->root.root.string[1] != 0 - && get_fdh ((struct ppc_link_hash_entry *) h, htab)) + && lookup_fdh ((struct ppc_link_hash_entry *) h, htab)) ; else ((struct ppc_link_hash_entry *) h)->is_func = 1; @@ -5103,7 +5374,9 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, && !info->shared && h != NULL && (h->root.type == bfd_link_hash_defweak - || !h->def_regular))) + || !h->def_regular)) + || (!info->shared + && ifunc != NULL)) { struct ppc_dyn_relocs *p; struct ppc_dyn_relocs **head; @@ -5192,8 +5465,6 @@ opd_entry_value (asection *opd_sec, /* No relocs implies we are linking a --just-symbols object. */ if (opd_sec->reloc_count == 0) { - bfd_vma val; - if (!bfd_get_section_contents (opd_bfd, opd_sec, &val, offset, 8)) return (bfd_vma) -1; @@ -5270,9 +5541,7 @@ opd_entry_value (asection *opd_sec, sym_hashes = elf_sym_hashes (opd_bfd); rh = sym_hashes[symndx - symtab_hdr->sh_info]; - while (rh->root.type == bfd_link_hash_indirect - || rh->root.type == bfd_link_hash_warning) - rh = ((struct elf_link_hash_entry *) rh->root.u.i.link); + rh = elf_follow_link (rh); BFD_ASSERT (rh->root.type == bfd_link_hash_defined || rh->root.type == bfd_link_hash_defweak); val = rh->root.u.def.value; @@ -5293,6 +5562,50 @@ 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. */ + +static struct ppc_link_hash_entry * +defined_code_entry (struct ppc_link_hash_entry *fdh) +{ + if (fdh->is_func_descriptor) + { + struct ppc_link_hash_entry *fh = ppc_follow_link (fdh->oh); + if (fh->elf.root.type == bfd_link_hash_defined + || fh->elf.root.type == bfd_link_hash_defweak) + return fh; + } + return NULL; +} + +/* If FH is a function code entry symbol, return the associated + function descriptor symbol if it is defined. Return NULL otherwise. */ + +static struct ppc_link_hash_entry * +defined_func_desc (struct ppc_link_hash_entry *fh) +{ + if (fh->oh != NULL + && fh->oh->is_func_descriptor) + { + struct ppc_link_hash_entry *fdh = ppc_follow_link (fh->oh); + if (fdh->elf.root.type == bfd_link_hash_defined + || fdh->elf.root.type == bfd_link_hash_defweak) + return fdh; + } + return NULL; +} + /* Mark all our entry sym sections, both opd and code section. */ static void @@ -5301,24 +5614,26 @@ ppc64_elf_gc_keep (struct bfd_link_info *info) struct ppc_link_hash_table *htab = ppc_hash_table (info); struct bfd_sym_chain *sym; + if (htab == NULL) + return; + for (sym = info->gc_sym_list; sym != NULL; sym = sym->next) { - struct ppc_link_hash_entry *eh; + struct ppc_link_hash_entry *eh, *fh; asection *sec; eh = (struct ppc_link_hash_entry *) - elf_link_hash_lookup (&htab->elf, sym->name, FALSE, FALSE, FALSE); + elf_link_hash_lookup (&htab->elf, sym->name, FALSE, FALSE, TRUE); if (eh == NULL) continue; if (eh->elf.root.type != bfd_link_hash_defined && eh->elf.root.type != bfd_link_hash_defweak) continue; - if (eh->is_func_descriptor - && (eh->oh->elf.root.type == bfd_link_hash_defined - || eh->oh->elf.root.type == bfd_link_hash_defweak)) + fh = defined_code_entry (eh); + if (fh != NULL) { - sec = eh->oh->elf.root.u.def.section; + sec = fh->elf.root.u.def.section; sec->flags |= SEC_KEEP; } else if (get_opd_info (eh->elf.root.u.def.section) != NULL @@ -5341,16 +5656,15 @@ ppc64_elf_gc_mark_dynamic_ref (struct elf_link_hash_entry *h, void *inf) { struct bfd_link_info *info = (struct bfd_link_info *) inf; struct ppc_link_hash_entry *eh = (struct ppc_link_hash_entry *) h; + struct ppc_link_hash_entry *fdh; if (eh->elf.root.type == bfd_link_hash_warning) eh = (struct ppc_link_hash_entry *) eh->elf.root.u.i.link; /* Dynamic linking info is on the func descriptor sym. */ - if (eh->oh != NULL - && eh->oh->is_func_descriptor - && (eh->oh->elf.root.type == bfd_link_hash_defined - || eh->oh->elf.root.type == bfd_link_hash_defweak)) - eh = eh->oh; + fdh = defined_func_desc (eh); + if (fdh != NULL) + eh = fdh; if ((eh->elf.root.type == bfd_link_hash_defined || eh->elf.root.type == bfd_link_hash_defweak) @@ -5361,15 +5675,18 @@ ppc64_elf_gc_mark_dynamic_ref (struct elf_link_hash_entry *h, void *inf) && ELF_ST_VISIBILITY (eh->elf.other) != STV_HIDDEN))) { asection *code_sec; + struct ppc_link_hash_entry *fh; eh->elf.root.u.def.section->flags |= SEC_KEEP; /* Function descriptor syms cause the associated function code sym section to be marked. */ - if (eh->is_func_descriptor - && (eh->oh->elf.root.type == bfd_link_hash_defined - || eh->oh->elf.root.type == bfd_link_hash_defweak)) - eh->oh->elf.root.u.def.section->flags |= SEC_KEEP; + fh = defined_code_entry (eh); + if (fh != NULL) + { + code_sec = fh->elf.root.u.def.section; + code_sec->flags |= SEC_KEEP; + } else if (get_opd_info (eh->elf.root.u.def.section) != NULL && opd_entry_value (eh->elf.root.u.def.section, eh->elf.root.u.def.value, @@ -5385,7 +5702,7 @@ ppc64_elf_gc_mark_dynamic_ref (struct elf_link_hash_entry *h, void *inf) static asection * ppc64_elf_gc_mark_hook (asection *sec, - struct bfd_link_info *info ATTRIBUTE_UNUSED, + struct bfd_link_info *info, Elf_Internal_Rela *rel, struct elf_link_hash_entry *h, Elf_Internal_Sym *sym) @@ -5401,7 +5718,7 @@ ppc64_elf_gc_mark_hook (asection *sec, if (h != NULL) { enum elf_ppc64_reloc_type r_type; - struct ppc_link_hash_entry *eh; + struct ppc_link_hash_entry *eh, *fh, *fdh; r_type = ELF64_R_TYPE (rel->r_info); switch (r_type) @@ -5416,22 +5733,19 @@ ppc64_elf_gc_mark_hook (asection *sec, case bfd_link_hash_defined: case bfd_link_hash_defweak: eh = (struct ppc_link_hash_entry *) h; - if (eh->oh != NULL - && eh->oh->is_func_descriptor - && (eh->oh->elf.root.type == bfd_link_hash_defined - || eh->oh->elf.root.type == bfd_link_hash_defweak)) - eh = eh->oh; + fdh = defined_func_desc (eh); + if (fdh != NULL) + eh = fdh; /* Function descriptor syms cause the associated function code sym section to be marked. */ - if (eh->is_func_descriptor - && (eh->oh->elf.root.type == bfd_link_hash_defined - || eh->oh->elf.root.type == bfd_link_hash_defweak)) + fh = defined_code_entry (eh); + if (fh != NULL) { /* They also mark their opd section. */ eh->elf.root.u.def.section->gc_mark = 1; - rsec = eh->oh->elf.root.u.def.section; + rsec = fh->elf.root.u.def.section; } else if (get_opd_info (eh->elf.root.u.def.section) != NULL && opd_entry_value (eh->elf.root.u.def.section, @@ -5447,7 +5761,7 @@ ppc64_elf_gc_mark_hook (asection *sec, break; default: - break; + return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); } } } @@ -5490,6 +5804,9 @@ ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, elf_section_data (sec)->local_dynrel = NULL; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + symtab_hdr = &elf_symtab_hdr (abfd); sym_hashes = elf_sym_hashes (abfd); local_got_ents = elf_local_got_ents (abfd); @@ -5500,7 +5817,7 @@ ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, unsigned long r_symndx; enum elf_ppc64_reloc_type r_type; struct elf_link_hash_entry *h = NULL; - char tls_type = 0; + unsigned char tls_type = 0; r_symndx = ELF64_R_SYM (rel->r_info); r_type = ELF64_R_TYPE (rel->r_info); @@ -5511,9 +5828,7 @@ ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, struct ppc_dyn_relocs *p; h = sym_hashes[r_symndx - symtab_hdr->sh_info]; - while (h->root.type == bfd_link_hash_indirect - || h->root.type == bfd_link_hash_warning) - h = (struct elf_link_hash_entry *) h->root.u.i.link; + h = elf_follow_link (h); eh = (struct ppc_link_hash_entry *) h; for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next) @@ -5525,6 +5840,38 @@ ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, } } + if (is_branch_reloc (r_type)) + { + struct plt_entry **ifunc = NULL; + if (h != NULL) + { + if (h->type == STT_GNU_IFUNC) + ifunc = &h->plt.plist; + } + else if (local_got_ents != NULL) + { + struct plt_entry **local_plt = (struct plt_entry **) + (local_got_ents + symtab_hdr->sh_info); + unsigned char *local_got_tls_masks = (unsigned char *) + (local_plt + symtab_hdr->sh_info); + if ((local_got_tls_masks[r_symndx] & PLT_IFUNC) != 0) + ifunc = local_plt + r_symndx; + } + if (ifunc != NULL) + { + struct plt_entry *ent; + + for (ent = *ifunc; ent != NULL; ent = ent->next) + if (ent->addend == rel->r_addend) + break; + if (ent == NULL) + abort (); + if (ent->plt.refcount > 0) + ent->plt.refcount -= 1; + continue; + } + } + switch (r_type) { case R_PPC64_GOT_TLSLD16: @@ -5598,9 +5945,7 @@ ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, for (ent = h->plt.plist; ent != NULL; ent = ent->next) if (ent->addend == rel->r_addend) break; - if (ent == NULL) - abort (); - if (ent->plt.refcount > 0) + if (ent != NULL && ent->plt.refcount > 0) ent->plt.refcount -= 1; } break; @@ -5625,7 +5970,7 @@ struct sfpr_def_parms /* Auto-generate _save*, _rest* functions in .sfpr. */ -static unsigned int +static bfd_boolean sfpr_define (struct bfd_link_info *info, const struct sfpr_def_parms *parm) { struct ppc_link_hash_table *htab = ppc_hash_table (info); @@ -5634,6 +5979,9 @@ sfpr_define (struct bfd_link_info *info, const struct sfpr_def_parms *parm) bfd_boolean writing = FALSE; char sym[16]; + if (htab == NULL) + return FALSE; + memcpy (sym, parm->name, len); sym[len + 2] = 0; @@ -5860,6 +6208,8 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf) info = inf; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; /* Resolve undefined references to dot-symbols as the value in the function descriptor, if we have one in a regular object. @@ -5867,18 +6217,17 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf) in dynamic objects are handled elsewhere. */ if (fh->elf.root.type == bfd_link_hash_undefweak && fh->was_undefined - && (fh->oh->elf.root.type == bfd_link_hash_defined - || fh->oh->elf.root.type == bfd_link_hash_defweak) - && get_opd_info (fh->oh->elf.root.u.def.section) != NULL - && opd_entry_value (fh->oh->elf.root.u.def.section, - fh->oh->elf.root.u.def.value, + && (fdh = defined_func_desc (fh)) != NULL + && get_opd_info (fdh->elf.root.u.def.section) != NULL + && opd_entry_value (fdh->elf.root.u.def.section, + fdh->elf.root.u.def.value, &fh->elf.root.u.def.section, &fh->elf.root.u.def.value) != (bfd_vma) -1) { - fh->elf.root.type = fh->oh->elf.root.type; + fh->elf.root.type = fdh->elf.root.type; fh->elf.forced_local = 1; - fh->elf.def_regular = fh->oh->elf.def_regular; - fh->elf.def_dynamic = fh->oh->elf.def_dynamic; + fh->elf.def_regular = fdh->elf.def_regular; + fh->elf.def_dynamic = fdh->elf.def_dynamic; } /* If this is a function code symbol, transfer dynamic linking @@ -5897,12 +6246,7 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf) /* Find the corresponding function descriptor symbol. Create it as undefined if necessary. */ - fdh = get_fdh (fh, htab); - if (fdh != NULL) - while (fdh->elf.root.type == bfd_link_hash_indirect - || fdh->elf.root.type == bfd_link_hash_warning) - fdh = (struct ppc_link_hash_entry *) fdh->elf.root.u.i.link; - + fdh = lookup_fdh (fh, htab); if (fdh == NULL && !info->executable && (fh->elf.root.type == bfd_link_hash_undefined @@ -6004,6 +6348,9 @@ ppc64_elf_func_desc_adjust (bfd *obfd ATTRIBUTE_UNUSED, }; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + if (htab->sfpr == NULL) /* We don't have any relocs. */ return TRUE; @@ -6036,9 +6383,12 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, asection *s; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; /* Deal with function syms. */ if (h->type == STT_FUNC + || h->type == STT_GNU_IFUNC || h->needs_plt) { /* Clear procedure linkage table information for any symbol that @@ -6048,9 +6398,10 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, if (ent->plt.refcount > 0) break; if (ent == NULL - || SYMBOL_CALLS_LOCAL (info, h) - || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT - && h->root.type == bfd_link_hash_undefweak)) + || (h->type != STT_GNU_IFUNC + && (SYMBOL_CALLS_LOCAL (info, h) + || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT + && h->root.type == bfd_link_hash_undefweak)))) { h->plt.plist = NULL; h->needs_plt = 0; @@ -6193,6 +6544,9 @@ ppc64_elf_hide_symbol (struct bfd_link_info *info, save = *p; *(char *) p = '.'; htab = ppc_hash_table (info); + if (htab == NULL) + return; + fh = (struct ppc_link_hash_entry *) elf_link_hash_lookup (&htab->elf, p, FALSE, FALSE, FALSE); *(char *) p = save; @@ -6225,7 +6579,7 @@ static bfd_boolean get_sym_h (struct elf_link_hash_entry **hp, Elf_Internal_Sym **symp, asection **symsecp, - char **tls_maskp, + unsigned char **tls_maskp, Elf_Internal_Sym **locsymsp, unsigned long r_symndx, bfd *ibfd) @@ -6238,9 +6592,7 @@ get_sym_h (struct elf_link_hash_entry **hp, struct elf_link_hash_entry *h; h = sym_hashes[r_symndx - symtab_hdr->sh_info]; - while (h->root.type == bfd_link_hash_indirect - || h->root.type == bfd_link_hash_warning) - h = (struct elf_link_hash_entry *) h->root.u.i.link; + h = elf_follow_link (h); if (hp != NULL) *hp = h; @@ -6295,13 +6647,16 @@ get_sym_h (struct elf_link_hash_entry **hp, if (tls_maskp != NULL) { struct got_entry **lgot_ents; - char *tls_mask; + unsigned char *tls_mask; tls_mask = NULL; lgot_ents = elf_local_got_ents (ibfd); if (lgot_ents != NULL) { - char *lgot_masks = (char *) (lgot_ents + symtab_hdr->sh_info); + struct plt_entry **local_plt = (struct plt_entry **) + (lgot_ents + symtab_hdr->sh_info); + unsigned char *lgot_masks = (unsigned char *) + (local_plt + symtab_hdr->sh_info); tls_mask = &lgot_masks[r_symndx]; } *tls_maskp = tls_mask; @@ -6315,7 +6670,7 @@ get_sym_h (struct elf_link_hash_entry **hp, type suitable for optimization, and 1 otherwise. */ static int -get_tls_mask (char **tls_maskp, +get_tls_mask (unsigned char **tls_maskp, unsigned long *toc_symndx, bfd_vma *toc_addend, Elf_Internal_Sym **locsymsp, @@ -6335,6 +6690,7 @@ get_tls_mask (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; @@ -6356,10 +6712,7 @@ get_tls_mask (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; @@ -6558,8 +6911,7 @@ dec_dynrel_count (bfd_vma r_info, applications. */ bfd_boolean -ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, - bfd_boolean non_overlapping) +ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping) { bfd *ibfd; bfd_boolean some_edited = FALSE; @@ -6571,12 +6923,14 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, 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; @@ -6593,7 +6947,6 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, 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, @@ -6720,6 +7073,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, 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; @@ -6729,7 +7083,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, new_contents = NULL; amt = sec->size * sizeof (long) / 8; opd = &ppc64_elf_section_data (sec)->u.opd; - opd->adjust = bfd_zalloc (obfd, amt); + opd->adjust = bfd_zalloc (sec->owner, amt); if (opd->adjust == NULL) return FALSE; ppc64_elf_section_data (sec)->sec_type = sec_opd; @@ -6805,8 +7159,12 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, if (h != NULL && h->root.root.string[0] == '.') { - fdh = get_fdh ((struct ppc_link_hash_entry *) h, - ppc_hash_table (info)); + struct ppc_link_hash_table *htab; + + htab = ppc_hash_table (info); + if (htab != NULL) + 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) @@ -6895,9 +7253,8 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, /* 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) @@ -6956,18 +7313,102 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, /* Set htab->tls_get_addr and call the generic ELF tls_setup function. */ asection * -ppc64_elf_tls_setup (bfd *obfd, struct bfd_link_info *info) +ppc64_elf_tls_setup (struct bfd_link_info *info, + int no_tls_get_addr_opt, + int *no_multi_toc) { struct ppc_link_hash_table *htab; htab = ppc_hash_table (info); + if (htab == NULL) + return NULL; + + if (*no_multi_toc) + htab->do_multi_toc = 0; + else if (!htab->do_multi_toc) + *no_multi_toc = 1; + htab->tls_get_addr = ((struct ppc_link_hash_entry *) elf_link_hash_lookup (&htab->elf, ".__tls_get_addr", FALSE, FALSE, TRUE)); + /* Move dynamic linking info to the function descriptor sym. */ + if (htab->tls_get_addr != NULL) + func_desc_adjust (&htab->tls_get_addr->elf, info); htab->tls_get_addr_fd = ((struct ppc_link_hash_entry *) elf_link_hash_lookup (&htab->elf, "__tls_get_addr", FALSE, FALSE, TRUE)); - return _bfd_elf_tls_setup (obfd, info); + if (!no_tls_get_addr_opt) + { + struct elf_link_hash_entry *opt, *opt_fd, *tga, *tga_fd; + + opt = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr_opt", + FALSE, FALSE, TRUE); + if (opt != NULL) + func_desc_adjust (opt, info); + opt_fd = elf_link_hash_lookup (&htab->elf, "__tls_get_addr_opt", + FALSE, FALSE, TRUE); + if (opt_fd != NULL + && (opt_fd->root.type == bfd_link_hash_defined + || opt_fd->root.type == bfd_link_hash_defweak)) + { + /* If glibc supports an optimized __tls_get_addr call stub, + signalled by the presence of __tls_get_addr_opt, and we'll + be calling __tls_get_addr via a plt call stub, then + make __tls_get_addr point to __tls_get_addr_opt. */ + tga_fd = &htab->tls_get_addr_fd->elf; + if (htab->elf.dynamic_sections_created + && tga_fd != NULL + && (tga_fd->type == STT_FUNC + || tga_fd->needs_plt) + && !(SYMBOL_CALLS_LOCAL (info, tga_fd) + || (ELF_ST_VISIBILITY (tga_fd->other) != STV_DEFAULT + && tga_fd->root.type == bfd_link_hash_undefweak))) + { + struct plt_entry *ent; + + for (ent = tga_fd->plt.plist; ent != NULL; ent = ent->next) + if (ent->plt.refcount > 0) + break; + if (ent != NULL) + { + tga_fd->root.type = bfd_link_hash_indirect; + tga_fd->root.u.i.link = &opt_fd->root; + ppc64_elf_copy_indirect_symbol (info, opt_fd, tga_fd); + if (opt_fd->dynindx != -1) + { + /* Use __tls_get_addr_opt in dynamic relocations. */ + opt_fd->dynindx = -1; + _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr, + opt_fd->dynstr_index); + if (!bfd_elf_link_record_dynamic_symbol (info, opt_fd)) + return NULL; + } + htab->tls_get_addr_fd = (struct ppc_link_hash_entry *) opt_fd; + tga = &htab->tls_get_addr->elf; + if (opt != NULL && tga != NULL) + { + tga->root.type = bfd_link_hash_indirect; + tga->root.u.i.link = &opt->root; + ppc64_elf_copy_indirect_symbol (info, opt, tga); + _bfd_elf_link_hash_hide_symbol (info, opt, + tga->forced_local); + htab->tls_get_addr = (struct ppc_link_hash_entry *) opt; + } + htab->tls_get_addr_fd->oh = htab->tls_get_addr; + htab->tls_get_addr_fd->is_func_descriptor = 1; + if (htab->tls_get_addr != NULL) + { + htab->tls_get_addr->oh = htab->tls_get_addr_fd; + htab->tls_get_addr->is_func = 1; + } + } + } + } + else + no_tls_get_addr_opt = TRUE; + } + htab->no_tls_get_addr_opt = no_tls_get_addr_opt; + return _bfd_elf_tls_setup (info->output_bfd, info); } /* Return TRUE iff REL is a branch reloc with a global symbol matching @@ -6983,23 +7424,13 @@ branch_reloc_hash_match (const bfd *ibfd, enum elf_ppc64_reloc_type r_type = ELF64_R_TYPE (rel->r_info); unsigned int r_symndx = ELF64_R_SYM (rel->r_info); - if (r_symndx >= symtab_hdr->sh_info - && (r_type == R_PPC64_REL24 - || r_type == R_PPC64_REL14 - || r_type == R_PPC64_REL14_BRTAKEN - || r_type == R_PPC64_REL14_BRNTAKEN - || r_type == R_PPC64_ADDR24 - || r_type == R_PPC64_ADDR14 - || r_type == R_PPC64_ADDR14_BRTAKEN - || r_type == R_PPC64_ADDR14_BRNTAKEN)) + if (r_symndx >= symtab_hdr->sh_info && is_branch_reloc (r_type)) { struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (ibfd); struct elf_link_hash_entry *h; h = sym_hashes[r_symndx - symtab_hdr->sh_info]; - while (h->root.type == bfd_link_hash_indirect - || h->root.type == bfd_link_hash_warning) - h = (struct elf_link_hash_entry *) h->root.u.i.link; + h = elf_follow_link (h); if (h == &hash1->elf || h == &hash2->elf) return TRUE; } @@ -7014,7 +7445,7 @@ branch_reloc_hash_match (const bfd *ibfd, dynamic relocations. */ bfd_boolean -ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) +ppc64_elf_tls_optimize (struct bfd_link_info *info) { bfd *ibfd; asection *sec; @@ -7025,6 +7456,9 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) return TRUE; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) { Elf_Internal_Sym *locsyms = NULL; @@ -7057,8 +7491,8 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) struct elf_link_hash_entry *h; Elf_Internal_Sym *sym; asection *sym_sec; - char *tls_mask; - char tls_set, tls_clear, tls_type = 0; + unsigned char *tls_mask; + unsigned char tls_set, tls_clear, tls_type = 0; bfd_vma value; bfd_boolean ok_tprel, is_local; long toc_ref_index = 0; @@ -7082,10 +7516,13 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) if (h != NULL) { - if (h->root.type != bfd_link_hash_defined - && h->root.type != bfd_link_hash_defweak) + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + value = h->root.u.def.value; + else if (h->root.type == bfd_link_hash_undefweak) + value = 0; + else continue; - value = h->root.u.def.value; } else /* Symbols referenced by TLS relocs must be of type @@ -7098,11 +7535,17 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) || !h->def_dynamic) { is_local = TRUE; - value += sym_sec->output_offset; - value += sym_sec->output_section->vma; - value -= htab->elf.tls_sec->vma; - ok_tprel = (value + TP_OFFSET + ((bfd_vma) 1 << 31) - < (bfd_vma) 1 << 32); + if (h != NULL + && h->root.type == bfd_link_hash_undefweak) + ok_tprel = TRUE; + else + { + value += sym_sec->output_offset; + value += sym_sec->output_section->vma; + value -= htab->elf.tls_sec->vma; + ok_tprel = (value + TP_OFFSET + ((bfd_vma) 1 << 31) + < (bfd_vma) 1 << 32); + } } r_type = ELF64_R_TYPE (rel->r_info); @@ -7262,7 +7705,7 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) if (expecting_tls_get_addr == 2) { /* Check for toc tls entries. */ - char *toc_tls; + unsigned char *toc_tls; int retval; retval = get_tls_mask (&toc_tls, NULL, NULL, @@ -7398,11 +7841,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; @@ -7420,16 +7866,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) @@ -7442,23 +7894,27 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf) unused .toc entries. */ bfd_boolean -ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) +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; 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 @@ -7468,7 +7924,6 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) 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; @@ -7536,18 +7991,100 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, 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. */ + relstart = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL, + info->keep_memory); + if (relstart == NULL) + goto error_ret; + + for (rel = relstart; rel < relstart + 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 (!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 - relstart) << 2); + } + + if (elf_section_data (toc)->relocs != relstart) + free (relstart); + } + if (skip == NULL) continue; @@ -7586,7 +8123,8 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, 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; @@ -7639,12 +8177,37 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, 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; @@ -7652,6 +8215,9 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, 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 @@ -7663,13 +8229,15 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, 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; @@ -7681,6 +8249,8 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, 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. */ @@ -7693,52 +8263,20 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, 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 @@ -7746,7 +8284,7 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, 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; @@ -7756,7 +8294,7 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, 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) @@ -7779,41 +8317,91 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, 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; - rel->r_addend -= skip[rel->r_addend >> 3]; - } - } + if (h != NULL) + val = h->root.u.def.value; + else + { + val = sym->st_value; + if (val != 0) + local_toc_syms = TRUE; + } - /* We shouldn't have local or global symbols defined in the TOC, - but handle them anyway. */ - if (local_syms != NULL) - { - Elf_Internal_Sym *sym; + 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 + = elf_section_data (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[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. */ + 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 (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 (skip[sym->st_value >> 3] != (unsigned long) -1) - sym->st_value -= skip[sym->st_value >> 3]; - else - { - (*_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; + if (local_toc_syms) + (*_bfd_error_handler) + (_("%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; } - } - /* Finally, adjust any global syms defined in the toc. */ + sym->st_value -= skip[i]; + symtab_hdr->contents = (unsigned char *) local_syms; + } + + /* Adjust any global syms defined in this toc input section. */ if (toc_inf.global_toc_syms) { toc_inf.toc = toc; @@ -7822,6 +8410,39 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, 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; + + /* 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] + & (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; + + toc->reloc_count = wrel - relstart; + rel_hdr = _bfd_elf_single_rel_hdr (toc); + sz = rel_hdr->sh_entsize; + rel_hdr->sh_size = toc->reloc_count * sz; + } } if (local_syms != NULL @@ -7838,6 +8459,72 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, 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 +allocate_got (struct elf_link_hash_entry *h, + struct bfd_link_info *info, + struct got_entry *gent) +{ + struct ppc_link_hash_table *htab = ppc_hash_table (info); + bfd_boolean dyn; + struct ppc_link_hash_entry *eh = (struct ppc_link_hash_entry *) h; + int entsize = (gent->tls_type & eh->tls_mask & (TLS_GD | TLS_LD) + ? 16 : 8); + int rentsize = (gent->tls_type & eh->tls_mask & TLS_GD + ? 2 : 1) * sizeof (Elf64_External_Rela); + asection *got = ppc64_elf_tdata (gent->owner)->got; + + gent->got.offset = got->size; + got->size += entsize; + + dyn = htab->elf.dynamic_sections_created; + if ((info->shared + || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)) + && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak)) + { + asection *relgot = ppc64_elf_tdata (gent->owner)->relgot; + relgot->size += rentsize; + } + else if (h->type == STT_GNU_IFUNC) + { + asection *relgot = htab->reliplt; + relgot->size += rentsize; + htab->got_reli_size += rentsize; + } +} + +/* 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. */ @@ -7849,7 +8536,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) asection *s; struct ppc_link_hash_entry *eh; struct ppc_dyn_relocs *p; - struct got_entry *gent; + struct got_entry **pgent, *gent; if (h->root.type == bfd_link_hash_indirect) return TRUE; @@ -7859,38 +8546,52 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) info = (struct bfd_link_info *) inf; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; - if (htab->elf.dynamic_sections_created - && h->dynindx != -1 - && WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h)) + if ((htab->elf.dynamic_sections_created + && h->dynindx != -1 + && WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h)) + || h->type == STT_GNU_IFUNC) { struct plt_entry *pent; bfd_boolean doneone = FALSE; for (pent = h->plt.plist; pent != NULL; pent = pent->next) if (pent->plt.refcount > 0) { - /* If this is the first .plt entry, make room for the special - first entry. */ - s = htab->plt; - if (s->size == 0) - s->size += PLT_INITIAL_ENTRY_SIZE; - - pent->plt.offset = s->size; - - /* Make room for this entry. */ - s->size += PLT_ENTRY_SIZE; - - /* Make room for the .glink code. */ - s = htab->glink; - if (s->size == 0) - s->size += GLINK_CALL_STUB_SIZE; - /* We need bigger stubs past index 32767. */ - if (s->size >= GLINK_CALL_STUB_SIZE + 32768*2*4) - s->size += 4; - s->size += 2*4; - - /* We also need to make an entry in the .rela.plt section. */ - s = htab->relplt; + if (!htab->elf.dynamic_sections_created + || h->dynindx == -1) + { + s = htab->iplt; + pent->plt.offset = s->size; + s->size += PLT_ENTRY_SIZE; + s = htab->reliplt; + } + else + { + /* If this is the first .plt entry, make room for the special + first entry. */ + s = htab->plt; + if (s->size == 0) + s->size += PLT_INITIAL_ENTRY_SIZE; + + pent->plt.offset = s->size; + + /* Make room for this entry. */ + s->size += PLT_ENTRY_SIZE; + + /* Make room for the .glink code. */ + s = htab->glink; + if (s->size == 0) + s->size += GLINK_CALL_STUB_SIZE; + /* We need bigger stubs past index 32767. */ + if (s->size >= GLINK_CALL_STUB_SIZE + 32768*2*4) + s->size += 4; + s->size += 2*4; + + /* We also need to make an entry in the .rela.plt section. */ + s = htab->relplt; + } s->size += sizeof (Elf64_External_Rela); doneone = TRUE; } @@ -7934,52 +8635,52 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) gent->tls_type = TLS_TLS | TLS_TPREL; } - for (gent = h->got.glist; gent != NULL; gent = gent->next) + /* 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) { - bfd_boolean dyn; + 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, nor will all TLS symbols. */ if (h->dynindx == -1 && !h->forced_local + && h->type != STT_GNU_IFUNC && htab->elf.dynamic_sections_created) { if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; } - if ((gent->tls_type & TLS_LD) != 0 - && !h->def_dynamic) - { - ppc64_tlsld_got (gent->owner)->refcount += 1; - gent->got.offset = (bfd_vma) -1; - continue; - } - if (!is_ppc64_elf (gent->owner)) - continue; + abort (); - s = ppc64_elf_tdata (gent->owner)->got; - gent->got.offset = s->size; - s->size - += (gent->tls_type & eh->tls_mask & (TLS_GD | TLS_LD)) ? 16 : 8; - dyn = htab->elf.dynamic_sections_created; - if ((info->shared - || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)) - && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT - || h->root.type != bfd_link_hash_undefweak)) - ppc64_elf_tdata (gent->owner)->relgot->size - += (gent->tls_type & eh->tls_mask & TLS_GD - ? 2 * sizeof (Elf64_External_Rela) - : sizeof (Elf64_External_Rela)); + allocate_got (h, info, gent); } - else - gent->got.offset = (bfd_vma) -1; if (eh->dyn_relocs == NULL - || !htab->elf.dynamic_sections_created) + || (!htab->elf.dynamic_sections_created + && h->type != STT_GNU_IFUNC)) return TRUE; /* In the shared -Bsymbolic case, discard space allocated for @@ -8029,6 +8730,11 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) } } } + else if (h->type == STT_GNU_IFUNC) + { + if (!h->non_got_ref) + eh->dyn_relocs = NULL; + } else if (ELIMINATE_COPY_RELOCS) { /* For the non-shared case, discard space for relocs against @@ -8062,6 +8768,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) for (p = eh->dyn_relocs; p != NULL; p = p->next) { asection *sreloc = elf_section_data (p->sec)->sreloc; + if (!htab->elf.dynamic_sections_created) + sreloc = htab->reliplt; sreloc->size += p->count * sizeof (Elf64_External_Rela); } @@ -8108,8 +8816,12 @@ 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) + return FALSE; + dynobj = htab->elf.dynobj; if (dynobj == NULL) abort (); @@ -8133,7 +8845,9 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, { struct got_entry **lgot_ents; struct got_entry **end_lgot_ents; - char *lgot_masks; + struct plt_entry **local_plt; + struct plt_entry **end_local_plt; + unsigned char *lgot_masks; bfd_size_type locsymcount; Elf_Internal_Shdr *symtab_hdr; asection *srel; @@ -8158,6 +8872,8 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, else if (p->count != 0) { srel = elf_section_data (p->sec)->sreloc; + if (!htab->elf.dynamic_sections_created) + srel = htab->reliplt; srel->size += p->count * sizeof (Elf64_External_Rela); if ((p->sec->output_section->flags & SEC_READONLY) != 0) info->flags |= DF_TEXTREL; @@ -8172,40 +8888,63 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, symtab_hdr = &elf_symtab_hdr (ibfd); locsymcount = symtab_hdr->sh_info; end_lgot_ents = lgot_ents + locsymcount; - lgot_masks = (char *) end_lgot_ents; + local_plt = (struct plt_entry **) end_lgot_ents; + end_local_plt = local_plt + locsymcount; + lgot_masks = (unsigned char *) end_local_plt; s = ppc64_elf_tdata (ibfd)->got; srel = ppc64_elf_tdata (ibfd)->relgot; for (; lgot_ents < end_lgot_ents; ++lgot_ents, ++lgot_masks) { - struct got_entry *ent; + struct got_entry **pent, *ent; - for (ent = *lgot_ents; ent != NULL; ent = ent->next) + pent = lgot_ents; + while ((ent = *pent) != NULL) if (ent->got.refcount > 0) { if ((ent->tls_type & *lgot_masks & TLS_LD) != 0) { - ppc64_tlsld_got (ibfd)->refcount += 1; - ent->got.offset = (bfd_vma) -1; + ppc64_tlsld_got (ibfd)->got.refcount += 1; + *pent = ent->next; } else { + 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) { - s->size += 16; - if (info->shared) - srel->size += 2 * sizeof (Elf64_External_Rela); - } - else - { - s->size += 8; - if (info->shared) - srel->size += sizeof (Elf64_External_Rela); + htab->reliplt->size + += num * sizeof (Elf64_External_Rela); + htab->got_reli_size + += num * sizeof (Elf64_External_Rela); } + pent = &ent->next; } } else - ent->got.offset = (bfd_vma) -1; + *pent = ent->next; + } + + /* Allocate space for calls to local STT_GNU_IFUNC syms in .iplt. */ + for (; local_plt < end_local_plt; ++local_plt) + { + struct plt_entry *ent; + + for (ent = *local_plt; ent != NULL; ent = ent->next) + if (ent->plt.refcount > 0) + { + s = htab->iplt; + ent->plt.offset = s->size; + s->size += PLT_ENTRY_SIZE; + + htab->reliplt->size += sizeof (Elf64_External_Rela); + } + else + ent->plt.offset = (bfd_vma) -1; } } @@ -8213,24 +8952,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)->refcount > 0) + ent = ppc64_tlsld_got (ibfd); + if (ent->got.refcount > 0) { - s = ppc64_elf_tdata (ibfd)->got; - ppc64_tlsld_got (ibfd)->offset = s->size; - 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)->offset = (bfd_vma) -1; + ent->got.offset = (bfd_vma) -1; } /* We now have determined the sizes of the various dynamic sections. @@ -8246,13 +9000,14 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, continue; else if (s == htab->got || s == htab->plt + || s == htab->iplt || s == htab->glink || s == htab->dynbss) { /* 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) { @@ -8366,6 +9121,12 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, return FALSE; } + if (!htab->no_tls_get_addr_opt + && htab->tls_get_addr_fd != NULL + && htab->tls_get_addr_fd->elf.plt.plist != NULL + && !add_dynamic_entry (DT_PPC64_TLSOPT, 0)) + return FALSE; + if (relocs) { if (!add_dynamic_entry (DT_RELA, 0) @@ -8396,6 +9157,7 @@ static inline enum ppc_stub_type ppc_type_of_stub (asection *input_sec, const Elf_Internal_Rela *rel, struct ppc_link_hash_entry **hash, + struct plt_entry **plt_ent, bfd_vma destination) { struct ppc_link_hash_entry *h = *hash; @@ -8406,35 +9168,50 @@ ppc_type_of_stub (asection *input_sec, if (h != NULL) { + struct plt_entry *ent; struct ppc_link_hash_entry *fdh = h; - if (fdh->oh != NULL - && fdh->oh->is_func_descriptor) - fdh = fdh->oh; + if (h->oh != NULL + && h->oh->is_func_descriptor) + { + 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) + { + *plt_ent = ent; + return ppc_stub_plt_call; + } + + /* Here, we know we don't have a plt entry. If we don't have a + 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 (!is_static_defined (&fdh->elf) + && !is_static_defined (&h->elf)) + return ppc_stub_none; + } + else if (elf_local_got_ents (input_sec->owner) != NULL) + { + Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (input_sec->owner); + struct plt_entry **local_plt = (struct plt_entry **) + elf_local_got_ents (input_sec->owner) + symtab_hdr->sh_info; + unsigned long r_symndx = ELF64_R_SYM (rel->r_info); - if (fdh->elf.dynindx != -1) + if (local_plt[r_symndx] != NULL) { struct plt_entry *ent; - for (ent = fdh->elf.plt.plist; ent != NULL; ent = ent->next) + for (ent = local_plt[r_symndx]; 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; } } - - /* Here, we know we don't have a plt entry. If we don't have a - 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)) - return ppc_stub_none; } /* Determine where the call point is. */ @@ -8541,6 +9318,49 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r) return p; } +/* Build a special .plt call stub for __tls_get_addr. */ + +#define LD_R11_0R3 0xe9630000 +#define LD_R12_0R3 0xe9830000 +#define MR_R0_R3 0x7c601b78 +#define CMPDI_R11_0 0x2c2b0000 +#define ADD_R3_R12_R13 0x7c6c6a14 +#define BEQLR 0x4d820020 +#define MR_R3_R0 0x7c030378 +#define MFLR_R11 0x7d6802a6 +#define STD_R11_0R1 0xf9610000 +#define BCTRL 0x4e800421 +#define LD_R11_0R1 0xe9610000 +#define LD_R2_0R1 0xe8410000 +#define MTLR_R11 0x7d6803a6 + +static inline bfd_byte * +build_tls_get_addr_stub (bfd *obfd, bfd_byte *p, int offset, + Elf_Internal_Rela *r) +{ + bfd_put_32 (obfd, LD_R11_0R3 + 0, p), p += 4; + bfd_put_32 (obfd, LD_R12_0R3 + 8, p), p += 4; + bfd_put_32 (obfd, MR_R0_R3, p), p += 4; + bfd_put_32 (obfd, CMPDI_R11_0, p), p += 4; + bfd_put_32 (obfd, ADD_R3_R12_R13, p), p += 4; + bfd_put_32 (obfd, BEQLR, p), p += 4; + bfd_put_32 (obfd, MR_R3_R0, p), p += 4; + bfd_put_32 (obfd, MFLR_R11, p), p += 4; + bfd_put_32 (obfd, STD_R11_0R1 + 32, p), p += 4; + + if (r != NULL) + r[0].r_offset += 9 * 4; + p = build_plt_stub (obfd, p, offset, r); + bfd_put_32 (obfd, BCTRL, p - 4); + + bfd_put_32 (obfd, LD_R11_0R1 + 32, p), p += 4; + bfd_put_32 (obfd, LD_R2_0R1 + 40, p), p += 4; + bfd_put_32 (obfd, MTLR_R11, p), p += 4; + bfd_put_32 (obfd, BLR, p), p += 4; + + return p; +} + static Elf_Internal_Rela * get_relocs (asection *sec, int count) { @@ -8557,9 +9377,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; @@ -8576,16 +9400,18 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) struct ppc_link_hash_table *htab; bfd_byte *loc; bfd_byte *p; - struct plt_entry *ent; bfd_vma dest, off; int size; Elf_Internal_Rela *r; + asection *plt; /* Massage our args to the form they really have. */ stub_entry = (struct ppc_stub_hash_entry *) gen_entry; info = in_arg; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; /* Make a note of the offset within the stubs for this entry. */ stub_entry->stub_offset = stub_entry->stub_sec->size; @@ -8667,7 +9493,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) hashes[symndx] = &h->elf; r->r_info = ELF64_R_INFO (symndx, R_PPC64_REL24); if (h->oh != NULL && h->oh->is_func) - h = h->oh; + h = ppc_follow_link (h->oh); if (h->elf.root.u.def.section != stub_entry->target_section) /* H is an opd symbol. The addend must be zero. */ r->r_addend = 0; @@ -8832,47 +9658,65 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) break; case ppc_stub_plt_call: - /* Do the best we can for shared libraries built without - exporting ".foo" for each "foo". This can happen when symbol - versioning scripts strip all bar a subset of symbols. */ - if (stub_entry->h->oh != NULL - && stub_entry->h->oh->elf.root.type != bfd_link_hash_defined - && stub_entry->h->oh->elf.root.type != bfd_link_hash_defweak) - { - /* Point the symbol at the stub. There may be multiple stubs, - we don't really care; The main thing is to make this sym - defined somewhere. Maybe defining the symbol in the stub - section is a silly idea. If we didn't do this, htab->top_id - could disappear. */ - stub_entry->h->oh->elf.root.type = bfd_link_hash_defined; - stub_entry->h->oh->elf.root.u.def.section = stub_entry->stub_sec; - stub_entry->h->oh->elf.root.u.def.value = stub_entry->stub_offset; + if (stub_entry->h != NULL + && stub_entry->h->is_func_descriptor + && stub_entry->h->oh != NULL) + { + struct ppc_link_hash_entry *fh = ppc_follow_link (stub_entry->h->oh); + + /* If the old-ABI "dot-symbol" is undefined make it weak so + we don't get a link error from RELOC_FOR_GLOBAL_SYMBOL. + FIXME: We used to define the symbol on one of the call + stubs instead, which is why we test symbol section id + against htab->top_id in various places. Likely all + these checks could now disappear. */ + if (fh->elf.root.type == bfd_link_hash_undefined) + fh->elf.root.type = bfd_link_hash_undefweak; } /* Now build the stub. */ - dest = (bfd_vma) -1; - for (ent = stub_entry->h->elf.plt.plist; ent != NULL; ent = ent->next) - if (ent->addend == stub_entry->addend) - { - dest = ent->plt.offset; - break; - } + dest = stub_entry->plt_ent->plt.offset & ~1; if (dest >= (bfd_vma) -2) abort (); - dest &= ~ (bfd_vma) 1; - dest += (htab->plt->output_offset - + htab->plt->output_section->vma); + plt = htab->plt; + if (!htab->elf.dynamic_sections_created + || stub_entry->h == NULL + || stub_entry->h->elf.dynindx == -1) + plt = htab->iplt; + + dest += plt->output_offset + plt->output_section->vma; + + if (stub_entry->h == NULL + && (stub_entry->plt_ent->plt.offset & 1) == 0) + { + Elf_Internal_Rela rela; + bfd_byte *rl; + + rela.r_offset = dest; + rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL); + rela.r_addend = (stub_entry->target_value + + stub_entry->target_section->output_offset + + stub_entry->target_section->output_section->vma); + + rl = (htab->reliplt->contents + + (htab->reliplt->reloc_count++ + * sizeof (Elf64_External_Rela))); + bfd_elf64_swap_reloca_out (info->output_bfd, &rela, rl); + stub_entry->plt_ent->plt.offset |= 1; + } off = (dest - - elf_gp (htab->plt->output_section->owner) + - elf_gp (plt->output_section->owner) - htab->stub_group[stub_entry->id_sec->id].toc_off); if (off + 0x80008000 > 0xffffffff || (off & 7) != 0) { (*_bfd_error_handler) (_("linkage table error against `%s'"), - stub_entry->h->elf.root.root.string); + stub_entry->h != NULL + ? stub_entry->h->elf.root.root.string + : ""); bfd_set_error (bfd_error_bad_value); htab->stub_error = TRUE; return FALSE; @@ -8891,7 +9735,13 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) r[0].r_offset += 2; r[0].r_addend = dest; } - p = build_plt_stub (htab->stub_bfd, loc, off, r); + if (stub_entry->h != NULL + && (stub_entry->h == htab->tls_get_addr_fd + || stub_entry->h == htab->tls_get_addr) + && !htab->no_tls_get_addr_opt) + p = build_tls_get_addr_stub (htab->stub_bfd, loc, off, r); + else + p = build_plt_stub (htab->stub_bfd, loc, off, r); size = p - loc; break; @@ -8958,22 +9808,23 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) info = in_arg; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; if (stub_entry->stub_type == ppc_stub_plt_call) { - struct plt_entry *ent; - off = (bfd_vma) -1; - for (ent = stub_entry->h->elf.plt.plist; ent != NULL; ent = ent->next) - if (ent->addend == stub_entry->addend) - { - off = ent->plt.offset & ~(bfd_vma) 1; - break; - } + asection *plt; + off = stub_entry->plt_ent->plt.offset & ~(bfd_vma) 1; if (off >= (bfd_vma) -2) abort (); - off += (htab->plt->output_offset - + htab->plt->output_section->vma - - elf_gp (htab->plt->output_section->owner) + plt = htab->plt; + if (!htab->elf.dynamic_sections_created + || stub_entry->h == NULL + || stub_entry->h->elf.dynindx == -1) + plt = htab->iplt; + off += (plt->output_offset + + plt->output_section->vma + - elf_gp (plt->output_section->owner) - htab->stub_group[stub_entry->id_sec->id].toc_off); size = PLT_CALL_STUB_SIZE; @@ -8981,6 +9832,11 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) size -= 4; if (PPC_HA (off + 16) != PPC_HA (off)) size += 4; + if (stub_entry->h != NULL + && (stub_entry->h == htab->tls_get_addr_fd + || stub_entry->h == htab->tls_get_addr) + && !htab->no_tls_get_addr_opt) + size += 13 * 4; if (info->emitrelocations) { stub_entry->stub_sec->reloc_count @@ -9093,9 +9949,10 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) 0 when no stubs will be needed, and 1 on success. */ int -ppc64_elf_setup_section_lists (bfd *output_bfd, - struct bfd_link_info *info, - int no_multi_toc) +ppc64_elf_setup_section_lists + (struct bfd_link_info *info, + asection *(*add_stub_section) (const char *, asection *), + void (*layout_sections_again) (void)) { bfd *input_bfd; int top_id, top_index, id; @@ -9104,7 +9961,11 @@ ppc64_elf_setup_section_lists (bfd *output_bfd, bfd_size_type amt; struct ppc_link_hash_table *htab = ppc_hash_table (info); - htab->no_multi_toc = no_multi_toc; + if (htab == NULL) + return -1; + /* Stash our params away. */ + htab->add_stub_section = add_stub_section; + htab->layout_sections_again = layout_sections_again; if (htab->brlt == NULL) return 0; @@ -9133,12 +9994,10 @@ ppc64_elf_setup_section_lists (bfd *output_bfd, for (id = 0; id < 3; id++) htab->stub_group[id].toc_off = TOC_BASE_OFF; - elf_gp (output_bfd) = htab->toc_curr = ppc64_elf_toc (output_bfd); - /* We can't use output_bfd->section_count here to find the top output section index as some sections may have been removed, and strip_excluded_output_sections doesn't renumber the indices. */ - for (section = output_bfd->sections, top_index = 0; + for (section = info->output_bfd->sections, top_index = 0; section != NULL; section = section->next) { @@ -9156,41 +10015,314 @@ ppc64_elf_setup_section_lists (bfd *output_bfd, return 1; } +/* Set up for first pass at multitoc partitioning. */ + +void +ppc64_elf_start_multitoc_partition (struct bfd_link_info *info) +{ + struct ppc_link_hash_table *htab = ppc_hash_table (info); + + elf_gp (info->output_bfd) = ppc64_elf_toc (info->output_bfd); + htab->toc_curr = elf_gp (info->output_bfd); + htab->toc_bfd = NULL; + htab->toc_first_sec = NULL; +} + /* The linker repeatedly calls this function for each TOC input section and linker generated GOT section. Group input bfds such that the toc - within a group is less than 64k in size. Will break with cute linker - scripts that play games with dot in the output toc section. */ + within a group is less than 64k in size. */ -void +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, limit; - if (!htab->no_multi_toc) + if (htab == NULL) + return FALSE; + + if (!htab->second_toc_pass) { - bfd_vma addr = isec->output_offset + isec->output_section->vma; - bfd_vma off = addr - htab->toc_curr; + /* Keep track of the first .toc or .got section for this input bfd. */ + if (htab->toc_bfd != isec->owner) + { + htab->toc_bfd = isec->owner; + htab->toc_first_sec = isec; + } + + addr = isec->output_offset + isec->output_section->vma; + off = addr - htab->toc_curr; + 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); + htab->toc_curr = addr; + } + + /* toc_curr is the base address of this toc group. Set elf_gp + for the input section to be the offset relative to the + output toc base plus 0x8000. Making the input elf_gp an + offset allows us to move the toc as a whole without + recalculating input elf_gp. */ + off = htab->toc_curr - elf_gp (isec->output_section->owner); + off += TOC_BASE_OFF; + + /* Die if someone uses a linker script that doesn't keep input + file .toc and .got together. */ + if (elf_gp (isec->owner) != 0 + && elf_gp (isec->owner) != off) + return FALSE; + + elf_gp (isec->owner) = off; + return TRUE; + } - if (off + isec->size > 0x10000) - htab->toc_curr = addr; + /* During the second pass toc_first_sec points to the start of + a toc group, and toc_curr is used to track the old elf_gp. + We use toc_bfd to ensure we only look at each bfd once. */ + if (htab->toc_bfd == isec->owner) + return TRUE; + htab->toc_bfd = isec->owner; - elf_gp (isec->owner) = (htab->toc_curr - - elf_gp (isec->output_section->owner) - + TOC_BASE_OFF); + if (htab->toc_first_sec == NULL + || htab->toc_curr != elf_gp (isec->owner)) + { + htab->toc_curr = elf_gp (isec->owner); + htab->toc_first_sec = isec; } + addr = (htab->toc_first_sec->output_offset + + htab->toc_first_sec->output_section->vma); + off = addr - elf_gp (isec->output_section->owner) + TOC_BASE_OFF; + elf_gp (isec->owner) = off; + + return TRUE; } -/* Called after the last call to the above function. */ +/* Called via elf_link_hash_traverse to merge GOT entries for global + symbol H. */ -void -ppc64_elf_reinit_toc (bfd *output_bfd, struct bfd_link_info *info) +static bfd_boolean +merge_global_got (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED) +{ + if (h->root.type == bfd_link_hash_indirect) + return TRUE; + + if (h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + merge_got_entries (&h->got.glist); + + return TRUE; +} + +/* Called via elf_link_hash_traverse to allocate GOT entries for global + symbol H. */ + +static bfd_boolean +reallocate_got (struct elf_link_hash_entry *h, void *inf) +{ + struct got_entry *gent; + + if (h->root.type == bfd_link_hash_indirect) + return TRUE; + + if (h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + for (gent = h->got.glist; gent != NULL; gent = gent->next) + if (!gent->is_indirect) + allocate_got (h, (struct bfd_link_info *) inf, gent); + return TRUE; +} + +/* Called on the first multitoc pass after the last call to + ppc64_elf_next_toc_section. This function removes duplicate GOT + entries. */ + +bfd_boolean +ppc64_elf_layout_multitoc (struct bfd_link_info *info) { struct ppc_link_hash_table *htab = ppc_hash_table (info); + struct bfd *ibfd, *ibfd2; + bfd_boolean done_something; + + htab->multi_toc_needed = htab->toc_curr != elf_gp (info->output_bfd); - htab->multi_toc_needed = htab->toc_curr != elf_gp (output_bfd); + if (!htab->do_multi_toc) + return FALSE; + + /* Merge global sym got entries within a toc group. */ + elf_link_hash_traverse (&htab->elf, merge_global_got, info); + + /* And tlsld_got. */ + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + struct got_entry *ent, *ent2; + + if (!is_ppc64_elf (ibfd)) + continue; + + ent = ppc64_tlsld_got (ibfd); + if (!ent->is_indirect + && ent->got.offset != (bfd_vma) -1) + { + for (ibfd2 = ibfd->link_next; ibfd2 != NULL; ibfd2 = ibfd2->link_next) + { + if (!is_ppc64_elf (ibfd2)) + continue; - /* toc_curr tracks the TOC offset used for code sections below in - ppc64_elf_next_input_section. Start off at 0x8000. */ + ent2 = ppc64_tlsld_got (ibfd2); + if (!ent2->is_indirect + && ent2->got.offset != (bfd_vma) -1 + && elf_gp (ibfd2) == elf_gp (ibfd)) + { + ent2->is_indirect = TRUE; + ent2->got.ent = ent; + } + } + } + } + + /* Zap sizes of got sections. */ + htab->reliplt->rawsize = htab->reliplt->size; + htab->reliplt->size -= htab->got_reli_size; + htab->got_reli_size = 0; + + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + asection *got, *relgot; + + if (!is_ppc64_elf (ibfd)) + continue; + + got = ppc64_elf_tdata (ibfd)->got; + if (got != NULL) + { + got->rawsize = got->size; + got->size = 0; + relgot = ppc64_elf_tdata (ibfd)->relgot; + relgot->rawsize = relgot->size; + relgot->size = 0; + } + } + + /* Now reallocate the got, local syms first. We don't need to + allocate section contents again since we never increase size. */ + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + struct got_entry **lgot_ents; + struct got_entry **end_lgot_ents; + struct plt_entry **local_plt; + struct plt_entry **end_local_plt; + unsigned char *lgot_masks; + bfd_size_type locsymcount; + Elf_Internal_Shdr *symtab_hdr; + asection *s, *srel; + + 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; + local_plt = (struct plt_entry **) end_lgot_ents; + end_local_plt = local_plt + locsymcount; + lgot_masks = (unsigned char *) end_local_plt; + s = ppc64_elf_tdata (ibfd)->got; + srel = ppc64_elf_tdata (ibfd)->relgot; + for (; lgot_ents < end_lgot_ents; ++lgot_ents, ++lgot_masks) + { + struct got_entry *ent; + + for (ent = *lgot_ents; ent != NULL; ent = ent->next) + { + 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); + } + } + } + } + + elf_link_hash_traverse (&htab->elf, reallocate_got, info); + + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + struct got_entry *ent; + + if (!is_ppc64_elf (ibfd)) + continue; + + ent = ppc64_tlsld_got (ibfd); + if (!ent->is_indirect + && ent->got.offset != (bfd_vma) -1) + { + asection *s = ppc64_elf_tdata (ibfd)->got; + ent->got.offset = s->size; + s->size += 16; + if (info->shared) + { + asection *srel = ppc64_elf_tdata (ibfd)->relgot; + srel->size += sizeof (Elf64_External_Rela); + } + } + } + + done_something = htab->reliplt->rawsize != htab->reliplt->size; + if (!done_something) + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + asection *got; + + if (!is_ppc64_elf (ibfd)) + continue; + + got = ppc64_elf_tdata (ibfd)->got; + if (got != NULL) + { + done_something = got->rawsize != got->size; + if (done_something) + break; + } + } + + if (done_something) + (*htab->layout_sections_again) (); + + /* Set up for second pass over toc sections to recalculate elf_gp + on input sections. */ + htab->toc_bfd = NULL; + htab->toc_first_sec = NULL; + htab->second_toc_pass = TRUE; + return done_something; +} + +/* Called after second pass of multitoc partitioning. */ + +void +ppc64_elf_finish_multitoc_partition (struct bfd_link_info *info) +{ + struct ppc_link_hash_table *htab = ppc_hash_table (info); + + /* 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; } @@ -9205,10 +10337,10 @@ ppc64_elf_reinit_toc (bfd *output_bfd, 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) @@ -9220,176 +10352,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); - 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 - && 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; } @@ -9404,6 +10549,9 @@ ppc64_elf_next_input_section (struct bfd_link_info *info, asection *isec) { struct ppc_link_hash_table *htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + if ((isec->output_section->flags & SEC_CODE) != 0 && isec->output_section->index <= htab->top_index) { @@ -9432,23 +10580,55 @@ 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) - { - int ret = toc_adjusting_stub_needed (info, isec); - if (ret < 0) - return FALSE; - else - isec->makes_toc_func_call = ret & 1; - } + else if (!isec->call_check_done + && toc_adjusting_stub_needed (info, isec) < 0) + return FALSE; } /* 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. */ + pasting work, because the fragments generally don't use the TOC. */ 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; + } + /* 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 @@ -9497,7 +10677,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"), @@ -9506,7 +10687,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; @@ -9539,7 +10721,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) { @@ -9563,19 +10746,15 @@ group_sections (struct ppc_link_hash_table *htab, instruction. */ bfd_boolean -ppc64_elf_size_stubs (bfd *output_bfd, - struct bfd_link_info *info, - bfd_signed_vma group_size, - asection *(*add_stub_section) (const char *, asection *), - void (*layout_sections_again) (void)) +ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size) { bfd_size_type stub_group_size; bfd_boolean stubs_always_before_branch; struct ppc_link_hash_table *htab = ppc_hash_table (info); - /* Stash our params away. */ - htab->add_stub_section = add_stub_section; - htab->layout_sections_again = layout_sections_again; + if (htab == NULL) + return FALSE; + stubs_always_before_branch = group_size < 0; if (group_size < 0) stub_group_size = -group_size; @@ -9627,7 +10806,7 @@ ppc64_elf_size_stubs (bfd *output_bfd, /* If this section is a link-once section that will be discarded, then don't create any stubs. */ if (section->output_section == NULL - || section->output_section->owner != output_bfd) + || section->output_section->owner != info->output_bfd) continue; /* Get the relocs. */ @@ -9647,7 +10826,7 @@ ppc64_elf_size_stubs (bfd *output_bfd, enum ppc_stub_type stub_type; struct ppc_stub_hash_entry *stub_entry; asection *sym_sec, *code_sec; - bfd_vma sym_value; + bfd_vma sym_value, code_value; bfd_vma destination; bfd_boolean ok_dest; struct ppc_link_hash_entry *hash; @@ -9657,6 +10836,7 @@ ppc64_elf_size_stubs (bfd *output_bfd, char *stub_name; const asection *id_sec; struct _opd_sec_data *opd; + struct plt_entry *plt_ent; r_type = ELF64_R_TYPE (irela->r_info); r_indx = ELF64_R_SYM (irela->r_info); @@ -9703,7 +10883,7 @@ ppc64_elf_size_stubs (bfd *output_bfd, use the func descriptor sym instead if it is defined. */ if (hash->elf.root.root.string[0] == '.' - && (fdh = get_fdh (hash, htab)) != NULL) + && (fdh = lookup_fdh (hash, htab)) != NULL) { if (fdh->elf.root.type == bfd_link_hash_defined || fdh->elf.root.type == bfd_link_hash_defweak) @@ -9733,6 +10913,7 @@ ppc64_elf_size_stubs (bfd *output_bfd, } code_sec = sym_sec; + code_value = sym_value; opd = get_opd_info (sym_sec); if (opd != NULL) { @@ -9743,10 +10924,11 @@ ppc64_elf_size_stubs (bfd *output_bfd, long adjust = opd->adjust[sym_value / 8]; if (adjust == -1) continue; + code_value += adjust; sym_value += adjust; } dest = opd_entry_value (sym_sec, sym_value, - &code_sec, &sym_value); + &code_sec, &code_value); if (dest != (bfd_vma) -1) { destination = dest; @@ -9756,14 +10938,15 @@ ppc64_elf_size_stubs (bfd *output_bfd, entry. */ hash->elf.root.type = bfd_link_hash_defweak; hash->elf.root.u.def.section = code_sec; - hash->elf.root.u.def.value = sym_value; + hash->elf.root.u.def.value = code_value; } } } /* Determine what (if any) linker stub is needed. */ + plt_ent = NULL; stub_type = ppc_type_of_stub (section, irela, &hash, - destination); + &plt_ent, destination); if (stub_type != ppc_stub_plt_call) { @@ -9794,7 +10977,7 @@ ppc64_elf_size_stubs (bfd *output_bfd, && irela != internal_relocs) { /* Get tls info. */ - char *tls_mask; + unsigned char *tls_mask; if (!get_tls_mask (&tls_mask, NULL, NULL, &local_syms, irela - 1, input_bfd)) @@ -9836,9 +11019,18 @@ ppc64_elf_size_stubs (bfd *output_bfd, } stub_entry->stub_type = stub_type; - stub_entry->target_value = sym_value; - stub_entry->target_section = code_sec; + if (stub_type != ppc_stub_plt_call) + { + stub_entry->target_value = code_value; + stub_entry->target_section = code_sec; + } + else + { + stub_entry->target_value = sym_value; + stub_entry->target_section = sym_sec; + } stub_entry->h = hash; + stub_entry->plt_ent = plt_ent; stub_entry->addend = irela->r_addend; if (stub_entry->h != NULL) @@ -9924,13 +11116,13 @@ ppc64_elf_toc (bfd *obfd) /* The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The TOC starts where the first of these sections starts. */ s = bfd_get_section_by_name (obfd, ".got"); - if (s == NULL) + if (s == NULL || (s->flags & SEC_EXCLUDE) != 0) s = bfd_get_section_by_name (obfd, ".toc"); - if (s == NULL) + if (s == NULL || (s->flags & SEC_EXCLUDE) != 0) s = bfd_get_section_by_name (obfd, ".tocbss"); - if (s == NULL) + if (s == NULL || (s->flags & SEC_EXCLUDE) != 0) s = bfd_get_section_by_name (obfd, ".plt"); - if (s == NULL) + if (s == NULL || (s->flags & SEC_EXCLUDE) != 0) { /* This may happen for o references to TOC base (SYM@toc / TOC[tc0]) without a @@ -9943,21 +11135,23 @@ ppc64_elf_toc (bfd *obfd) /* Look for a likely section. We probably won't even be using TOCstart. */ for (s = obfd->sections; s != NULL; s = s->next) - if ((s->flags & (SEC_ALLOC | SEC_SMALL_DATA | SEC_READONLY)) + if ((s->flags & (SEC_ALLOC | SEC_SMALL_DATA | SEC_READONLY + | SEC_EXCLUDE)) == (SEC_ALLOC | SEC_SMALL_DATA)) break; if (s == NULL) for (s = obfd->sections; s != NULL; s = s->next) - if ((s->flags & (SEC_ALLOC | SEC_SMALL_DATA)) + if ((s->flags & (SEC_ALLOC | SEC_SMALL_DATA | SEC_EXCLUDE)) == (SEC_ALLOC | SEC_SMALL_DATA)) break; if (s == NULL) for (s = obfd->sections; s != NULL; s = s->next) - if ((s->flags & (SEC_ALLOC | SEC_READONLY)) == SEC_ALLOC) + if ((s->flags & (SEC_ALLOC | SEC_READONLY | SEC_EXCLUDE)) + == SEC_ALLOC) break; if (s == NULL) for (s = obfd->sections; s != NULL; s = s->next) - if ((s->flags & SEC_ALLOC) == SEC_ALLOC) + if ((s->flags & (SEC_ALLOC | SEC_EXCLUDE)) == SEC_ALLOC) break; } @@ -9982,6 +11176,9 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms, bfd_byte *p; int stub_sec_count = 0; + if (htab == NULL) + return FALSE; + htab->emit_stub_syms = emit_stub_syms; /* Allocate memory to hold the linker stubs. */ @@ -10181,7 +11378,9 @@ void ppc64_elf_restore_symbols (struct bfd_link_info *info) { struct ppc_link_hash_table *htab = ppc_hash_table (info); - elf_link_hash_traverse (&htab->elf, undo_symbol_twiddle, info); + + if (htab != NULL) + elf_link_hash_traverse (&htab->elf, undo_symbol_twiddle, info); } /* What to do when ld finds relocations against symbols defined in @@ -10202,6 +11401,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. @@ -10249,7 +11505,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'. */ @@ -10261,6 +11519,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, ppc_howto_init (); htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; /* Don't relocate stub sections. */ if (input_section->owner == htab->stub_bfd) @@ -10273,6 +11533,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; @@ -10289,12 +11551,13 @@ ppc64_elf_relocate_section (bfd *output_bfd, const char *sym_name; unsigned long r_symndx, toc_symndx; bfd_vma toc_addend; - char tls_mask, tls_gd, tls_type; - char sym_type; + unsigned char tls_mask, tls_gd, tls_type; + unsigned char sym_type; bfd_vma relocation; bfd_boolean unresolved_reloc; bfd_boolean warned; - unsigned long insn, mask; + unsigned int insn; + bfd_vma mask; struct ppc_stub_hash_entry *stub_entry; bfd_vma max_br_offset; bfd_vma from; @@ -10386,8 +11649,10 @@ ppc64_elf_relocate_section (bfd *output_bfd, tls_mask = h->tls_mask; else if (local_got_ents != NULL) { - char *lgot_masks; - lgot_masks = (char *) (local_got_ents + symtab_hdr->sh_info); + struct plt_entry **local_plt = (struct plt_entry **) + (local_got_ents + symtab_hdr->sh_info); + unsigned char *lgot_masks = (unsigned char *) + (local_plt + symtab_hdr->sh_info); tls_mask = lgot_masks[r_symndx]; } if (tls_mask == 0 @@ -10396,7 +11661,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, || r_type == R_PPC64_TLSLD)) { /* Check for toc tls entries. */ - char *toc_tls; + unsigned char *toc_tls; if (!get_tls_mask (&toc_tls, &toc_symndx, &toc_addend, &local_syms, rel, input_bfd)) @@ -10408,7 +11673,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 @@ -10454,13 +11719,23 @@ 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: case R_PPC64_TOC16_LO_DS: { /* Check for toc tls entries. */ - char *toc_tls; + unsigned char *toc_tls; int retval; retval = get_tls_mask (&toc_tls, &toc_symndx, &toc_addend, @@ -10498,6 +11773,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 @@ -10527,37 +11814,10 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (tls_mask != 0 && (tls_mask & TLS_TPREL) == 0) { - bfd_vma rtra; insn = bfd_get_32 (output_bfd, contents + rel->r_offset); - if ((insn & ((0x3f << 26) | (31 << 11))) - == ((31 << 26) | (13 << 11))) - rtra = insn & ((1 << 26) - (1 << 16)); - else if ((insn & ((0x3f << 26) | (31 << 16))) - == ((31 << 26) | (13 << 16))) - rtra = (insn & (31 << 21)) | ((insn & (31 << 11)) << 5); - else - abort (); - if ((insn & ((1 << 11) - (1 << 1))) == 266 << 1) - /* add -> addi. */ - insn = 14 << 26; - else if ((insn & (31 << 1)) == 23 << 1 - && ((insn & (31 << 6)) < 14 << 6 - || ((insn & (31 << 6)) >= 16 << 6 - && (insn & (31 << 6)) < 24 << 6))) - /* load and store indexed -> dform. */ - insn = (32 | ((insn >> 6) & 31)) << 26; - else if ((insn & (31 << 1)) == 21 << 1 - && (insn & (0x1a << 6)) == 0) - /* ldx, ldux, stdx, stdux -> ld, ldu, std, stdu. */ - insn = (((58 | ((insn >> 6) & 4)) << 26) - | ((insn >> 6) & 1)); - else if ((insn & (31 << 1)) == 21 << 1 - && (insn & ((1 << 11) - (1 << 1))) == 341 << 1) - /* lwax -> lwa. */ - insn = (58 << 26) | 2; - else + insn = _bfd_elf_ppc_at_tls_transform (insn, 13); + if (insn == 0) abort (); - insn |= rtra; bfd_put_32 (output_bfd, insn, contents + rel->r_offset); /* Was PPC64_TLS which sits on insn boundary, now PPC64_TPREL16_LO which is at low-order half-word. */ @@ -10594,8 +11854,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); @@ -10638,8 +11898,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, insn1 |= 58 << 26; /* ld */ insn2 = 0x7c636a14; /* add 3,3,13 */ if (offset != (bfd_vma) -1) - rel[1].r_info = ELF64_R_INFO (ELF64_R_SYM (rel[1].r_info), - R_PPC64_NONE); + rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE); if ((tls_mask & TLS_EXPLICIT) == 0) r_type = (((r_type - (R_PPC64_GOT_TLSGD16 & 3)) & 3) + R_PPC64_GOT_TPREL16_DS); @@ -10663,9 +11922,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); @@ -10738,8 +11997,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, rel->r_info = ELF64_R_INFO (r_symndx, r_type); /* Zap the reloc on the _tls_get_addr call too. */ BFD_ASSERT (offset == rel[1].r_offset); - rel[1].r_info = ELF64_R_INFO (ELF64_R_SYM (rel[1].r_info), - R_PPC64_NONE); + rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE); insn3 = bfd_get_32 (output_bfd, contents + offset + 4); if (insn3 == NOP @@ -10772,9 +12030,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); @@ -10784,8 +12042,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, rel->r_offset = offset + d_offset; /* Zap the reloc on the _tls_get_addr call too. */ BFD_ASSERT (offset == rel[1].r_offset); - rel[1].r_info = ELF64_R_INFO (ELF64_R_SYM (rel[1].r_info), - R_PPC64_NONE); + rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE); insn2 = 0x38630000; /* addi 3,3,0 */ insn3 = bfd_get_32 (output_bfd, contents + offset + 4); @@ -10873,19 +12130,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 - && (((fdh = h->oh) != NULL - && fdh->elf.plt.plist != NULL) - || (fdh = h)->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))) - && (stub_entry = ppc_get_stub_entry (input_section, sec, fdh, - rel, htab)) != NULL + if (h != NULL + && h->oh != NULL + && h->oh->is_func_descriptor) + fdh = ppc_follow_link (h->oh); + 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)) @@ -10899,8 +12150,16 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (nop == NOP || nop == CROR_151515 || nop == CROR_313131) { - bfd_put_32 (input_bfd, LD_R2_40R1, - contents + rel->r_offset + 4); + if (h != NULL + && (h == htab->tls_get_addr_fd + || h == htab->tls_get_addr) + && !htab->no_tls_get_addr_opt) + { + /* Special stub used, leave nop alone. */ + } + else + bfd_put_32 (input_bfd, LD_R2_40R1, + contents + rel->r_offset + 4); can_plt_call = TRUE; } } @@ -10962,7 +12221,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. */ @@ -10983,13 +12244,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) { @@ -11030,6 +12293,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, checking whether the function is defined. */ else if (h != NULL && h->elf.root.type == bfd_link_hash_undefweak + && h->elf.dynindx == -1 && r_type == R_PPC64_REL24 && relocation == 0 && addend == 0) @@ -11107,14 +12371,14 @@ ppc64_elf_relocate_section (bfd *output_bfd, bfd_vma *offp; bfd_vma off; unsigned long indx = 0; + struct got_entry *ent; if (tls_type == (TLS_TLS | TLS_LD) && (h == NULL || !h->elf.def_dynamic)) - offp = &ppc64_tlsld_got (input_bfd)->offset; + ent = ppc64_tlsld_got (input_bfd); else { - struct got_entry *ent; if (h != NULL) { @@ -11122,7 +12386,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 @@ -11147,12 +12411,14 @@ ppc64_elf_relocate_section (bfd *output_bfd, && ent->owner == input_bfd && ent->tls_type == tls_type) break; - if (ent == NULL) - abort (); - offp = &ent->got.offset; } - got = ppc64_elf_tdata (input_bfd)->got; + if (ent == NULL) + abort (); + if (ent->is_indirect) + ent = ent->got.ent; + offp = &ent->got.offset; + got = ppc64_elf_tdata (ent->owner)->got; if (got == NULL) abort (); @@ -11167,14 +12433,24 @@ ppc64_elf_relocate_section (bfd *output_bfd, /* Generate relocs for the dynamic linker, except in the case of TLSLD where we'll use one entry per module. */ - asection *relgot = ppc64_elf_tdata (input_bfd)->relgot; + asection *relgot; + bfd_boolean ifunc; *offp = off | 1; + relgot = NULL; + ifunc = (h != NULL + ? h->elf.type == STT_GNU_IFUNC + : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC); if ((info->shared || indx != 0) - && (offp == &ppc64_tlsld_got (input_bfd)->offset - || h == NULL + && (h == NULL + || (tls_type == (TLS_TLS | TLS_LD) + && !h->elf.def_dynamic) || ELF_ST_VISIBILITY (h->elf.other) == STV_DEFAULT || h->elf.root.type != bfd_link_hash_undefweak)) + relgot = ppc64_elf_tdata (ent->owner)->relgot; + else if (ifunc) + relgot = htab->reliplt; + if (relgot != NULL) { outrel.r_offset = (got->output_section->vma + got->output_offset @@ -11201,9 +12477,14 @@ ppc64_elf_relocate_section (bfd *output_bfd, outrel.r_info = ELF64_R_INFO (indx, R_PPC64_DTPREL64); else if (tls_type == (TLS_TLS | TLS_TPREL)) outrel.r_info = ELF64_R_INFO (indx, R_PPC64_TPREL64); - else if (indx == 0) + else if (indx != 0) + outrel.r_info = ELF64_R_INFO (indx, R_PPC64_GLOB_DAT); + else { - outrel.r_info = ELF64_R_INFO (indx, R_PPC64_RELATIVE); + if (ifunc) + outrel.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE); + else + outrel.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE); /* Write the .got section contents for the sake of prelink. */ @@ -11211,8 +12492,6 @@ ppc64_elf_relocate_section (bfd *output_bfd, bfd_put_64 (output_bfd, outrel.r_addend + relocation, loc); } - else - outrel.r_info = ELF64_R_INFO (indx, R_PPC64_GLOB_DAT); if (indx == 0 && tls_type != (TLS_TLS | TLS_LD)) { @@ -11255,10 +12534,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (off >= (bfd_vma) -2) abort (); - relocation = got->output_offset + off; - - /* TOC base (r2) is TOC start plus 0x8000. */ - addend = -TOC_BASE_OFF; + relocation = got->output_section->vma + got->output_offset + off; + addend = -(TOCstart + htab->stub_group[input_section->id].toc_off); } break; @@ -11297,7 +12574,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) ; @@ -11331,6 +12608,12 @@ ppc64_elf_relocate_section (bfd *output_bfd, addend -= sec->output_section->vma; break; + case R_PPC64_REL16: + case R_PPC64_REL16_LO: + case R_PPC64_REL16_HI: + case R_PPC64_REL16_HA: + break; + case R_PPC64_REL14: case R_PPC64_REL14_BRNTAKEN: case R_PPC64_REL14_BRTAKEN: @@ -11347,6 +12630,22 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_TPREL16_HIGHERA: case R_PPC64_TPREL16_HIGHEST: case R_PPC64_TPREL16_HIGHESTA: + if (h != NULL + && h->elf.root.type == bfd_link_hash_undefweak + && h->elf.dynindx == -1) + { + /* Make this relocation against an undefined weak symbol + resolve to zero. This is really just a tweak, since + code using weak externs ought to check that they are + defined before using them. */ + bfd_byte *p = contents + rel->r_offset - d_offset; + + insn = bfd_get_32 (output_bfd, p); + insn = _bfd_elf_ppc_at_tprel_transform (insn, 13); + if (insn != 0) + bfd_put_32 (output_bfd, insn, p); + break; + } addend -= htab->elf.tls_sec->vma + TP_OFFSET; if (info->shared) /* The TPREL16 relocs shouldn't really be used in shared @@ -11423,12 +12722,14 @@ ppc64_elf_relocate_section (bfd *output_bfd, && h != NULL && h->elf.dynindx != -1 && !h->elf.non_got_ref - && !h->elf.def_regular)) + && !h->elf.def_regular) + || (!info->shared + && (h != NULL + ? h->elf.type == STT_GNU_IFUNC + : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC))) { - Elf_Internal_Rela outrel; bfd_boolean skip, relocate; asection *sreloc; - bfd_byte *loc; bfd_vma out_off; /* When generating a dynamic object, these relocations @@ -11462,7 +12763,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); @@ -11488,19 +12789,42 @@ ppc64_elf_relocate_section (bfd *output_bfd, entry in this lib. */ unresolved_reloc = FALSE; } - outrel.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE); + if (!is_opd + && r_type == R_PPC64_ADDR64 + && (h != NULL + ? h->elf.type == STT_GNU_IFUNC + : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)) + outrel.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE); + else + { + outrel.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE); - /* We need to relocate .opd contents for ld.so. - Prelink also wants simple and consistent rules - for relocs. This make all RELATIVE relocs have - *r_offset equal to r_addend. */ - relocate = TRUE; + /* We need to relocate .opd contents for ld.so. + Prelink also wants simple and consistent rules + for relocs. This make all RELATIVE relocs have + *r_offset equal to r_addend. */ + relocate = TRUE; + } } else { long indx = 0; - if (r_symndx == 0 || bfd_is_abs_section (sec)) + if (h != NULL + ? h->elf.type == STT_GNU_IFUNC + : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) + { + (*_bfd_error_handler) + (_("%B(%A+0x%lx): relocation %s for indirect " + "function %s unsupported"), + input_bfd, + input_section, + (long) rel->r_offset, + ppc64_elf_howto_table[r_type]->name, + sym_name); + ret = FALSE; + } + else if (r_symndx == STN_UNDEF || bfd_is_abs_section (sec)) ; else if (sec == NULL || sec->owner == NULL) { @@ -11538,6 +12862,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, } sreloc = elf_section_data (input_section)->sreloc; + if (!htab->elf.dynamic_sections_created) + sreloc = htab->reliplt; if (sreloc == NULL) abort (); @@ -11579,6 +12905,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_COPY: case R_PPC64_GLOB_DAT: case R_PPC64_JMP_SLOT: + case R_PPC64_JMP_IREL: case R_PPC64_RELATIVE: /* We shouldn't ever see these dynamic relocs in relocatable files. */ @@ -11604,6 +12931,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) { @@ -11611,6 +13032,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, break; case R_PPC64_ADDR16_HA: + case R_PPC64_REL16_HA: case R_PPC64_ADDR16_HIGHERA: case R_PPC64_ADDR16_HIGHESTA: case R_PPC64_TOC16_HA: @@ -11755,6 +13177,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 @@ -11821,37 +13260,49 @@ ppc64_elf_finish_dynamic_symbol (bfd *output_bfd, bfd_byte *loc; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; for (ent = h->plt.plist; ent != NULL; ent = ent->next) if (ent->plt.offset != (bfd_vma) -1) { /* This symbol has an entry in the procedure linkage table. Set it up. */ - - if (htab->plt == NULL - || htab->relplt == NULL - || htab->glink == NULL) - abort (); - - /* Create a JMP_SLOT reloc to inform the dynamic linker to - fill in the PLT entry. */ - rela.r_offset = (htab->plt->output_section->vma - + htab->plt->output_offset - + ent->plt.offset); - rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_JMP_SLOT); - rela.r_addend = ent->addend; - - loc = htab->relplt->contents; - loc += ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE) / PLT_ENTRY_SIZE - * sizeof (Elf64_External_Rela)); + if (!htab->elf.dynamic_sections_created + || h->dynindx == -1) + { + BFD_ASSERT (h->type == STT_GNU_IFUNC + && h->def_regular + && (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak)); + rela.r_offset = (htab->iplt->output_section->vma + + htab->iplt->output_offset + + ent->plt.offset); + rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL); + rela.r_addend = (h->root.u.def.value + + h->root.u.def.section->output_offset + + h->root.u.def.section->output_section->vma + + ent->addend); + loc = (htab->reliplt->contents + + (htab->reliplt->reloc_count++ + * sizeof (Elf64_External_Rela))); + } + else + { + rela.r_offset = (htab->plt->output_section->vma + + htab->plt->output_offset + + ent->plt.offset); + rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_JMP_SLOT); + rela.r_addend = ent->addend; + loc = (htab->relplt->contents + + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE) + / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela)))); + } bfd_elf64_swap_reloca_out (output_bfd, &rela, loc); } if (h->needs_copy) { - Elf_Internal_Rela rela; - bfd_byte *loc; - /* This symbol needs a copy reloc. Set it up. */ if (h->dynindx == -1 @@ -11910,6 +13361,9 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd, asection *sdyn; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + dynobj = htab->elf.dynobj; sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); @@ -12023,7 +13477,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; @@ -12032,7 +13486,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;