X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=bfd%2Felf64-ppc.c;h=4560599ec844c934dbfe9fad16e427beae4aac5f;hb=0eb7c19fef336c75c38c762dbe7ad84585fbf383;hp=a181c282763601286ba3cbc1fc9c44116e2644ad;hpb=ac4320bcc290e9367b40ca0fae50c832c447a752;p=pf3gnuchains%2Fpf3gnuchains4x.git diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index a181c28276..4560599ec8 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, 2011 Free Software Foundation, Inc. Written by Linus Nordberg, Swox AB , based on elf32-ppc.c by Ian Lance Taylor. - Largely rewritten by Alan Modra + Largely rewritten by Alan Modra. This file is part of BFD, the Binary File Descriptor library. @@ -34,6 +34,7 @@ #include "elf-bfd.h" #include "elf/ppc64.h" #include "elf64-ppc.h" +#include "dwarf2.h" static bfd_reloc_status_type ppc64_elf_ha_reloc (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); @@ -61,6 +62,7 @@ static bfd_vma opd_entry_value #define TARGET_BIG_SYM bfd_elf64_powerpc_vec #define TARGET_BIG_NAME "elf64-powerpc" #define ELF_ARCH bfd_arch_powerpc +#define ELF_TARGET_ID PPC64_ELF_DATA #define ELF_MACHINE_CODE EM_PPC64 #define ELF_MAXPAGESIZE 0x10000 #define ELF_COMMONPAGESIZE 0x1000 @@ -78,12 +80,13 @@ static bfd_vma opd_entry_value #define bfd_elf64_mkobject ppc64_elf_mkobject #define bfd_elf64_bfd_reloc_type_lookup ppc64_elf_reloc_type_lookup -#define bfd_elf64_bfd_reloc_name_lookup ppc64_elf_reloc_name_lookup -#define bfd_elf64_bfd_merge_private_bfd_data ppc64_elf_merge_private_bfd_data +#define bfd_elf64_bfd_reloc_name_lookup ppc64_elf_reloc_name_lookup +#define bfd_elf64_bfd_merge_private_bfd_data _bfd_generic_verify_endian_match #define bfd_elf64_new_section_hook ppc64_elf_new_section_hook #define bfd_elf64_bfd_link_hash_table_create ppc64_elf_link_hash_table_create #define bfd_elf64_bfd_link_hash_table_free ppc64_elf_link_hash_table_free #define bfd_elf64_get_synthetic_symtab ppc64_elf_get_synthetic_symtab +#define bfd_elf64_bfd_link_just_syms ppc64_elf_link_just_syms #define elf_backend_object_p ppc64_elf_object_p #define elf_backend_grok_prstatus ppc64_elf_grok_prstatus @@ -1279,6 +1282,20 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { 0, /* dst_mask */ FALSE), /* pcrel_offset */ + HOWTO (R_PPC64_TOCSAVE, + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_PPC64_TOCSAVE", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* Computes the load module index of the load module that contains the definition of its TLS sym. */ HOWTO (R_PPC64_DTPMOD64, @@ -2526,6 +2543,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; @@ -2538,15 +2603,20 @@ 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 : 1; + + /* Set if toc/got ha relocs detected not using r2, or lo reloc + instruction not one we handle. */ + unsigned int unexpected_toc_insn : 1; }; #define ppc64_elf_tdata(bfd) \ @@ -2557,7 +2627,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. */ @@ -2565,7 +2635,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 @@ -2602,7 +2672,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; @@ -2619,6 +2689,8 @@ ppc64_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) if (note->descsz != 136) return FALSE; + elf_tdata (abfd)->core_pid + = bfd_get_32 (abfd, note->descdata + 24); elf_tdata (abfd)->core_program = _bfd_elfcore_strndup (abfd, note->descdata + 40, 16); elf_tdata (abfd)->core_command @@ -2674,35 +2746,6 @@ ppc64_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_type, } } -/* Merge backend specific data from an object file to the output - object file when linking. */ - -static bfd_boolean -ppc64_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) -{ - /* Check if we have the same endianess. */ - if (ibfd->xvec->byteorder != obfd->xvec->byteorder - && ibfd->xvec->byteorder != BFD_ENDIAN_UNKNOWN - && obfd->xvec->byteorder != BFD_ENDIAN_UNKNOWN) - { - const char *msg; - - if (bfd_big_endian (ibfd)) - msg = _("%B: compiled for a big endian system " - "and target is little endian"); - else - msg = _("%B: compiled for a little endian system " - "and target is big endian"); - - (*_bfd_error_handler) (msg, ibfd); - - bfd_set_error (bfd_error_wrong_format); - return FALSE; - } - - return TRUE; -} - /* Add extra PPC sections. */ static const struct bfd_elf_special_section ppc64_elf_special_sections[]= @@ -3266,8 +3309,9 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd, { if (sec->vma > ent) break; - if ((sec->flags & SEC_ALLOC) == 0 - || (sec->flags & SEC_LOAD) == 0) + /* SEC_LOAD may not be set if SEC is from a separate debug + info file. */ + if ((sec->flags & SEC_ALLOC) == 0) break; if ((sec->flags & SEC_CODE) != 0) s->section = sec; @@ -3433,70 +3477,6 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd, calls may use the function descriptor symbol, ie. "bl foo". This behaves exactly as "bl .foo". */ -/* The linker needs to keep track of the number of relocs that it - decides to copy as dynamic relocs in check_relocs for each symbol. - This is so that it can later discard them if they are found to be - unnecessary. We store the information in a field extending the - regular ELF linker hash table. */ - -struct ppc_dyn_relocs -{ - struct ppc_dyn_relocs *next; - - /* The input section of the reloc. */ - asection *sec; - - /* Total number of relocs copied for the input section. */ - bfd_size_type count; - - /* Number of pc-relative relocs copied for the input section. */ - 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. */ @@ -3663,7 +3643,7 @@ struct ppc_link_hash_entry } u; /* Track dynamic relocs copied for this symbol. */ - struct ppc_dyn_relocs *dyn_relocs; + struct elf_dyn_relocs *dyn_relocs; /* Link between function code and descriptor symbols. */ struct ppc_link_hash_entry *oh; @@ -3697,7 +3677,7 @@ struct ppc_link_hash_entry #define TLS_EXPLICIT 32 /* Marks TOC section TLS relocs. */ #define TLS_TPRELGD 64 /* TPREL reloc resulting from GD->IE. */ #define PLT_IFUNC 128 /* STT_GNU_IFUNC. */ - char tls_mask; + unsigned char tls_mask; }; /* ppc64 ELF linker hash table. */ @@ -3712,6 +3692,9 @@ struct ppc_link_hash_table /* Another hash table for plt_branch stubs. */ struct bfd_hash_table branch_hash_table; + /* Hash table for function prologue tocsave. */ + htab_t tocsave_htab; + /* Linker stub bfd. */ bfd *stub_bfd; @@ -3732,6 +3715,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; @@ -3757,17 +3742,24 @@ struct ppc_link_hash_table asection *sfpr; asection *brlt; asection *relbrlt; + asection *glink_eh_frame; /* Shortcut to .__tls_get_addr and __tls_get_addr. */ 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]; /* Number of stubs against global syms. */ unsigned long stub_globals; + /* Set if PLT call stubs should load r11. */ + unsigned int plt_static_chain:1; + /* Set if we should emit symbols for stubs. */ unsigned int emit_stub_syms:1; @@ -3775,8 +3767,10 @@ struct ppc_link_hash_table 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; @@ -3793,14 +3787,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 *) \ @@ -3932,6 +3941,26 @@ link_hash_newfunc (struct bfd_hash_entry *entry, return entry; } +struct tocsave_entry { + asection *sec; + bfd_vma offset; +}; + +static hashval_t +tocsave_htab_hash (const void *p) +{ + const struct tocsave_entry *e = (const struct tocsave_entry *) p; + return ((bfd_vma)(intptr_t) e->sec ^ e->offset) >> 3; +} + +static int +tocsave_htab_eq (const void *p1, const void *p2) +{ + const struct tocsave_entry *e1 = (const struct tocsave_entry *) p1; + const struct tocsave_entry *e2 = (const struct tocsave_entry *) p2; + return e1->sec == e2->sec && e1->offset == e2->offset; +} + /* Create a ppc64 ELF linker hash table. */ static struct bfd_link_hash_table * @@ -3945,7 +3974,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; @@ -3961,6 +3991,13 @@ ppc64_elf_link_hash_table_create (bfd *abfd) sizeof (struct ppc_branch_hash_entry))) return NULL; + htab->tocsave_htab = htab_try_create (1024, + tocsave_htab_hash, + tocsave_htab_eq, + NULL); + if (htab->tocsave_htab == NULL) + return NULL; + /* Initializing two fields of the union is just cosmetic. We really only care about glist, but when compiled on a 32-bit host the bfd_vma fields are larger. Setting the bfd_vma to zero makes @@ -3982,10 +4019,12 @@ ppc64_elf_link_hash_table_create (bfd *abfd) static void ppc64_elf_link_hash_table_free (struct bfd_link_hash_table *hash) { - struct ppc_link_hash_table *ret = (struct ppc_link_hash_table *) hash; + struct ppc_link_hash_table *htab = (struct ppc_link_hash_table *) hash; - bfd_hash_table_free (&ret->stub_hash_table); - bfd_hash_table_free (&ret->branch_hash_table); + bfd_hash_table_free (&htab->stub_hash_table); + bfd_hash_table_free (&htab->branch_hash_table); + if (htab->tocsave_htab) + htab_delete (htab->tocsave_htab); _bfd_generic_link_hash_table_free (hash); } @@ -4002,6 +4041,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; } @@ -4103,8 +4144,9 @@ ppc_get_stub_entry (const asection *input_section, static struct ppc_stub_hash_entry * ppc_add_stub (const char *stub_name, asection *section, - struct ppc_link_hash_table *htab) + struct bfd_link_info *info) { + struct ppc_link_hash_table *htab = ppc_hash_table (info); asection *link_sec; asection *stub_sec; struct ppc_stub_hash_entry *stub_entry; @@ -4141,8 +4183,8 @@ ppc_add_stub (const char *stub_name, TRUE, FALSE); if (stub_entry == NULL) { - (*_bfd_error_handler) (_("%B: cannot create stub entry %s"), - section->owner, stub_name); + info->callbacks->einfo (_("%P: %B: cannot create stub entry %s\n"), + section->owner, stub_name); return NULL; } @@ -4161,6 +4203,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 @@ -4178,6 +4222,18 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info) || ! bfd_set_section_alignment (dynobj, htab->glink, 3)) return FALSE; + if (!info->no_ld_generated_unwind_info) + { + flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS + | SEC_IN_MEMORY | SEC_LINKER_CREATED); + htab->glink_eh_frame = bfd_make_section_anyway_with_flags (dynobj, + ".eh_frame", + flags); + if (htab->glink_eh_frame == NULL + || !bfd_set_section_alignment (abfd, htab->glink_eh_frame, 2)) + return FALSE; + } + flags = SEC_ALLOC | SEC_LINKER_CREATED; htab->iplt = bfd_make_section_anyway_with_flags (dynobj, ".iplt", flags); if (htab->iplt == NULL @@ -4229,6 +4285,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) { @@ -4270,6 +4328,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"); @@ -4355,19 +4416,38 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info, edir = (struct ppc_link_hash_entry *) dir; eind = (struct ppc_link_hash_entry *) ind; + 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. + We clear it ourselves for ELIMINATE_COPY_RELOCS. */ + if (!(ELIMINATE_COPY_RELOCS + && eind->elf.root.type != bfd_link_hash_indirect + && edir->elf.dynamic_adjusted)) + edir->elf.non_got_ref |= eind->elf.non_got_ref; + + edir->elf.ref_dynamic |= eind->elf.ref_dynamic; + edir->elf.ref_regular |= eind->elf.ref_regular; + edir->elf.ref_regular_nonweak |= eind->elf.ref_regular_nonweak; + edir->elf.needs_plt |= eind->elf.needs_plt; + /* Copy over any dynamic relocs we may have on the indirect sym. */ if (eind->dyn_relocs != NULL) { if (edir->dyn_relocs != NULL) { - struct ppc_dyn_relocs **pp; - struct ppc_dyn_relocs *p; + struct elf_dyn_relocs **pp; + struct elf_dyn_relocs *p; /* Add reloc counts against the indirect sym to the direct sym list. Merge any entries against the same section. */ for (pp = &eind->dyn_relocs; (p = *pp) != NULL; ) { - struct ppc_dyn_relocs *q; + struct elf_dyn_relocs *q; for (q = edir->dyn_relocs; q != NULL; q = q->next) if (q->sec == p->sec) @@ -4387,26 +4467,13 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info, eind->dyn_relocs = NULL; } - 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. - We clear it ourselves for ELIMINATE_COPY_RELOCS. */ - if (!(ELIMINATE_COPY_RELOCS - && eind->elf.root.type != bfd_link_hash_indirect - && edir->elf.dynamic_adjusted)) - edir->elf.non_got_ref |= eind->elf.non_got_ref; - - edir->elf.ref_dynamic |= eind->elf.ref_dynamic; - edir->elf.ref_regular |= eind->elf.ref_regular; - edir->elf.ref_regular_nonweak |= eind->elf.ref_regular_nonweak; - edir->elf.needs_plt |= eind->elf.needs_plt; - - /* If we were called to copy over info for a weak sym, that's all. */ + /* If we were called to copy over info for a weak sym, that's all. + You might think dyn_relocs need not be copied over; After all, + both syms will be dynamic or both non-dynamic so we're just + moving reloc accounting around. However, ELIMINATE_COPY_RELOCS + code in ppc64_elf_adjust_dynamic_symbol needs to check for + dyn_relocs in read-only sections, and it does so on what is the + DIR sym here. */ if (eind->elf.root.type != bfd_link_hash_indirect) return; @@ -4522,7 +4589,7 @@ make_fdh (struct bfd_link_info *info, function type. */ static bfd_boolean -ppc64_elf_add_symbol_hook (bfd *ibfd ATTRIBUTE_UNUSED, +ppc64_elf_add_symbol_hook (bfd *ibfd, struct bfd_link_info *info, Elf_Internal_Sym *isym, const char **name ATTRIBUTE_UNUSED, @@ -4530,12 +4597,19 @@ ppc64_elf_add_symbol_hook (bfd *ibfd ATTRIBUTE_UNUSED, asection **sec, bfd_vma *value ATTRIBUTE_UNUSED) { + if ((ibfd->flags & DYNAMIC) == 0 + && ELF_ST_BIND (isym->st_info) == STB_GNU_UNIQUE) + elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE; + if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) - elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE; + { + if ((ibfd->flags & DYNAMIC) == 0) + elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE; + } else if (ELF_ST_TYPE (isym->st_info) == STT_FUNC) ; else if (*sec != NULL - && strcmp (bfd_get_section_name (ibfd, *sec), ".opd") == 0) + && strcmp ((*sec)->name, ".opd") == 0) isym->st_info = ELF_ST_INFO (ELF_ST_BIND (isym->st_info), STT_FUNC); return TRUE; @@ -4602,6 +4676,9 @@ add_symbol_adjust (struct ppc_link_hash_entry *eh, struct bfd_link_info *info) abort (); htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + fdh = lookup_fdh (eh, htab); if (fdh == NULL) { @@ -4649,9 +4726,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)) { @@ -4690,17 +4769,41 @@ 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; } +/* If --just-symbols against a final linked binary, then assume we need + toc adjusting stubs when calling functions defined there. */ + +static void +ppc64_elf_link_just_syms (asection *sec, struct bfd_link_info *info) +{ + if ((sec->flags & SEC_CODE) != 0 + && (sec->owner->flags & (EXEC_P | DYNAMIC)) != 0 + && is_ppc64_elf (sec->owner)) + { + asection *got = bfd_get_section_by_name (sec->owner, ".got"); + if (got != NULL + && got->size >= elf_backend_got_header_size + && bfd_get_section_by_name (sec->owner, ".opd") != NULL) + sec->has_toc_reloc = 1; + } + _bfd_elf_link_just_syms (sec, info); +} + static struct plt_entry ** update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr, unsigned long r_symndx, bfd_vma r_addend, int tls_type) { struct got_entry **local_got_ents = elf_local_got_ents (abfd); struct plt_entry **local_plt; - char *local_got_tls_masks; + unsigned char *local_got_tls_masks; if (local_got_ents == NULL) { @@ -4734,6 +4837,7 @@ 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; } @@ -4741,7 +4845,7 @@ update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr, } local_plt = (struct plt_entry **) (local_got_ents + symtab_hdr->sh_info); - local_got_tls_masks = (char *) (local_plt + 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 local_plt + r_symndx; @@ -4793,7 +4897,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; @@ -4815,20 +4919,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 @@ -4966,6 +5068,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; @@ -4991,6 +5104,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; } @@ -5061,10 +5175,12 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, 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; @@ -5314,8 +5430,8 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, || (!info->shared && ifunc != NULL)) { - struct ppc_dyn_relocs *p; - struct ppc_dyn_relocs **head; + struct elf_dyn_relocs *p; + struct elf_dyn_relocs **head; /* We must copy these reloc types into the output file. Create a reloc section in dynobj and make room for @@ -5354,7 +5470,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, s = sec; vpp = &elf_section_data (s)->local_dynrel; - head = (struct ppc_dyn_relocs **) vpp; + head = (struct elf_dyn_relocs **) vpp; } p = *head; @@ -5401,9 +5517,12 @@ opd_entry_value (asection *opd_sec, /* No relocs implies we are linking a --just-symbols object. */ if (opd_sec->reloc_count == 0) { - if (!bfd_get_section_contents (opd_bfd, opd_sec, &val, offset, 8)) + char buf[8]; + + if (!bfd_get_section_contents (opd_bfd, opd_sec, buf, offset, 8)) return (bfd_vma) -1; + val = bfd_get_64 (opd_bfd, buf); if (code_sec != NULL) { asection *sec, *likely = NULL; @@ -5498,6 +5617,17 @@ opd_entry_value (asection *opd_sec, return val; } +/* Return true if symbol is defined in a regular object file. */ + +static bfd_boolean +is_static_defined (struct elf_link_hash_entry *h) +{ + return ((h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && h->root.u.def.section != NULL + && h->root.u.def.section->output_section != NULL); +} + /* If FDH is a function descriptor symbol, return the associated code entry symbol if it is defined. Return NULL otherwise. */ @@ -5539,6 +5669,9 @@ 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, *fh; @@ -5580,9 +5713,6 @@ ppc64_elf_gc_mark_dynamic_ref (struct elf_link_hash_entry *h, void *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. */ fdh = defined_func_desc (eh); if (fdh != NULL) @@ -5594,7 +5724,10 @@ ppc64_elf_gc_mark_dynamic_ref (struct elf_link_hash_entry *h, void *inf) || (!info->executable && eh->elf.def_regular && ELF_ST_VISIBILITY (eh->elf.other) != STV_INTERNAL - && ELF_ST_VISIBILITY (eh->elf.other) != STV_HIDDEN))) + && ELF_ST_VISIBILITY (eh->elf.other) != STV_HIDDEN + && (strchr (eh->elf.root.root.string, ELF_VER_CHR) != NULL + || !bfd_hide_sym_by_version (info->version_info, + eh->elf.root.root.string))))) { asection *code_sec; struct ppc_link_hash_entry *fh; @@ -5624,7 +5757,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) @@ -5683,7 +5816,7 @@ ppc64_elf_gc_mark_hook (asection *sec, break; default: - break; + return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); } } } @@ -5726,6 +5859,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); @@ -5736,15 +5872,15 @@ 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); if (r_symndx >= symtab_hdr->sh_info) { struct ppc_link_hash_entry *eh; - struct ppc_dyn_relocs **pp; - struct ppc_dyn_relocs *p; + struct elf_dyn_relocs **pp; + struct elf_dyn_relocs *p; h = sym_hashes[r_symndx - symtab_hdr->sh_info]; h = elf_follow_link (h); @@ -5771,7 +5907,7 @@ ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, { struct plt_entry **local_plt = (struct plt_entry **) (local_got_ents + symtab_hdr->sh_info); - char *local_got_tls_masks = (char *) + 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; @@ -5864,9 +6000,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; @@ -5891,7 +6025,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); @@ -5900,6 +6034,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; @@ -6121,11 +6258,10 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf) if (fh->elf.root.type == bfd_link_hash_indirect) return TRUE; - if (fh->elf.root.type == bfd_link_hash_warning) - fh = (struct ppc_link_hash_entry *) fh->elf.root.u.i.link; - 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. @@ -6264,6 +6400,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; @@ -6296,6 +6435,8 @@ 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 @@ -6354,7 +6495,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, if (ELIMINATE_COPY_RELOCS) { struct ppc_link_hash_entry * eh; - struct ppc_dyn_relocs *p; + struct elf_dyn_relocs *p; eh = (struct ppc_link_hash_entry *) h; for (p = eh->dyn_relocs; p != NULL; p = p->next) @@ -6380,9 +6521,9 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, function pointers, vtable refs and suchlike in read-only sections. Allow them to proceed, but warn that this might break at runtime. */ - (*_bfd_error_handler) - (_("copy reloc against `%s' requires lazy plt linking; " - "avoid setting LD_BIND_NOW=1 or upgrade gcc"), + info->callbacks->einfo + (_("%P: copy reloc against `%s' requires lazy plt linking; " + "avoid setting LD_BIND_NOW=1 or upgrade gcc\n"), h->root.root.string); } @@ -6391,8 +6532,8 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, if (h->size == 0) { - (*_bfd_error_handler) (_("dynamic variable `%s' is zero size"), - h->root.root.string); + info->callbacks->einfo (_("%P: dynamic variable `%s' is zero size\n"), + h->root.root.string); return TRUE; } @@ -6455,6 +6596,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; @@ -6487,7 +6631,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) @@ -6555,7 +6699,7 @@ 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); @@ -6563,7 +6707,7 @@ get_sym_h (struct elf_link_hash_entry **hp, { struct plt_entry **local_plt = (struct plt_entry **) (lgot_ents + symtab_hdr->sh_info); - char *lgot_masks = (char *) + unsigned char *lgot_masks = (unsigned char *) (local_plt + symtab_hdr->sh_info); tls_mask = &lgot_masks[r_symndx]; } @@ -6578,7 +6722,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, @@ -6598,6 +6742,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; @@ -6619,15 +6764,61 @@ 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; } +/* Find (or create) an entry in the tocsave hash table. */ + +static struct tocsave_entry * +tocsave_find (struct ppc_link_hash_table *htab, + enum insert_option insert, + Elf_Internal_Sym **local_syms, + const Elf_Internal_Rela *irela, + bfd *ibfd) +{ + unsigned long r_indx; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; + struct tocsave_entry ent, *p; + hashval_t hash; + struct tocsave_entry **slot; + + r_indx = ELF64_R_SYM (irela->r_info); + if (!get_sym_h (&h, &sym, &ent.sec, NULL, local_syms, r_indx, ibfd)) + return NULL; + if (ent.sec == NULL || ent.sec->output_section == NULL) + { + (*_bfd_error_handler) + (_("%B: undefined symbol on R_PPC64_TOCSAVE relocation")); + return NULL; + } + + if (h != NULL) + ent.offset = h->root.u.def.value; + else + ent.offset = sym->st_value; + ent.offset += irela->r_addend; + + hash = tocsave_htab_hash (&ent); + slot = ((struct tocsave_entry **) + htab_find_slot_with_hash (htab->tocsave_htab, &ent, hash, insert)); + if (slot == NULL) + return NULL; + + if (*slot == NULL) + { + p = (struct tocsave_entry *) bfd_alloc (ibfd, sizeof (*p)); + if (p == NULL) + return NULL; + *p = ent; + *slot = p; + } + return *slot; +} + /* Adjust all global syms defined in opd sections. In gcc generated code for the old ABI, these will already have been done. */ @@ -6641,9 +6832,6 @@ adjust_opd_syms (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; - if (h->root.type != bfd_link_hash_defined && h->root.type != bfd_link_hash_defweak) return TRUE; @@ -6693,8 +6881,8 @@ dec_dynrel_count (bfd_vma r_info, asection *sym_sec) { enum elf_ppc64_reloc_type r_type; - struct ppc_dyn_relocs *p; - struct ppc_dyn_relocs **pp; + struct elf_dyn_relocs *p; + struct elf_dyn_relocs **pp; /* Can this reloc be dynamic? This switch, and later tests here should be kept in sync with the code in check_relocs. */ @@ -6779,12 +6967,12 @@ dec_dynrel_count (bfd_vma r_info, if (sym_sec != NULL) { void *vpp = &elf_section_data (sym_sec)->local_dynrel; - pp = (struct ppc_dyn_relocs **) vpp; + pp = (struct elf_dyn_relocs **) vpp; } else { void *vpp = &elf_section_data (sec)->local_dynrel; - pp = (struct ppc_dyn_relocs **) vpp; + pp = (struct elf_dyn_relocs **) vpp; } /* elf_gc_sweep may have already removed all dyn relocs associated @@ -6808,8 +6996,8 @@ dec_dynrel_count (bfd_vma r_info, pp = &p->next; } - (*_bfd_error_handler) (_("dynreloc miscount for %B, section %A"), - sec->owner, sec); + info->callbacks->einfo (_("%P: dynreloc miscount for %B, section %A\n"), + sec->owner, sec); bfd_set_error (bfd_error_bad_value); return FALSE; } @@ -6821,8 +7009,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; @@ -6834,12 +7021,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; @@ -6856,7 +7045,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, @@ -6983,6 +7171,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; @@ -6992,7 +7181,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; @@ -7068,8 +7257,12 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, if (h != NULL && h->root.root.string[0] == '.') { - fdh = lookup_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) @@ -7158,9 +7351,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) @@ -7219,13 +7411,21 @@ 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, - int no_tls_get_addr_opt) +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)); @@ -7279,7 +7479,7 @@ ppc64_elf_tls_setup (bfd *obfd, _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr, opt_fd->dynstr_index); if (!bfd_elf_link_record_dynamic_symbol (info, opt_fd)) - return FALSE; + return NULL; } htab->tls_get_addr_fd = (struct ppc_link_hash_entry *) opt_fd; tga = &htab->tls_get_addr->elf; @@ -7306,7 +7506,7 @@ ppc64_elf_tls_setup (bfd *obfd, no_tls_get_addr_opt = TRUE; } htab->no_tls_get_addr_opt = no_tls_get_addr_opt; - return _bfd_elf_tls_setup (obfd, info); + return _bfd_elf_tls_setup (info->output_bfd, info); } /* Return TRUE iff REL is a branch reloc with a global symbol matching @@ -7343,34 +7543,40 @@ 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; struct ppc_link_hash_table *htab; + unsigned char *toc_ref; int pass; if (info->relocatable || !info->executable) return TRUE; htab = ppc_hash_table (info); - for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) - { - Elf_Internal_Sym *locsyms = NULL; - asection *toc = bfd_get_section_by_name (ibfd, ".toc"); - unsigned char *toc_ref = NULL; - - /* Look at all the sections for this file. Make two passes over - the relocs. On the first pass, mark toc entries involved - with tls relocs, and check that tls relocs involved in - setting up a tls_get_addr call are indeed followed by such a - call. If they are not, exclude them from the optimizations - done on the second pass. */ - for (pass = 0; pass < 2; ++pass) + if (htab == NULL) + return FALSE; + + /* Make two passes over the relocs. On the first pass, mark toc + entries involved with tls relocs, and check that tls relocs + involved in setting up a tls_get_addr call are indeed followed by + such a call. If they are not, we can't do any tls optimization. + On the second pass twiddle tls_mask flags to notify + relocate_section that optimization can be done, and adjust got + and plt refcounts. */ + toc_ref = NULL; + for (pass = 0; pass < 2; ++pass) + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + Elf_Internal_Sym *locsyms = NULL; + asection *toc = bfd_get_section_by_name (ibfd, ".toc"); + for (sec = ibfd->sections; sec != NULL; sec = sec->next) if (sec->has_tls_reloc && !bfd_is_abs_section (sec->output_section)) { Elf_Internal_Rela *relstart, *rel, *relend; + bfd_boolean found_tls_get_addr_arg = 0; /* Read the relocations. */ relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL, @@ -7386,12 +7592,13 @@ 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; int expecting_tls_get_addr = 0; + bfd_boolean ret = FALSE; r_symndx = ELF64_R_SYM (rel->r_info); if (!get_sym_h (&h, &sym, &sym_sec, &tls_mask, &locsyms, @@ -7406,15 +7613,21 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) && (elf_symtab_hdr (ibfd).contents != (unsigned char *) locsyms)) free (locsyms); - return FALSE; + return ret; } if (h != NULL) { - if (h->root.type != bfd_link_hash_defined - && h->root.type != bfd_link_hash_defweak) - continue; - value = h->root.u.def.value; + 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 + { + found_tls_get_addr_arg = 0; + continue; + } } else /* Symbols referenced by TLS relocs must be of type @@ -7427,19 +7640,48 @@ 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); + /* If this section has old-style __tls_get_addr calls + without marker relocs, then check that each + __tls_get_addr call reloc is preceded by a reloc + that conceivably belongs to the __tls_get_addr arg + setup insn. If we don't find matching arg setup + relocs, don't do any tls optimization. */ + if (pass == 0 + && sec->has_tls_get_addr_call + && h != NULL + && (h == &htab->tls_get_addr->elf + || h == &htab->tls_get_addr_fd->elf) + && !found_tls_get_addr_arg + && is_branch_reloc (r_type)) + { + info->callbacks->minfo (_("%H __tls_get_addr lost arg, " + "TLS optimization disabled\n"), + ibfd, sec, rel->r_offset); + ret = TRUE; + goto err_free_rel; + } + + found_tls_get_addr_arg = 0; switch (r_type) { case R_PPC64_GOT_TLSLD16: case R_PPC64_GOT_TLSLD16_LO: expecting_tls_get_addr = 1; + found_tls_get_addr_arg = 1; /* Fall thru */ case R_PPC64_GOT_TLSLD16_HI: @@ -7459,6 +7701,7 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: expecting_tls_get_addr = 1; + found_tls_get_addr_arg = 1; /* Fall thru */ case R_PPC64_GOT_TLSGD16_HI: @@ -7487,11 +7730,14 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) } continue; - case R_PPC64_TOC16: - case R_PPC64_TOC16_LO: - case R_PPC64_TLS: case R_PPC64_TLSGD: case R_PPC64_TLSLD: + found_tls_get_addr_arg = 1; + /* Fall thru */ + + case R_PPC64_TLS: + case R_PPC64_TOC16: + case R_PPC64_TOC16_LO: if (sym_sec == NULL || sym_sec != toc) continue; @@ -7500,18 +7746,17 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) case of R_PPC64_TLS, and after checking for tls_get_addr for the TOC16 relocs. */ if (toc_ref == NULL) - { - toc_ref = bfd_zmalloc (toc->size / 8); - if (toc_ref == NULL) - goto err_free_rel; - } + toc_ref = bfd_zmalloc (toc->output_section->rawsize / 8); + if (toc_ref == NULL) + goto err_free_rel; + if (h != NULL) value = h->root.u.def.value; else value = sym->st_value; value += rel->r_addend; BFD_ASSERT (value < toc->size && value % 8 == 0); - toc_ref_index = value / 8; + toc_ref_index = (value + toc->output_offset) / 8; if (r_type == R_PPC64_TLS || r_type == R_PPC64_TLSGD || r_type == R_PPC64_TLSLD) @@ -7532,7 +7777,7 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) if (pass == 0 || sec != toc || toc_ref == NULL - || !toc_ref[rel->r_offset / 8]) + || !toc_ref[(rel->r_offset + toc->output_offset) / 8]) continue; if (ok_tprel) { @@ -7547,7 +7792,7 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) if (pass == 0 || sec != toc || toc_ref == NULL - || !toc_ref[rel->r_offset / 8]) + || !toc_ref[(rel->r_offset + toc->output_offset) / 8]) continue; if (rel + 1 < relend && (rel[1].r_info @@ -7591,7 +7836,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, @@ -7599,8 +7844,13 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) rel, ibfd); if (retval == 0) goto err_free_rel; - if (retval > 1 && toc_tls != NULL) - toc_ref[toc_ref_index] = 1; + if (toc_tls != NULL) + { + if ((*toc_tls & (TLS_GD | TLS_LD)) != 0) + found_tls_get_addr_arg = 1; + if (retval > 1) + toc_ref[toc_ref_index] = 1; + } } continue; } @@ -7611,9 +7861,12 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) /* Uh oh, we didn't find the expected call. We could just mark this symbol to exclude it from tls optimization but it's safer to skip - the entire section. */ - sec->has_tls_reloc = 0; - break; + the entire optimization. */ + info->callbacks->minfo (_("%H arg lost __tls_get_addr, " + "TLS optimization disabled\n"), + ibfd, sec, rel->r_offset); + ret = TRUE; + goto err_free_rel; } if (expecting_tls_get_addr && htab->tls_get_addr != NULL) @@ -7699,18 +7952,18 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) free (relstart); } - if (toc_ref != NULL) - free (toc_ref); + if (locsyms != NULL + && (elf_symtab_hdr (ibfd).contents != (unsigned char *) locsyms)) + { + if (!info->keep_memory) + free (locsyms); + else + elf_symtab_hdr (ibfd).contents = (unsigned char *) locsyms; + } + } - if (locsyms != NULL - && (elf_symtab_hdr (ibfd).contents != (unsigned char *) locsyms)) - { - if (!info->keep_memory) - free (locsyms); - else - elf_symtab_hdr (ibfd).contents = (unsigned char *) locsyms; - } - } + if (toc_ref != NULL) + free (toc_ref); return TRUE; } @@ -7727,17 +7980,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; - - 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; + unsigned long i; if (h->root.type != bfd_link_hash_defined && h->root.type != bfd_link_hash_defweak) @@ -7749,16 +7999,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) @@ -7767,27 +8023,57 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf) return TRUE; } +/* Return TRUE iff INSN is one we expect on a _LO variety toc/got reloc. */ + +static bfd_boolean +ok_lo_toc_insn (unsigned int insn) +{ + return ((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)) + || (insn & (0x3f << 26)) == 12u << 26 /* addic */); +} + /* Examine all relocs referencing .toc sections in order to remove 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; + Elf_Internal_Rela *relstart, *rel, *toc_relocs; unsigned long *skip, *drop; unsigned char *used; unsigned char *keep, last, some_unused; + if (!is_ppc64_elf (ibfd)) + continue; + toc = bfd_get_section_by_name (ibfd, ".toc"); if (toc == NULL || toc->size == 0 @@ -7795,9 +8081,9 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) || elf_discarded_section (toc)) continue; + toc_relocs = NULL; local_syms = NULL; symtab_hdr = &elf_symtab_hdr (ibfd); - sym_hashes = elf_sym_hashes (ibfd); /* Look at sections dropped from the final link. */ skip = NULL; @@ -7865,18 +8151,101 @@ 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. */ + toc_relocs = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL, + info->keep_memory); + if (toc_relocs == NULL) + goto error_ret; + + for (rel = toc_relocs; rel < toc_relocs + toc->reloc_count; ++rel) + { + enum elf_ppc64_reloc_type r_type; + unsigned long r_symndx; + asection *sym_sec; + struct elf_link_hash_entry *h; + Elf_Internal_Sym *sym; + bfd_vma val, addr; + + r_type = ELF64_R_TYPE (rel->r_info); + if (r_type != R_PPC64_ADDR64) + continue; + + r_symndx = ELF64_R_SYM (rel->r_info); + if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms, + r_symndx, ibfd)) + goto error_ret; + + if (sym_sec == NULL + || elf_discarded_section (sym_sec)) + continue; + + if (!SYMBOL_CALLS_LOCAL (info, h)) + continue; + + if (h != NULL) + { + if (h->type == STT_GNU_IFUNC) + continue; + val = h->root.u.def.value; + } + else + { + if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) + continue; + val = sym->st_value; + } + val += rel->r_addend; + val += sym_sec->output_section->vma + sym_sec->output_offset; + + /* We don't yet know the exact toc pointer value, but we + know it will be somewhere in the toc section. Don't + optimize if the difference from any possible toc + pointer is outside [ff..f80008000, 7fff7fff]. */ + addr = toc->output_section->vma + TOC_BASE_OFF; + if (val - addr + (bfd_vma) 0x80008000 >= (bfd_vma) 1 << 32) + continue; + + addr = toc->output_section->vma + toc->output_section->rawsize; + if (val - addr + (bfd_vma) 0x80008000 >= (bfd_vma) 1 << 32) + continue; + + if (skip == NULL) + { + skip = bfd_zmalloc (sizeof (*skip) * (toc->size + 15) / 8); + if (skip == NULL) + goto error_ret; + } + + skip[rel->r_offset >> 3] + |= can_optimize | ((rel - toc_relocs) << 2); + } + } + if (skip == NULL) continue; @@ -7891,6 +8260,9 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) && relstart != NULL && elf_section_data (sec)->relocs != relstart) free (relstart); + if (toc_relocs != NULL + && elf_section_data (toc)->relocs != toc_relocs) + free (toc_relocs); if (skip != NULL) free (skip); return FALSE; @@ -7915,7 +8287,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; @@ -7930,10 +8303,66 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) struct elf_link_hash_entry *h; Elf_Internal_Sym *sym; bfd_vma val; + enum {no_check, check_lo, check_ha} insn_check; r_type = ELF64_R_TYPE (rel->r_info); switch (r_type) { + default: + insn_check = no_check; + 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: + insn_check = check_ha; + 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: + insn_check = check_lo; + break; + } + + if (insn_check != no_check) + { + bfd_vma off = rel->r_offset & ~3; + unsigned char buf[4]; + unsigned int insn; + + if (!bfd_get_section_contents (ibfd, sec, buf, off, 4)) + { + free (used); + goto error_ret; + } + insn = bfd_get_32 (ibfd, buf); + if (insn_check == check_lo + ? !ok_lo_toc_insn (insn) + : ((insn & ((0x3f << 26) | 0x1f << 16)) + != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)) + { + char str[12]; + + ppc64_elf_tdata (ibfd)->unexpected_toc_insn = 1; + sprintf (str, "%#08x", insn); + info->callbacks->einfo + (_("%P: %H: toc optimization is not supported for" + " %s instruction.\n"), + ibfd, sec, rel->r_offset & ~3, str); + } + } + + switch (r_type) + { case R_PPC64_TOC16: case R_PPC64_TOC16_LO: case R_PPC64_TOC16_HI: @@ -7968,12 +8397,40 @@ 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)) + { + free (used); + goto error_ret; + } + 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; @@ -7981,6 +8438,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 @@ -7992,13 +8452,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) + else if ((*drop & ref_from_discarded) != 0) { some_unused = 1; - last = 1; + last = ref_from_discarded; } else *drop = last; @@ -8010,6 +8472,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. */ @@ -8022,52 +8486,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 @@ -8075,7 +8507,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; @@ -8085,7 +8517,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) @@ -8108,41 +8540,100 @@ 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; + + if (h != NULL) + val = h->root.u.def.value; + else + { + val = sym->st_value; + if (val != 0) + local_toc_syms = TRUE; + } + + val += rel->r_addend; + + if (val > toc->rawsize) + val = toc->rawsize; + else if ((skip[val >> 3] & ref_from_discarded) != 0) + continue; + else if ((skip[val >> 3] & can_optimize) != 0) + { + Elf_Internal_Rela *tocrel + = toc_relocs + (skip[val >> 3] >> 2); + unsigned long tsym = ELF64_R_SYM (tocrel->r_info); + + switch (r_type) + { + case R_PPC64_TOC16_HA: + rel->r_info = ELF64_R_INFO (tsym, R_PPC64_TOC16_HA); + break; + + case R_PPC64_TOC16_LO_DS: + rel->r_info = ELF64_R_INFO (tsym, R_PPC64_LO_DS_OPT); + break; + + default: + if (!ppc64_elf_howto_table[R_PPC64_ADDR32]) + ppc_howto_init (); + info->callbacks->einfo + (_("%P: %H: %s relocation references " + "optimized away TOC entry\n"), + ibfd, sec, rel->r_offset, + ppc64_elf_howto_table[r_type]->name); + bfd_set_error (bfd_error_bad_value); + goto error_ret; + } + rel->r_addend = tocrel->r_addend; + elf_section_data (sec)->relocs = relstart; + continue; + } + + if (h != NULL || sym->st_value != 0) continue; - rel->r_addend -= skip[rel->r_addend >> 3]; + rel->r_addend -= skip[val >> 3]; + elf_section_data (sec)->relocs = relstart; } + + if (elf_section_data (sec)->relocs != relstart) + free (relstart); } /* We shouldn't have local or global symbols defined in the TOC, but handle them anyway. */ if (local_syms != NULL) - { - Elf_Internal_Sym *sym; + for (sym = local_syms; + sym < local_syms + symtab_hdr->sh_info; + ++sym) + if (sym->st_value != 0 + && bfd_section_from_elf_index (ibfd, sym->st_shndx) == toc) + { + unsigned long i; - for (sym = local_syms; - sym < local_syms + symtab_hdr->sh_info; - ++sym) - if (sym->st_value != 0 - && bfd_section_from_elf_index (ibfd, sym->st_shndx) == toc) - { - if (skip[sym->st_value >> 3] != (unsigned long) -1) - sym->st_value -= skip[sym->st_value >> 3]; - else - { + if (sym->st_value > toc->rawsize) + i = toc->rawsize >> 3; + else + i = sym->st_value >> 3; + + if ((skip[i] & (ref_from_discarded | can_optimize)) != 0) + { + if (local_toc_syms) (*_bfd_error_handler) - (_("%s defined in removed toc entry"), - bfd_elf_sym_name (ibfd, symtab_hdr, sym, - NULL)); - sym->st_value = 0; - sym->st_shndx = SHN_ABS; - } - symtab_hdr->contents = (unsigned char *) local_syms; - } - } + (_("%s defined on removed toc entry"), + bfd_elf_sym_name (ibfd, symtab_hdr, sym, NULL)); + do + ++i; + while ((skip[i] & (ref_from_discarded | can_optimize))); + sym->st_value = (bfd_vma) i << 3; + } + + sym->st_value -= skip[i]; + symtab_hdr->contents = (unsigned char *) local_syms; + } - /* Finally, adjust any global syms defined in the toc. */ + /* Adjust any global syms defined in this toc input section. */ if (toc_inf.global_toc_syms) { toc_inf.toc = toc; @@ -8151,22 +8642,125 @@ 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 (local_syms != NULL - && symtab_hdr->contents != (unsigned char *) local_syms) - { - if (!info->keep_memory) - free (local_syms); - else - symtab_hdr->contents = (unsigned char *) local_syms; - } - free (skip); - } + + if (toc->reloc_count != 0) + { + Elf_Internal_Shdr *rel_hdr; + Elf_Internal_Rela *wrel; + bfd_size_type sz; + + /* Remove unused toc relocs, and adjust those we keep. */ + if (toc_relocs == NULL) + toc_relocs = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL, + info->keep_memory); + if (toc_relocs == NULL) + goto error_ret; + + wrel = toc_relocs; + for (rel = toc_relocs; rel < toc_relocs + toc->reloc_count; ++rel) + if ((skip[rel->r_offset >> 3] + & (ref_from_discarded | can_optimize)) == 0) + { + wrel->r_offset = rel->r_offset - skip[rel->r_offset >> 3]; + wrel->r_info = rel->r_info; + wrel->r_addend = rel->r_addend; + ++wrel; + } + else if (!dec_dynrel_count (rel->r_info, toc, info, + &local_syms, NULL, NULL)) + goto error_ret; + + elf_section_data (toc)->relocs = toc_relocs; + toc->reloc_count = wrel - toc_relocs; + rel_hdr = _bfd_elf_single_rel_hdr (toc); + sz = rel_hdr->sh_entsize; + rel_hdr->sh_size = toc->reloc_count * sz; + } + } + else if (toc_relocs != NULL + && elf_section_data (toc)->relocs != toc_relocs) + free (toc_relocs); + + if (local_syms != NULL + && symtab_hdr->contents != (unsigned char *) local_syms) + { + if (!info->keep_memory) + free (local_syms); + else + symtab_hdr->contents = (unsigned char *) local_syms; + } + free (skip); + } 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. */ @@ -8177,17 +8771,16 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) struct ppc_link_hash_table *htab; asection *s; struct ppc_link_hash_entry *eh; - struct ppc_dyn_relocs *p; - struct got_entry *gent; + struct elf_dyn_relocs *p; + struct got_entry **pgent, *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; - 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 @@ -8275,12 +8868,31 @@ 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; - asection *rsec; + 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. */ @@ -8293,37 +8905,11 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) return FALSE; } - if ((gent->tls_type & TLS_LD) != 0 - && !h->def_dynamic) - { - ppc64_tlsld_got (gent->owner)->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; - rsec = NULL; - 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)) - rsec = ppc64_elf_tdata (gent->owner)->relgot; - else if (h->type == STT_GNU_IFUNC) - rsec = htab->reliplt; - if (rsec != NULL) - rsec->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 @@ -8346,7 +8932,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) then they should avoid writing weird assembly. */ if (SYMBOL_CALLS_LOCAL (info, h)) { - struct ppc_dyn_relocs **pp; + struct elf_dyn_relocs **pp; for (pp = &eh->dyn_relocs; (p = *pp) != NULL; ) { @@ -8429,10 +9015,7 @@ static bfd_boolean readonly_dynrelocs (struct elf_link_hash_entry *h, void *inf) { struct ppc_link_hash_entry *eh; - struct ppc_dyn_relocs *p; - - if (h->root.type == bfd_link_hash_warning) - h = (struct elf_link_hash_entry *) h->root.u.i.link; + struct elf_dyn_relocs *p; eh = (struct ppc_link_hash_entry *) h; for (p = eh->dyn_relocs; p != NULL; p = p->next) @@ -8463,8 +9046,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 (); @@ -8490,7 +9077,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, struct got_entry **end_lgot_ents; struct plt_entry **local_plt; struct plt_entry **end_local_plt; - char *lgot_masks; + unsigned char *lgot_masks; bfd_size_type locsymcount; Elf_Internal_Shdr *symtab_hdr; asection *srel; @@ -8500,7 +9087,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, for (s = ibfd->sections; s != NULL; s = s->next) { - struct ppc_dyn_relocs *p; + struct elf_dyn_relocs *p; for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next) { @@ -8533,20 +9120,21 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, end_lgot_ents = lgot_ents + locsymcount; local_plt = (struct plt_entry **) end_lgot_ents; end_local_plt = local_plt + locsymcount; - lgot_masks = (char *) end_local_plt; + 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 { @@ -8558,11 +9146,17 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, 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->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. */ @@ -8588,24 +9182,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. @@ -8628,7 +9237,13 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, /* Strip this section if we don't need it; see the comment below. */ } - else if (CONST_STRNEQ (bfd_get_section_name (dynobj, s), ".rela")) + else if (s == htab->glink_eh_frame) + { + if (!bfd_is_abs_section (s->output_section)) + /* Not sized yet. */ + continue; + } + else if (CONST_STRNEQ (s->name, ".rela")) { if (s->size != 0) { @@ -8793,13 +9408,15 @@ ppc_type_of_stub (asection *input_sec, struct ppc_link_hash_entry *fdh = h; if (h->oh != NULL && h->oh->is_func_descriptor) - fdh = ppc_follow_link (h->oh); + { + fdh = ppc_follow_link (h->oh); + *hash = fdh; + } for (ent = fdh->elf.plt.plist; ent != NULL; ent = ent->next) if (ent->addend == rel->r_addend && ent->plt.offset != (bfd_vma) -1) { - *hash = fdh; *plt_ent = ent; return ppc_stub_plt_call; } @@ -8808,12 +9425,8 @@ ppc_type_of_stub (asection *input_sec, either a defined function descriptor or a defined entry symbol in a regular object file, then it is pointless trying to make any other type of stub. */ - if (!((fdh->elf.root.type == bfd_link_hash_defined - || fdh->elf.root.type == bfd_link_hash_defweak) - && fdh->elf.root.u.def.section->output_section != NULL) - && !((h->elf.root.type == bfd_link_hash_defined - || h->elf.root.type == bfd_link_hash_defweak) - && h->elf.root.u.def.section->output_section != NULL)) + if (!is_static_defined (&fdh->elf) + && !is_static_defined (&h->elf)) return ppc_stub_none; } else if (elf_local_got_ents (input_sec->owner) != NULL) @@ -8861,7 +9474,8 @@ ppc_type_of_stub (asection *input_sec, /* Build a .plt call stub. */ static inline bfd_byte * -build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r) +build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r, + bfd_boolean plt_static_chain) { #define PPC_LO(v) ((v) & 0xffff) #define PPC_HI(v) (((v) >> 16) & 0xffff) @@ -8871,11 +9485,12 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r) { if (r != NULL) { + r[0].r_offset += 4; r[0].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_HA); - r[1].r_offset = r[0].r_offset + 8; + r[1].r_offset = r[0].r_offset + 4; r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS); r[1].r_addend = r[0].r_addend; - if (PPC_HA (offset + 16) != PPC_HA (offset)) + if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset)) { r[2].r_offset = r[1].r_offset + 4; r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO); @@ -8886,22 +9501,26 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r) r[2].r_offset = r[1].r_offset + 8; r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS); r[2].r_addend = r[0].r_addend + 8; - r[3].r_offset = r[2].r_offset + 4; - r[3].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS); - r[3].r_addend = r[0].r_addend + 16; + if (plt_static_chain) + { + r[3].r_offset = r[2].r_offset + 4; + r[3].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS); + r[3].r_addend = r[0].r_addend + 16; + } } } - bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (offset), p), p += 4; bfd_put_32 (obfd, STD_R2_40R1, p), p += 4; + bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (offset), p), p += 4; bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset), p), p += 4; - if (PPC_HA (offset + 16) != PPC_HA (offset)) + if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset)) { bfd_put_32 (obfd, ADDI_R12_R12 | PPC_LO (offset), p), p += 4; offset = 0; } bfd_put_32 (obfd, MTCTR_R11, p), p += 4; bfd_put_32 (obfd, LD_R2_0R12 | PPC_LO (offset + 8), p), p += 4; - bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset + 16), p), p += 4; + if (plt_static_chain) + bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset + 16), p), p += 4; bfd_put_32 (obfd, BCTR, p), p += 4; } else @@ -8910,7 +9529,7 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r) { r[0].r_offset += 4; r[0].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS); - if (PPC_HA (offset + 16) != PPC_HA (offset)) + if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset)) { r[1].r_offset = r[0].r_offset + 4; r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16); @@ -8920,21 +9539,25 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r) { r[1].r_offset = r[0].r_offset + 8; r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS); - r[1].r_addend = r[0].r_addend + 16; - r[2].r_offset = r[1].r_offset + 4; - r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS); - r[2].r_addend = r[0].r_addend + 8; + r[1].r_addend = r[0].r_addend + 8 + 8 * plt_static_chain; + if (plt_static_chain) + { + r[2].r_offset = r[1].r_offset + 4; + r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS); + r[2].r_addend = r[0].r_addend + 8; + } } } bfd_put_32 (obfd, STD_R2_40R1, p), p += 4; bfd_put_32 (obfd, LD_R11_0R2 | PPC_LO (offset), p), p += 4; - if (PPC_HA (offset + 16) != PPC_HA (offset)) + if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset)) { bfd_put_32 (obfd, ADDI_R2_R2 | PPC_LO (offset), p), p += 4; offset = 0; } bfd_put_32 (obfd, MTCTR_R11, p), p += 4; - bfd_put_32 (obfd, LD_R11_0R2 | PPC_LO (offset + 16), p), p += 4; + if (plt_static_chain) + bfd_put_32 (obfd, LD_R11_0R2 | PPC_LO (offset + 16), p), p += 4; bfd_put_32 (obfd, LD_R2_0R2 | PPC_LO (offset + 8), p), p += 4; bfd_put_32 (obfd, BCTR, p), p += 4; } @@ -8959,7 +9582,7 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r) static inline bfd_byte * build_tls_get_addr_stub (bfd *obfd, bfd_byte *p, int offset, - Elf_Internal_Rela *r) + Elf_Internal_Rela *r, bfd_boolean plt_static_chain) { bfd_put_32 (obfd, LD_R11_0R3 + 0, p), p += 4; bfd_put_32 (obfd, LD_R12_0R3 + 8, p), p += 4; @@ -8973,7 +9596,7 @@ build_tls_get_addr_stub (bfd *obfd, bfd_byte *p, int offset, if (r != NULL) r[0].r_offset += 9 * 4; - p = build_plt_stub (obfd, p, offset, r); + p = build_plt_stub (obfd, p, offset, r, plt_static_chain); bfd_put_32 (obfd, BCTRL, p - 4); bfd_put_32 (obfd, LD_R11_0R1 + 32, p), p += 4; @@ -9000,9 +9623,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; @@ -9010,6 +9637,38 @@ get_relocs (asection *sec, int count) return relocs; } +static bfd_vma +get_r2off (struct bfd_link_info *info, + struct ppc_stub_hash_entry *stub_entry) +{ + struct ppc_link_hash_table *htab = ppc_hash_table (info); + bfd_vma r2off = htab->stub_group[stub_entry->target_section->id].toc_off; + + if (r2off == 0) + { + /* Support linking -R objects. Get the toc pointer from the + opd entry. */ + char buf[8]; + asection *opd = stub_entry->h->elf.root.u.def.section; + bfd_vma opd_off = stub_entry->h->elf.root.u.def.value; + + if (strcmp (opd->name, ".opd") != 0 + || opd->reloc_count != 0) + { + info->callbacks->einfo (_("%P: cannot find opd entry toc for %s\n"), + stub_entry->h->elf.root.root.string); + bfd_set_error (bfd_error_bad_value); + return 0; + } + if (!bfd_get_section_contents (opd->owner, opd, buf, opd_off + 8, 8)) + return 0; + r2off = bfd_get_64 (opd->owner, buf); + r2off -= elf_gp (info->output_bfd); + } + r2off -= htab->stub_group[stub_entry->id_sec->id].toc_off; + return r2off; +} + static bfd_boolean ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) { @@ -9029,6 +9688,8 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) 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; @@ -9052,10 +9713,13 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) size = 4; if (stub_entry->stub_type == ppc_stub_long_branch_r2off) { - bfd_vma r2off; + bfd_vma r2off = get_r2off (info, stub_entry); - r2off = (htab->stub_group[stub_entry->target_section->id].toc_off - - htab->stub_group[stub_entry->id_sec->id].toc_off); + if (r2off == 0) + { + htab->stub_error = TRUE; + return FALSE; + } bfd_put_32 (htab->stub_bfd, STD_R2_40R1, loc); loc += 4; size = 12; @@ -9073,8 +9737,8 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (off + (1 << 25) >= (bfd_vma) (1 << 26)) { - (*_bfd_error_handler) (_("long branch stub `%s' offset overflow"), - stub_entry->root.string); + info->callbacks->einfo (_("%P: long branch stub `%s' offset overflow\n"), + stub_entry->root.string); htab->stub_error = TRUE; return FALSE; } @@ -9132,8 +9796,8 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) FALSE, FALSE); if (br_entry == NULL) { - (*_bfd_error_handler) (_("can't find branch stub `%s'"), - stub_entry->root.string); + info->callbacks->einfo (_("%P: can't find branch stub `%s'\n"), + stub_entry->root.string); htab->stub_error = TRUE; return FALSE; } @@ -9193,8 +9857,8 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (off + 0x80008000 > 0xffffffff || (off & 7) != 0) { - (*_bfd_error_handler) - (_("linkage table error against `%s'"), + info->callbacks->einfo + (_("%P: linkage table error against `%s'\n"), stub_entry->root.string); bfd_set_error (bfd_error_bad_value); htab->stub_error = TRUE; @@ -9239,10 +9903,14 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) } else { - bfd_vma r2off; + bfd_vma r2off = get_r2off (info, stub_entry); + + if (r2off == 0) + { + htab->stub_error = TRUE; + return FALSE; + } - r2off = (htab->stub_group[stub_entry->target_section->id].toc_off - - htab->stub_group[stub_entry->id_sec->id].toc_off); bfd_put_32 (htab->stub_bfd, STD_R2_40R1, loc); loc += 4; size = 20; @@ -9289,6 +9957,8 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) these checks could now disappear. */ if (fh->elf.root.type == bfd_link_hash_undefined) fh->elf.root.type = bfd_link_hash_undefweak; + /* Stop undo_symbol_twiddle changing it back to undefined. */ + fh->was_undefined = 0; } /* Now build the stub. */ @@ -9329,8 +9999,8 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (off + 0x80008000 > 0xffffffff || (off & 7) != 0) { - (*_bfd_error_handler) - (_("linkage table error against `%s'"), + info->callbacks->einfo + (_("%P: linkage table error against `%s'\n"), stub_entry->h != NULL ? stub_entry->h->elf.root.root.string : ""); @@ -9343,8 +10013,10 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (info->emitrelocations) { r = get_relocs (stub_entry->stub_sec, - (2 + (PPC_HA (off) != 0) - + (PPC_HA (off + 16) == PPC_HA (off)))); + (2 + + (PPC_HA (off) != 0) + + (htab->plt_static_chain + && PPC_HA (off + 16) == PPC_HA (off)))); if (r == NULL) return FALSE; r[0].r_offset = loc - stub_entry->stub_sec->contents; @@ -9356,9 +10028,11 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) && (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); + p = build_tls_get_addr_stub (htab->stub_bfd, loc, off, r, + htab->plt_static_chain); else - p = build_plt_stub (htab->stub_bfd, loc, off, r); + p = build_plt_stub (htab->stub_bfd, loc, off, r, + htab->plt_static_chain); size = p - loc; break; @@ -9425,6 +10099,8 @@ 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) { @@ -9443,9 +10119,11 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) - htab->stub_group[stub_entry->id_sec->id].toc_off); size = PLT_CALL_STUB_SIZE; + if (!htab->plt_static_chain) + size -= 4; if (PPC_HA (off) == 0) size -= 4; - if (PPC_HA (off + 16) != PPC_HA (off)) + if (PPC_HA (off + 8 + 8 * htab->plt_static_chain) != PPC_HA (off)) size += 4; if (stub_entry->h != NULL && (stub_entry->h == htab->tls_get_addr_fd @@ -9455,7 +10133,10 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (info->emitrelocations) { stub_entry->stub_sec->reloc_count - += 2 + (PPC_HA (off) != 0) + (PPC_HA (off + 16) == PPC_HA (off)); + += (2 + + (PPC_HA (off) != 0) + + (htab->plt_static_chain + && PPC_HA (off + 16) == PPC_HA (off))); stub_entry->stub_sec->flags |= SEC_RELOC; } } @@ -9480,8 +10161,12 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) size = 4; if (stub_entry->stub_type == ppc_stub_long_branch_r2off) { - r2off = (htab->stub_group[stub_entry->target_section->id].toc_off - - htab->stub_group[stub_entry->id_sec->id].toc_off); + r2off = get_r2off (info, stub_entry); + if (r2off == 0) + { + htab->stub_error = TRUE; + return FALSE; + } size = 12; if (PPC_HA (r2off) != 0) size = 16; @@ -9498,8 +10183,8 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) TRUE, FALSE); if (br_entry == NULL) { - (*_bfd_error_handler) (_("can't build branch stub `%s'"), - stub_entry->root.string); + info->callbacks->einfo (_("%P: can't build branch stub `%s'\n"), + stub_entry->root.string); htab->stub_error = TRUE; return FALSE; } @@ -9564,9 +10249,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; @@ -9575,7 +10261,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; @@ -9604,12 +10294,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) { @@ -9627,41 +10315,308 @@ 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 == NULL) + return FALSE; + + if (!htab->second_toc_pass) + { + /* 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; + } + + /* 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; + + 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 via elf_link_hash_traverse to merge GOT entries for global + symbol H. */ + +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; + + 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; + + 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; - if (!htab->no_multi_toc) + htab->multi_toc_needed = htab->toc_curr != elf_gp (info->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; + + 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) { - bfd_vma addr = isec->output_offset + isec->output_section->vma; - bfd_vma off = addr - htab->toc_curr; + asection *got, *relgot; - if (off + isec->size > 0x10000) - htab->toc_curr = addr; + if (!is_ppc64_elf (ibfd)) + continue; - elf_gp (isec->owner) = (htab->toc_curr - - elf_gp (isec->output_section->owner) - + TOC_BASE_OFF); + 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 the last call to the above function. */ +/* Called after second pass of multitoc partitioning. */ void -ppc64_elf_reinit_toc (bfd *output_bfd, struct bfd_link_info *info) +ppc64_elf_finish_multitoc_partition (struct bfd_link_info *info) { struct ppc_link_hash_table *htab = ppc_hash_table (info); - htab->multi_toc_needed = htab->toc_curr != elf_gp (output_bfd); - - /* toc_curr tracks the TOC offset used for code sections below in - ppc64_elf_next_input_section. Start off at 0x8000. */ + /* 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; } @@ -9676,10 +10631,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) @@ -9691,176 +10646,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 - && ppc_follow_link (eh->oh)->elf.plt.plist != NULL))) - { - ret = 1; - break; - } - - if (sym_sec == NULL) - /* Ignore other undefined symbols. */ - continue; + Elf_Internal_Rela *relstart, *rel; + Elf_Internal_Sym *local_syms; + struct ppc_link_hash_table *htab; - /* 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; - } + relstart = _bfd_elf_link_read_relocs (isec->owner, isec, NULL, NULL, + info->keep_memory); + if (relstart == NULL) + return -1; - 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; + /* Look for branches to outside of this section. */ + local_syms = NULL; + htab = ppc_hash_table (info); + if (htab == NULL) + return -1; - /* If this branch reloc uses an opd sym, find the code section. */ - opd = get_opd_info (sym_sec); - if (opd != NULL) + for (rel = relstart; rel < relstart + isec->reloc_count; ++rel) { - if (h == NULL && opd->adjust != NULL) + 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)) { - 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; } @@ -9875,6 +10843,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) { @@ -9903,23 +10874,78 @@ ppc64_elf_next_input_section (struct bfd_link_info *info, asection *isec) if (elf_gp (isec->owner) != 0) htab->toc_curr = elf_gp (isec->owner); } - else if (htab->stub_group[isec->id].toc_off == 0) + else { - int ret = toc_adjusting_stub_needed (info, isec); - if (ret < 0) + if (!isec->call_check_done + && toc_adjusting_stub_needed (info, isec) < 0) return FALSE; - else - isec->makes_toc_func_call = ret & 1; + /* If we make a local call from this section, ie. a branch + without a following nop, then we have no place to put a + toc restoring insn. We must use the same toc group as + the callee. + Testing makes_toc_func_call actually tests for *any* + calls to functions that need a good toc pointer. A more + precise test would be better, as this one will set + incorrect values for pasted .init/.fini fragments. + (Fixed later in check_pasted_section.) */ + if (isec->makes_toc_func_call + && elf_gp (isec->owner) != 0) + htab->toc_curr = elf_gp (isec->owner); } } /* Functions that don't use the TOC can belong in any TOC group. - Use the last TOC base. This happens to make _init and _fini - pasting work. */ + Use the last TOC base. */ htab->stub_group[isec->id].toc_off = htab->toc_curr; return TRUE; } +/* Check that all .init and .fini sections use the same toc, if they + have toc relocs. */ + +static bfd_boolean +check_pasted_section (struct bfd_link_info *info, const char *name) +{ + asection *o = bfd_get_section_by_name (info->output_bfd, name); + + if (o != NULL) + { + struct ppc_link_hash_table *htab = ppc_hash_table (info); + bfd_vma toc_off = 0; + asection *i; + + for (i = o->map_head.s; i != NULL; i = i->map_head.s) + if (i->has_toc_reloc) + { + if (toc_off == 0) + toc_off = htab->stub_group[i->id].toc_off; + else if (toc_off != htab->stub_group[i->id].toc_off) + return FALSE; + } + + if (toc_off == 0) + for (i = o->map_head.s; i != NULL; i = i->map_head.s) + if (i->makes_toc_func_call) + { + toc_off = htab->stub_group[i->id].toc_off; + break; + } + + /* Make sure the whole pasted function uses the same toc offset. */ + if (toc_off != 0) + for (i = o->map_head.s; i != NULL; i = i->map_head.s) + htab->stub_group[i->id].toc_off = toc_off; + } + return TRUE; +} + +bfd_boolean +ppc64_elf_check_init_fini (struct bfd_link_info *info) +{ + return (check_pasted_section (info, ".init") + & check_pasted_section (info, ".fini")); +} + /* See whether we can group stub sections together. Grouping stub sections may result in fewer stubs. More importantly, we need to put all .init* and .fini* stubs at the beginning of the .init or @@ -9968,7 +10994,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"), @@ -9977,7 +11004,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; @@ -10010,7 +11038,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) { @@ -10027,6 +11056,40 @@ group_sections (struct ppc_link_hash_table *htab, #undef PREV_SEC } +static const unsigned char glink_eh_frame_cie[] = +{ + 0, 0, 0, 16, /* length. */ + 0, 0, 0, 0, /* id. */ + 1, /* CIE version. */ + 'z', 'R', 0, /* Augmentation string. */ + 4, /* Code alignment. */ + 0x78, /* Data alignment. */ + 65, /* RA reg. */ + 1, /* Augmentation size. */ + DW_EH_PE_pcrel | DW_EH_PE_sdata4, /* FDE encoding. */ + DW_CFA_def_cfa, 1, 0 /* def_cfa: r1 offset 0. */ +}; + +/* Stripping output sections is normally done before dynamic section + symbols have been allocated. This function is called later, and + handles cases like htab->brlt which is mapped to its own output + section. */ + +static void +maybe_strip_output (struct bfd_link_info *info, asection *isec) +{ + if (isec->size == 0 + && isec->output_section->size == 0 + && !bfd_section_removed_from_list (info->output_bfd, + isec->output_section) + && elf_section_data (isec->output_section)->dynindx == 0) + { + isec->output_section->flags |= SEC_EXCLUDE; + bfd_section_list_remove (info->output_bfd, isec->output_section); + info->output_bfd->section_count--; + } +} + /* Determine and set the size of the stub section for a final link. The basic idea here is to examine all the relocations looking for @@ -10034,19 +11097,17 @@ 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_boolean plt_static_chain) { 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; + + htab->plt_static_chain = plt_static_chain; stubs_always_before_branch = group_size < 0; if (group_size < 0) stub_group_size = -group_size; @@ -10098,7 +11159,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. */ @@ -10269,7 +11330,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)) @@ -10278,6 +11339,14 @@ ppc64_elf_size_stubs (bfd *output_bfd, continue; } + if (stub_type == ppc_stub_plt_call + && irela + 1 < irelaend + && irela[1].r_offset == irela->r_offset + 4 + && ELF64_R_TYPE (irela[1].r_info) == R_PPC64_TOCSAVE + && !tocsave_find (htab, INSERT, + &local_syms, irela + 1, input_bfd)) + goto error_ret_free_internal; + /* Support for grouping stub sections. */ id_sec = htab->stub_group[section->id].link_sec; @@ -10295,7 +11364,7 @@ ppc64_elf_size_stubs (bfd *output_bfd, continue; } - stub_entry = ppc_add_stub (stub_name, section, htab); + stub_entry = ppc_add_stub (stub_name, section, info); if (stub_entry == NULL) { free (stub_name); @@ -10372,6 +11441,25 @@ ppc64_elf_size_stubs (bfd *output_bfd, htab->glink->flags |= SEC_RELOC; } + if (htab->glink_eh_frame != NULL + && !bfd_is_abs_section (htab->glink_eh_frame->output_section) + && (htab->glink_eh_frame->flags & SEC_EXCLUDE) == 0) + { + bfd_size_type size = 0; + + for (stub_sec = htab->stub_bfd->sections; + stub_sec != NULL; + stub_sec = stub_sec->next) + if ((stub_sec->flags & SEC_LINKER_CREATED) == 0) + size += 20; + if (htab->glink != NULL && htab->glink->size != 0) + size += 24; + if (size != 0) + size += sizeof (glink_eh_frame_cie); + htab->glink_eh_frame->rawsize = htab->glink_eh_frame->size; + htab->glink_eh_frame->size = size; + } + for (stub_sec = htab->stub_bfd->sections; stub_sec != NULL; stub_sec = stub_sec->next) @@ -10381,17 +11469,18 @@ ppc64_elf_size_stubs (bfd *output_bfd, /* Exit from this loop when no stubs have been added, and no stubs have changed size. */ - if (stub_sec == NULL) + if (stub_sec == NULL + && (htab->glink_eh_frame == NULL + || htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size)) break; /* Ask the linker to do its stuff. */ (*htab->layout_sections_again) (); } - /* It would be nice to strip htab->brlt from the output if the - section is empty, but it's too late. If we strip sections here, - the dynamic symbol table is corrupted since the section symbol - for the stripped section isn't written. */ + maybe_strip_output (info, htab->brlt); + if (htab->glink_eh_frame != NULL) + maybe_strip_output (info, htab->glink_eh_frame); return TRUE; } @@ -10468,6 +11557,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. */ @@ -10593,6 +11685,100 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms, return FALSE; } + if (htab->glink_eh_frame != NULL + && htab->glink_eh_frame->size != 0) + { + bfd_vma val; + + p = bfd_zalloc (htab->glink_eh_frame->owner, htab->glink_eh_frame->size); + if (p == NULL) + return FALSE; + htab->glink_eh_frame->contents = p; + + htab->glink_eh_frame->rawsize = htab->glink_eh_frame->size; + + memcpy (p, glink_eh_frame_cie, sizeof (glink_eh_frame_cie)); + /* CIE length (rewrite in case little-endian). */ + bfd_put_32 (htab->elf.dynobj, sizeof (glink_eh_frame_cie) - 4, p); + p += sizeof (glink_eh_frame_cie); + + for (stub_sec = htab->stub_bfd->sections; + stub_sec != NULL; + stub_sec = stub_sec->next) + if ((stub_sec->flags & SEC_LINKER_CREATED) == 0) + { + /* FDE length. */ + bfd_put_32 (htab->elf.dynobj, 16, p); + p += 4; + /* CIE pointer. */ + val = p - htab->glink_eh_frame->contents; + bfd_put_32 (htab->elf.dynobj, val, p); + p += 4; + /* Offset to stub section. */ + val = (stub_sec->output_section->vma + + stub_sec->output_offset); + val -= (htab->glink_eh_frame->output_section->vma + + htab->glink_eh_frame->output_offset); + val -= p - htab->glink_eh_frame->contents; + if (val + 0x80000000 > 0xffffffff) + { + info->callbacks->einfo + (_("%P: %s offset too large for .eh_frame sdata4 encoding"), + stub_sec->name); + return FALSE; + } + bfd_put_32 (htab->elf.dynobj, val, p); + p += 4; + /* stub section size. */ + bfd_put_32 (htab->elf.dynobj, stub_sec->rawsize, p); + p += 4; + /* Augmentation. */ + p += 1; + /* Pad. */ + p += 3; + } + if (htab->glink != NULL && htab->glink->size != 0) + { + /* FDE length. */ + bfd_put_32 (htab->elf.dynobj, 20, p); + p += 4; + /* CIE pointer. */ + val = p - htab->glink_eh_frame->contents; + bfd_put_32 (htab->elf.dynobj, val, p); + p += 4; + /* Offset to .glink. */ + val = (htab->glink->output_section->vma + + htab->glink->output_offset + + 8); + val -= (htab->glink_eh_frame->output_section->vma + + htab->glink_eh_frame->output_offset); + val -= p - htab->glink_eh_frame->contents; + if (val + 0x80000000 > 0xffffffff) + { + info->callbacks->einfo + (_("%P: %s offset too large for .eh_frame sdata4 encoding"), + htab->glink->name); + return FALSE; + } + bfd_put_32 (htab->elf.dynobj, val, p); + p += 4; + /* .glink size. */ + bfd_put_32 (htab->elf.dynobj, htab->glink->rawsize - 8, p); + p += 4; + /* Augmentation. */ + p += 1; + + *p++ = DW_CFA_advance_loc + 1; + *p++ = DW_CFA_register; + *p++ = 65; + *p++ = 12; + *p++ = DW_CFA_advance_loc + 4; + *p++ = DW_CFA_restore_extended; + *p++ = 65; + } + htab->glink_eh_frame->size = p - htab->glink_eh_frame->contents; + } + /* Build the stubs as directed by the stub hash table. */ bfd_hash_traverse (&htab->stub_hash_table, ppc_build_one_stub, info); @@ -10610,10 +11796,12 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms, } if (stub_sec != NULL - || htab->glink->rawsize != htab->glink->size) + || htab->glink->rawsize != htab->glink->size + || (htab->glink_eh_frame != NULL + && htab->glink_eh_frame->rawsize != htab->glink_eh_frame->size)) { htab->stub_error = TRUE; - (*_bfd_error_handler) (_("stubs don't match calculated size")); + info->callbacks->einfo (_("%P: stubs don't match calculated size\n")); } if (htab->stub_error) @@ -10652,9 +11840,6 @@ undo_symbol_twiddle (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; - eh = (struct ppc_link_hash_entry *) h; if (eh->elf.root.type != bfd_link_hash_undefweak || !eh->was_undefined) return TRUE; @@ -10667,7 +11852,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 @@ -10747,6 +11934,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) @@ -10775,12 +11964,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; + unsigned int mask; struct ppc_stub_hash_entry *stub_entry; bfd_vma max_br_offset; bfd_vma from; @@ -10847,16 +12037,10 @@ ppc64_elf_relocate_section (bfd *output_bfd, h = (struct ppc_link_hash_entry *) h_elf; if (sec != NULL && elf_discarded_section (sec)) - { - /* For relocs against symbols from removed linkonce sections, - or sections discarded by a linker script, we just want the - section contents zeroed. Avoid any special processing. */ - _bfd_clear_contents (ppc64_elf_howto_table[r_type], input_bfd, - contents + rel->r_offset); - rel->r_info = 0; - rel->r_addend = 0; - continue; - } + RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section, + rel, relend, + ppc64_elf_howto_table[r_type], + contents); if (info->relocatable) continue; @@ -10874,7 +12058,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, { struct plt_entry **local_plt = (struct plt_entry **) (local_got_ents + symtab_hdr->sh_info); - char *lgot_masks = (char *) + unsigned char *lgot_masks = (unsigned char *) (local_plt + symtab_hdr->sh_info); tls_mask = lgot_masks[r_symndx]; } @@ -10884,7 +12068,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)) @@ -10896,7 +12080,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 @@ -10913,13 +12097,11 @@ ppc64_elf_relocate_section (bfd *output_bfd, /* R_PPC64_TLS is OK against a symbol in the TOC. */ ; else - (*_bfd_error_handler) + info->callbacks->einfo (!IS_PPC64_TLS_RELOC (r_type) - ? _("%B(%A+0x%lx): %s used with TLS symbol %s") - : _("%B(%A+0x%lx): %s used with non-TLS symbol %s"), - input_bfd, - input_section, - (long) rel->r_offset, + ? _("%P: %H: %s used with TLS symbol %s\n") + : _("%P: %H: %s used with non-TLS symbol %s\n"), + input_bfd, input_section, rel->r_offset, ppc64_elf_howto_table[r_type]->name, sym_name); } @@ -10942,13 +12124,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, @@ -10986,6 +12178,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 @@ -11055,8 +12259,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); @@ -11123,9 +12327,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); @@ -11231,9 +12435,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); @@ -11307,6 +12511,21 @@ ppc64_elf_relocate_section (bfd *output_bfd, default: break; + case R_PPC64_TOCSAVE: + if (relocation + addend == (rel->r_offset + + input_section->output_offset + + input_section->output_section->vma) + && tocsave_find (htab, NO_INSERT, + &local_syms, rel, input_bfd)) + { + insn = bfd_get_32 (input_bfd, contents + rel->r_offset); + if (insn == NOP + || insn == CROR_151515 || insn == CROR_313131) + bfd_put_32 (input_bfd, STD_R2_40R1, + contents + rel->r_offset); + } + break; + /* Branch taken prediction relocations. */ case R_PPC64_ADDR14_BRTAKEN: case R_PPC64_REL14_BRTAKEN: @@ -11331,23 +12550,13 @@ ppc64_elf_relocate_section (bfd *output_bfd, linkage stubs needs to be followed by a nop, as the nop will be replaced with an instruction to restore the TOC base pointer. */ - stub_entry = NULL; fdh = h; if (h != NULL && h->oh != NULL && h->oh->is_func_descriptor) fdh = ppc_follow_link (h->oh); - if (((fdh != NULL - && fdh->elf.plt.plist != NULL) - || (sec != NULL - && sec->output_section != NULL - && sec->id <= htab->top_id - && (htab->stub_group[sec->id].toc_off - != htab->stub_group[input_section->id].toc_off)) - || (h == NULL - && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)) - && (stub_entry = ppc_get_stub_entry (input_section, sec, fdh, - rel, htab)) != NULL + stub_entry = ppc_get_stub_entry (input_section, sec, fdh, rel, htab); + if (stub_entry != NULL && (stub_entry->stub_type == ppc_stub_plt_call || stub_entry->stub_type == ppc_stub_plt_branch_r2off || stub_entry->stub_type == ppc_stub_long_branch_r2off)) @@ -11403,23 +12612,19 @@ ppc64_elf_relocate_section (bfd *output_bfd, ".init") == 0 || strcmp (input_section->output_section->name, ".fini") == 0) - (*_bfd_error_handler) - (_("%B(%A+0x%lx): automatic multiple TOCs " + info->callbacks->einfo + (_("%P: %H: automatic multiple TOCs " "not supported using your crt files; " - "recompile with -mminimal-toc or upgrade gcc"), - input_bfd, - input_section, - (long) rel->r_offset); + "recompile with -mminimal-toc or upgrade gcc\n"), + input_bfd, input_section, rel->r_offset); else - (*_bfd_error_handler) - (_("%B(%A+0x%lx): sibling call optimization to `%s' " + info->callbacks->einfo + (_("%P: %H: sibling call optimization to `%s' " "does not allow automatic multiple TOCs; " "recompile with -mminimal-toc or " "-fno-optimize-sibling-calls, " - "or make `%s' extern"), - input_bfd, - input_section, - (long) rel->r_offset, + "or make `%s' extern\n"), + input_bfd, input_section, rel->r_offset, sym_name, sym_name); bfd_set_error (bfd_error_bad_value); @@ -11432,7 +12637,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. */ @@ -11453,13 +12660,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) { @@ -11469,6 +12678,12 @@ ppc64_elf_relocate_section (bfd *output_bfd, + stub_entry->stub_sec->output_offset + stub_entry->stub_sec->output_section->vma); addend = 0; + + if (stub_entry->stub_type == ppc_stub_plt_call + && rel + 1 < relend + && rel[1].r_offset == rel->r_offset + 4 + && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOCSAVE) + relocation += 4; } if (insn != 0) @@ -11500,6 +12715,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) @@ -11515,8 +12731,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, switch (r_type) { default: - (*_bfd_error_handler) - (_("%B: unknown relocation type %d for symbol %s"), + info->callbacks->einfo + (_("%P: %B: unknown relocation type %d for symbol %s\n"), input_bfd, (int) r_type, sym_name); bfd_set_error (bfd_error_bad_value); @@ -11527,6 +12743,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_TLS: case R_PPC64_TLSGD: case R_PPC64_TLSLD: + case R_PPC64_TOCSAVE: case R_PPC64_GNU_VTINHERIT: case R_PPC64_GNU_VTENTRY: continue; @@ -11577,14 +12794,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) { @@ -11592,7 +12809,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 @@ -11617,12 +12834,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 (); @@ -11646,11 +12865,12 @@ ppc64_elf_relocate_section (bfd *output_bfd, ? 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 (input_bfd)->relgot; + relgot = ppc64_elf_tdata (ent->owner)->relgot; else if (ifunc) relgot = htab->reliplt; if (relgot != NULL) @@ -11737,10 +12957,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; @@ -11779,7 +12997,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) ; @@ -11835,6 +13053,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 @@ -11952,7 +13186,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); @@ -12003,17 +13237,15 @@ ppc64_elf_relocate_section (bfd *output_bfd, ? 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, + info->callbacks->einfo + (_("%P: %H: relocation %s for indirect " + "function %s unsupported\n"), + input_bfd, input_section, rel->r_offset, ppc64_elf_howto_table[r_type]->name, sym_name); ret = FALSE; } - else if (r_symndx == 0 || bfd_is_abs_section (sec)) + else if (r_symndx == STN_UNDEF || bfd_is_abs_section (sec)) ; else if (sec == NULL || sec->owner == NULL) { @@ -12110,8 +13342,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_PLTREL64: /* These ones haven't been implemented yet. */ - (*_bfd_error_handler) - (_("%B: relocation %s is not supported for symbol %s."), + info->callbacks->einfo + (_("%P: %B: relocation %s is not supported for symbol %s\n"), input_bfd, ppc64_elf_howto_table[r_type]->name, sym_name); @@ -12120,6 +13352,72 @@ 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: + if (htab->do_toc_opt && relocation + addend + 0x8000 < 0x10000 + && !ppc64_elf_tdata (input_bfd)->unexpected_toc_insn) + { + bfd_byte *p = contents + (rel->r_offset & ~3); + bfd_put_32 (input_bfd, NOP, p); + } + 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 + && !ppc64_elf_tdata (input_bfd)->unexpected_toc_insn) + { + bfd_byte *p = contents + (rel->r_offset & ~3); + insn = bfd_get_32 (input_bfd, p); + if ((insn & (0x3f << 26)) == 12u << 26 /* addic */) + { + /* Transform addic to addi when we change reg. */ + insn &= ~((0x3f << 26) | (0x1f << 16)); + insn |= (14u << 26) | (2 << 16); + } + else + { + insn &= ~(0x1f << 16); + insn |= 2 << 16; + } + bfd_put_32 (input_bfd, insn, p); + } + break; + } + /* Do any further special processing. */ switch (r_type) { @@ -12193,9 +13491,9 @@ ppc64_elf_relocate_section (bfd *output_bfd, mask = 15; if (((relocation + addend) & mask) != 0) { - (*_bfd_error_handler) - (_("%B: error: relocation %s not a multiple of %d"), - input_bfd, + info->callbacks->einfo + (_("%P: %H: error: %s not a multiple of %u\n"), + input_bfd, input_section, rel->r_offset, ppc64_elf_howto_table[r_type]->name, mask + 1); bfd_set_error (bfd_error_bad_value); @@ -12210,13 +13508,13 @@ ppc64_elf_relocate_section (bfd *output_bfd, not process them. */ if (unresolved_reloc && !((input_section->flags & SEC_DEBUGGING) != 0 - && h->elf.def_dynamic)) + && h->elf.def_dynamic) + && _bfd_elf_section_offset (output_bfd, info, input_section, + rel->r_offset) != (bfd_vma) -1) { - (*_bfd_error_handler) - (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"), - input_bfd, - input_section, - (long) rel->r_offset, + info->callbacks->einfo + (_("%P: %H: unresolvable %s relocation against symbol `%s'\n"), + input_bfd, input_section, rel->r_offset, ppc64_elf_howto_table[(int) r_type]->name, h->elf.root.root.string); ret = FALSE; @@ -12259,11 +13557,9 @@ ppc64_elf_relocate_section (bfd *output_bfd, } else { - (*_bfd_error_handler) - (_("%B(%A+0x%lx): %s reloc against `%s': error %d"), - input_bfd, - input_section, - (long) rel->r_offset, + info->callbacks->einfo + (_("%P: %H: %s reloc against `%s': error %d\n"), + input_bfd, input_section, rel->r_offset, ppc64_elf_howto_table[r_type]->name, sym_name, (int) r); @@ -12338,6 +13634,8 @@ 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) @@ -12437,6 +13735,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"); @@ -12550,7 +13851,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; @@ -12559,11 +13860,19 @@ 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; + + if (htab->glink_eh_frame != NULL + && htab->glink_eh_frame->sec_info_type == ELF_INFO_TYPE_EH_FRAME + && !_bfd_elf_write_section_eh_frame (output_bfd, info, + htab->glink_eh_frame, + htab->glink_eh_frame->contents)) + return FALSE; + /* We need to handle writing out multiple GOT sections ourselves, since we didn't add them to DYNOBJ. We know dynobj is the first bfd. */ @@ -12596,3 +13905,22 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd, } #include "elf64-target.h" + +/* FreeBSD support */ + +#undef TARGET_LITTLE_SYM +#undef TARGET_LITTLE_NAME + +#undef TARGET_BIG_SYM +#define TARGET_BIG_SYM bfd_elf64_powerpc_freebsd_vec +#undef TARGET_BIG_NAME +#define TARGET_BIG_NAME "elf64-powerpc-freebsd" + +#undef ELF_OSABI +#define ELF_OSABI ELFOSABI_FREEBSD + +#undef elf64_bed +#define elf64_bed elf64_powerpc_fbsd_bed + +#include "elf64-target.h" +