| Index: bfd/elf64-ppc.c
|
| diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
|
| index 4560599ec844c934dbfe9fad16e427beae4aac5f..50dad3b10e64ffe9d1abae32a252fbbbd7fd1a3f 100644
|
| --- a/bfd/elf64-ppc.c
|
| +++ b/bfd/elf64-ppc.c
|
| @@ -1,6 +1,6 @@
|
| /* PowerPC64-specific support for 64-bit ELF.
|
| Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
|
| - 2009, 2010, 2011 Free Software Foundation, Inc.
|
| + 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
|
| Written by Linus Nordberg, Swox AB <info@swox.com>,
|
| based on elf32-ppc.c by Ian Lance Taylor.
|
| Largely rewritten by Alan Modra.
|
| @@ -55,7 +55,7 @@ static bfd_reloc_status_type ppc64_elf_toc64_reloc
|
| static bfd_reloc_status_type ppc64_elf_unhandled_reloc
|
| (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
|
| static bfd_vma opd_entry_value
|
| - (asection *, bfd_vma, asection **, bfd_vma *);
|
| + (asection *, bfd_vma, asection **, bfd_vma *, bfd_boolean);
|
|
|
| #define TARGET_LITTLE_SYM bfd_elf64_powerpcle_vec
|
| #define TARGET_LITTLE_NAME "elf64-powerpcle"
|
| @@ -105,6 +105,7 @@ static bfd_vma opd_entry_value
|
| #define elf_backend_gc_sweep_hook ppc64_elf_gc_sweep_hook
|
| #define elf_backend_adjust_dynamic_symbol ppc64_elf_adjust_dynamic_symbol
|
| #define elf_backend_hide_symbol ppc64_elf_hide_symbol
|
| +#define elf_backend_maybe_function_sym ppc64_elf_maybe_function_sym
|
| #define elf_backend_always_size_sections ppc64_elf_func_desc_adjust
|
| #define elf_backend_size_dynamic_sections ppc64_elf_size_dynamic_sections
|
| #define elf_backend_init_index_section _bfd_elf_init_2_index_sections
|
| @@ -152,6 +153,13 @@ static bfd_vma opd_entry_value
|
| #define ADDIS_R2_R2 0x3c420000 /* addis %r2,%r2,off@ha */
|
| #define ADDI_R2_R2 0x38420000 /* addi %r2,%r2,off@l */
|
|
|
| +#define XOR_R11_R11_R11 0x7d6b5a78 /* xor %r11,%r11,%r11 */
|
| +#define ADD_R12_R12_R11 0x7d8c5a14 /* add %r12,%r12,%r11 */
|
| +#define ADD_R2_R2_R11 0x7c425a14 /* add %r2,%r2,%r11 */
|
| +#define CMPLDI_R2_0 0x28220000 /* cmpldi %r2,0 */
|
| +#define BNECTR 0x4ca20420 /* bnectr+ */
|
| +#define BNECTR_P4 0x4ce20420 /* bnectr+ */
|
| +
|
| #define LD_R11_0R2 0xe9620000 /* ld %r11,xxx+0(%r2) */
|
| #define LD_R2_0R2 0xe8420000 /* ld %r2,xxx+0(%r2) */
|
|
|
| @@ -2339,7 +2347,7 @@ ppc64_elf_branch_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
|
| {
|
| bfd_vma dest = opd_entry_value (symbol->section,
|
| symbol->value + reloc_entry->addend,
|
| - NULL, NULL);
|
| + NULL, NULL, FALSE);
|
| if (dest != (bfd_vma) -1)
|
| reloc_entry->addend = dest - (symbol->value
|
| + symbol->section->output_section->vma
|
| @@ -2356,8 +2364,8 @@ ppc64_elf_brtaken_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
|
| long insn;
|
| enum elf_ppc64_reloc_type r_type;
|
| bfd_size_type octets;
|
| - /* Disabled until we sort out how ld should choose 'y' vs 'at'. */
|
| - bfd_boolean is_power4 = FALSE;
|
| + /* Assume 'at' branch hints. */
|
| + bfd_boolean is_isa_v2 = TRUE;
|
|
|
| /* If this is a relocatable link (output_bfd test tells us), just
|
| call the generic function. Any adjustment will be done at final
|
| @@ -2374,7 +2382,7 @@ ppc64_elf_brtaken_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
|
| || r_type == R_PPC64_REL14_BRTAKEN)
|
| insn |= 0x01 << 21; /* 'y' or 't' bit, lowest bit of BO field. */
|
|
|
| - if (is_power4)
|
| + if (is_isa_v2)
|
| {
|
| /* Set 'a' bit. This is 0b00010 in BO field for branch
|
| on CR(BI) insns (BO == 001at or 011at), and 0b01000
|
| @@ -2714,7 +2722,7 @@ ppc64_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_type,
|
| va_list ap;
|
|
|
| va_start (ap, note_type);
|
| - memset (data, 0, 40);
|
| + memset (data, 0, sizeof (data));
|
| strncpy (data + 40, va_arg (ap, const char *), 16);
|
| strncpy (data + 56, va_arg (ap, const char *), 80);
|
| va_end (ap);
|
| @@ -3584,7 +3592,8 @@ enum ppc_stub_type {
|
| ppc_stub_long_branch_r2off,
|
| ppc_stub_plt_branch,
|
| ppc_stub_plt_branch_r2off,
|
| - ppc_stub_plt_call
|
| + ppc_stub_plt_call,
|
| + ppc_stub_plt_call_r2save
|
| };
|
|
|
| struct ppc_stub_hash_entry {
|
| @@ -3752,14 +3761,20 @@ struct ppc_link_hash_table
|
| bfd_size_type got_reli_size;
|
|
|
| /* Statistics. */
|
| - unsigned long stub_count[ppc_stub_plt_call];
|
| + unsigned long stub_count[ppc_stub_plt_call_r2save];
|
|
|
| /* Number of stubs against global syms. */
|
| unsigned long stub_globals;
|
|
|
| + /* Alignment of PLT call stubs. */
|
| + unsigned int plt_stub_align:4;
|
| +
|
| /* Set if PLT call stubs should load r11. */
|
| unsigned int plt_static_chain:1;
|
|
|
| + /* Set if PLT call stubs need a read-read barrier. */
|
| + unsigned int plt_thread_safe:1;
|
| +
|
| /* Set if we should emit symbols for stubs. */
|
| unsigned int emit_stub_syms:1;
|
|
|
| @@ -4230,7 +4245,7 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
|
| ".eh_frame",
|
| flags);
|
| if (htab->glink_eh_frame == NULL
|
| - || !bfd_set_section_alignment (abfd, htab->glink_eh_frame, 2))
|
| + || !bfd_set_section_alignment (dynobj, htab->glink_eh_frame, 2))
|
| return FALSE;
|
| }
|
|
|
| @@ -4293,7 +4308,7 @@ create_got_section (bfd *abfd, struct bfd_link_info *info)
|
| if (! _bfd_elf_create_got_section (htab->elf.dynobj, info))
|
| return FALSE;
|
|
|
| - htab->got = bfd_get_section_by_name (htab->elf.dynobj, ".got");
|
| + htab->got = bfd_get_linker_section (htab->elf.dynobj, ".got");
|
| if (!htab->got)
|
| abort ();
|
| }
|
| @@ -4332,12 +4347,12 @@ ppc64_elf_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
|
| return FALSE;
|
|
|
| if (!htab->got)
|
| - htab->got = bfd_get_section_by_name (dynobj, ".got");
|
| - htab->plt = bfd_get_section_by_name (dynobj, ".plt");
|
| - htab->relplt = bfd_get_section_by_name (dynobj, ".rela.plt");
|
| - htab->dynbss = bfd_get_section_by_name (dynobj, ".dynbss");
|
| + htab->got = bfd_get_linker_section (dynobj, ".got");
|
| + htab->plt = bfd_get_linker_section (dynobj, ".plt");
|
| + htab->relplt = bfd_get_linker_section (dynobj, ".rela.plt");
|
| + htab->dynbss = bfd_get_linker_section (dynobj, ".dynbss");
|
| if (!info->shared)
|
| - htab->relbss = bfd_get_section_by_name (dynobj, ".rela.bss");
|
| + htab->relbss = bfd_get_linker_section (dynobj, ".rela.bss");
|
|
|
| if (!htab->got || !htab->plt || !htab->relplt || !htab->dynbss
|
| || (!info->shared && !htab->relbss))
|
| @@ -5507,14 +5522,16 @@ static bfd_vma
|
| opd_entry_value (asection *opd_sec,
|
| bfd_vma offset,
|
| asection **code_sec,
|
| - bfd_vma *code_off)
|
| + bfd_vma *code_off,
|
| + bfd_boolean in_code_sec)
|
| {
|
| bfd *opd_bfd = opd_sec->owner;
|
| Elf_Internal_Rela *relocs;
|
| Elf_Internal_Rela *lo, *hi, *look;
|
| bfd_vma val;
|
|
|
| - /* No relocs implies we are linking a --just-symbols object. */
|
| + /* No relocs implies we are linking a --just-symbols object, or looking
|
| + at a final linked executable with addr2line or somesuch. */
|
| if (opd_sec->reloc_count == 0)
|
| {
|
| char buf[8];
|
| @@ -5526,11 +5543,22 @@ opd_entry_value (asection *opd_sec,
|
| if (code_sec != NULL)
|
| {
|
| asection *sec, *likely = NULL;
|
| - for (sec = opd_bfd->sections; sec != NULL; sec = sec->next)
|
| - if (sec->vma <= val
|
| - && (sec->flags & SEC_LOAD) != 0
|
| - && (sec->flags & SEC_ALLOC) != 0)
|
| - likely = sec;
|
| +
|
| + if (in_code_sec)
|
| + {
|
| + sec = *code_sec;
|
| + if (sec->vma <= val
|
| + && val < sec->vma + sec->size)
|
| + likely = sec;
|
| + else
|
| + val = -1;
|
| + }
|
| + else
|
| + for (sec = opd_bfd->sections; sec != NULL; sec = sec->next)
|
| + if (sec->vma <= val
|
| + && (sec->flags & SEC_LOAD) != 0
|
| + && (sec->flags & SEC_ALLOC) != 0)
|
| + likely = sec;
|
| if (likely != NULL)
|
| {
|
| *code_sec = likely;
|
| @@ -5569,15 +5597,18 @@ opd_entry_value (asection *opd_sec,
|
| unsigned long symndx = ELF64_R_SYM (look->r_info);
|
| asection *sec;
|
|
|
| - if (symndx < symtab_hdr->sh_info)
|
| + if (symndx < symtab_hdr->sh_info
|
| + || elf_sym_hashes (opd_bfd) == NULL)
|
| {
|
| Elf_Internal_Sym *sym;
|
|
|
| sym = (Elf_Internal_Sym *) symtab_hdr->contents;
|
| if (sym == NULL)
|
| {
|
| - sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr,
|
| - symtab_hdr->sh_info,
|
| + size_t symcnt = symtab_hdr->sh_info;
|
| + if (elf_sym_hashes (opd_bfd) == NULL)
|
| + symcnt = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
|
| + sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr, symcnt,
|
| 0, NULL, NULL, NULL);
|
| if (sym == NULL)
|
| break;
|
| @@ -5606,7 +5637,12 @@ opd_entry_value (asection *opd_sec,
|
| if (code_off != NULL)
|
| *code_off = val;
|
| if (code_sec != NULL)
|
| - *code_sec = sec;
|
| + {
|
| + if (in_code_sec && *code_sec != sec)
|
| + return -1;
|
| + else
|
| + *code_sec = sec;
|
| + }
|
| if (sec != NULL && sec->output_section != NULL)
|
| val += sec->output_section->vma + sec->output_offset;
|
| }
|
| @@ -5617,6 +5653,55 @@ opd_entry_value (asection *opd_sec,
|
| return val;
|
| }
|
|
|
| +/* If the ELF symbol SYM might be a function in SEC, return the
|
| + function size and set *CODE_OFF to the function's entry point,
|
| + otherwise return zero. */
|
| +
|
| +static bfd_size_type
|
| +ppc64_elf_maybe_function_sym (const asymbol *sym, asection *sec,
|
| + bfd_vma *code_off)
|
| +{
|
| + bfd_size_type size;
|
| +
|
| + if ((sym->flags & (BSF_SECTION_SYM | BSF_FILE | BSF_OBJECT
|
| + | BSF_THREAD_LOCAL | BSF_RELC | BSF_SRELC)) != 0)
|
| + return 0;
|
| +
|
| + size = 0;
|
| + if (!(sym->flags & BSF_SYNTHETIC))
|
| + size = ((elf_symbol_type *) sym)->internal_elf_sym.st_size;
|
| +
|
| + if (strcmp (sym->section->name, ".opd") == 0)
|
| + {
|
| + if (opd_entry_value (sym->section, sym->value,
|
| + &sec, code_off, TRUE) == (bfd_vma) -1)
|
| + return 0;
|
| + /* An old ABI binary with dot-syms has a size of 24 on the .opd
|
| + symbol. This size has nothing to do with the code size of the
|
| + function, which is what we're supposed to return, but the
|
| + code size isn't available without looking up the dot-sym.
|
| + However, doing that would be a waste of time particularly
|
| + since elf_find_function will look at the dot-sym anyway.
|
| + Now, elf_find_function will keep the largest size of any
|
| + function sym found at the code address of interest, so return
|
| + 1 here to avoid it incorrectly caching a larger function size
|
| + for a small function. This does mean we return the wrong
|
| + size for a new-ABI function of size 24, but all that does is
|
| + disable caching for such functions. */
|
| + if (size == 24)
|
| + size = 1;
|
| + }
|
| + else
|
| + {
|
| + if (sym->section != sec)
|
| + return 0;
|
| + *code_off = sym->value;
|
| + }
|
| + if (size == 0)
|
| + size = 1;
|
| + return size;
|
| +}
|
| +
|
| /* Return true if symbol is defined in a regular object file. */
|
|
|
| static bfd_boolean
|
| @@ -5694,7 +5779,7 @@ ppc64_elf_gc_keep (struct bfd_link_info *info)
|
| else if (get_opd_info (eh->elf.root.u.def.section) != NULL
|
| && opd_entry_value (eh->elf.root.u.def.section,
|
| eh->elf.root.u.def.value,
|
| - &sec, NULL) != (bfd_vma) -1)
|
| + &sec, NULL, FALSE) != (bfd_vma) -1)
|
| sec->flags |= SEC_KEEP;
|
|
|
| sec = eh->elf.root.u.def.section;
|
| @@ -5745,7 +5830,7 @@ ppc64_elf_gc_mark_dynamic_ref (struct elf_link_hash_entry *h, void *inf)
|
| else if (get_opd_info (eh->elf.root.u.def.section) != NULL
|
| && opd_entry_value (eh->elf.root.u.def.section,
|
| eh->elf.root.u.def.value,
|
| - &code_sec, NULL) != (bfd_vma) -1)
|
| + &code_sec, NULL, FALSE) != (bfd_vma) -1)
|
| code_sec->flags |= SEC_KEEP;
|
| }
|
|
|
| @@ -5805,7 +5890,7 @@ ppc64_elf_gc_mark_hook (asection *sec,
|
| else if (get_opd_info (eh->elf.root.u.def.section) != NULL
|
| && opd_entry_value (eh->elf.root.u.def.section,
|
| eh->elf.root.u.def.value,
|
| - &rsec, NULL) != (bfd_vma) -1)
|
| + &rsec, NULL, FALSE) != (bfd_vma) -1)
|
| eh->elf.root.u.def.section->gc_mark = 1;
|
| else
|
| rsec = h->root.u.def.section;
|
| @@ -6274,7 +6359,7 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
|
| && opd_entry_value (fdh->elf.root.u.def.section,
|
| fdh->elf.root.u.def.value,
|
| &fh->elf.root.u.def.section,
|
| - &fh->elf.root.u.def.value) != (bfd_vma) -1)
|
| + &fh->elf.root.u.def.value, FALSE) != (bfd_vma) -1)
|
| {
|
| fh->elf.root.type = fdh->elf.root.type;
|
| fh->elf.forced_local = 1;
|
| @@ -6383,7 +6468,7 @@ ppc64_elf_func_desc_adjust (bfd *obfd ATTRIBUTE_UNUSED,
|
| {
|
| struct ppc_link_hash_table *htab;
|
| unsigned int i;
|
| - const struct sfpr_def_parms funcs[] =
|
| + static const struct sfpr_def_parms funcs[] =
|
| {
|
| { "_savegpr0_", 14, 31, savegpr0, savegpr0_tail },
|
| { "_restgpr0_", 14, 29, restgpr0, restgpr0_tail },
|
| @@ -6409,9 +6494,10 @@ ppc64_elf_func_desc_adjust (bfd *obfd ATTRIBUTE_UNUSED,
|
|
|
| /* Provide any missing _save* and _rest* functions. */
|
| htab->sfpr->size = 0;
|
| - for (i = 0; i < sizeof (funcs) / sizeof (funcs[0]); i++)
|
| - if (!sfpr_define (info, &funcs[i]))
|
| - return FALSE;
|
| + if (!info->relocatable)
|
| + for (i = 0; i < sizeof (funcs) / sizeof (funcs[0]); i++)
|
| + if (!sfpr_define (info, &funcs[i]))
|
| + return FALSE;
|
|
|
| elf_link_hash_traverse (&htab->elf, func_desc_adjust, info);
|
|
|
| @@ -6530,13 +6616,6 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
|
| /* This is a reference to a symbol defined by a dynamic object which
|
| is not a function. */
|
|
|
| - if (h->size == 0)
|
| - {
|
| - info->callbacks->einfo (_("%P: dynamic variable `%s' is zero size\n"),
|
| - h->root.root.string);
|
| - return TRUE;
|
| - }
|
| -
|
| /* We must allocate the symbol in our .dynbss section, which will
|
| become part of the .bss section of the executable. There will be
|
| an entry for this symbol in the .dynsym section. The dynamic
|
| @@ -6551,7 +6630,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
|
| to copy the initial value out of the dynamic object and into the
|
| runtime process image. We need to remember the offset into the
|
| .rela.bss section we are going to use. */
|
| - if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
|
| + if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
|
| {
|
| htab->relbss->size += sizeof (Elf64_External_Rela);
|
| h->needs_copy = 1;
|
| @@ -6852,7 +6931,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
|
| if (dsec == NULL)
|
| {
|
| for (dsec = sym_sec->owner->sections; dsec; dsec = dsec->next)
|
| - if (elf_discarded_section (dsec))
|
| + if (discarded_section (dsec))
|
| {
|
| ppc64_elf_tdata (sym_sec->owner)->deleted_section = dsec;
|
| break;
|
| @@ -7033,7 +7112,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
|
| if (sec == NULL || sec->size == 0)
|
| continue;
|
|
|
| - if (sec->sec_info_type == ELF_INFO_TYPE_JUST_SYMS)
|
| + if (sec->sec_info_type == SEC_INFO_TYPE_JUST_SYMS)
|
| continue;
|
|
|
| if (sec->output_section == bfd_abs_section_ptr)
|
| @@ -8077,8 +8156,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
|
| toc = bfd_get_section_by_name (ibfd, ".toc");
|
| if (toc == NULL
|
| || toc->size == 0
|
| - || toc->sec_info_type == ELF_INFO_TYPE_JUST_SYMS
|
| - || elf_discarded_section (toc))
|
| + || toc->sec_info_type == SEC_INFO_TYPE_JUST_SYMS
|
| + || discarded_section (toc))
|
| continue;
|
|
|
| toc_relocs = NULL;
|
| @@ -8091,7 +8170,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
|
| for (sec = ibfd->sections; sec != NULL; sec = sec->next)
|
| {
|
| if (sec->reloc_count == 0
|
| - || !elf_discarded_section (sec)
|
| + || !discarded_section (sec)
|
| || get_opd_info (sec)
|
| || (sec->flags & SEC_ALLOC) == 0
|
| || (sec->flags & SEC_DEBUGGING) != 0)
|
| @@ -8201,7 +8280,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
|
| goto error_ret;
|
|
|
| if (sym_sec == NULL
|
| - || elf_discarded_section (sym_sec))
|
| + || discarded_section (sym_sec))
|
| continue;
|
|
|
| if (!SYMBOL_CALLS_LOCAL (info, h))
|
| @@ -8281,7 +8360,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
|
| int repeat;
|
|
|
| if (sec->reloc_count == 0
|
| - || elf_discarded_section (sec)
|
| + || discarded_section (sec)
|
| || get_opd_info (sec)
|
| || (sec->flags & SEC_ALLOC) == 0
|
| || (sec->flags & SEC_DEBUGGING) != 0)
|
| @@ -8503,7 +8582,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
|
| for (sec = ibfd->sections; sec != NULL; sec = sec->next)
|
| {
|
| if (sec->reloc_count == 0
|
| - || elf_discarded_section (sec))
|
| + || discarded_section (sec))
|
| continue;
|
|
|
| relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
|
| @@ -9061,7 +9140,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
|
| /* Set the contents of the .interp section to the interpreter. */
|
| if (info->executable)
|
| {
|
| - s = bfd_get_section_by_name (dynobj, ".interp");
|
| + s = bfd_get_linker_section (dynobj, ".interp");
|
| if (s == NULL)
|
| abort ();
|
| s->size = sizeof ELF_DYNAMIC_INTERPRETER;
|
| @@ -9471,21 +9550,126 @@ ppc_type_of_stub (asection *input_sec,
|
| return ppc_stub_none;
|
| }
|
|
|
| -/* Build a .plt call stub. */
|
| +/* With power7 weakly ordered memory model, it is possible for ld.so
|
| + to update a plt entry in one thread and have another thread see a
|
| + stale zero toc entry. To avoid this we need some sort of acquire
|
| + barrier in the call stub. One solution is to make the load of the
|
| + toc word seem to appear to depend on the load of the function entry
|
| + word. Another solution is to test for r2 being zero, and branch to
|
| + the appropriate glink entry if so.
|
| +
|
| + . fake dep barrier compare
|
| + . ld 11,xxx(2) ld 11,xxx(2)
|
| + . mtctr 11 mtctr 11
|
| + . xor 11,11,11 ld 2,xxx+8(2)
|
| + . add 2,2,11 cmpldi 2,0
|
| + . ld 2,xxx+8(2) bnectr+
|
| + . bctr b <glink_entry>
|
| +
|
| + The solution involving the compare turns out to be faster, so
|
| + that's what we use unless the branch won't reach. */
|
| +
|
| +#define ALWAYS_USE_FAKE_DEP 0
|
| +#define ALWAYS_EMIT_R2SAVE 0
|
|
|
| -static inline bfd_byte *
|
| -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)
|
| #define PPC_HA(v) PPC_HI ((v) + 0x8000)
|
|
|
| +static inline unsigned int
|
| +plt_stub_size (struct ppc_link_hash_table *htab,
|
| + struct ppc_stub_hash_entry *stub_entry,
|
| + bfd_vma off)
|
| +{
|
| + unsigned size = PLT_CALL_STUB_SIZE;
|
| +
|
| + if (!(ALWAYS_EMIT_R2SAVE
|
| + || stub_entry->stub_type == ppc_stub_plt_call_r2save))
|
| + size -= 4;
|
| + if (!htab->plt_static_chain)
|
| + size -= 4;
|
| + if (htab->plt_thread_safe)
|
| + size += 8;
|
| + if (PPC_HA (off) == 0)
|
| + size -= 4;
|
| + 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
|
| + || stub_entry->h == htab->tls_get_addr)
|
| + && !htab->no_tls_get_addr_opt)
|
| + size += 13 * 4;
|
| + return size;
|
| +}
|
| +
|
| +/* If this stub would cross fewer 2**plt_stub_align boundaries if we align,
|
| + then return the padding needed to do so. */
|
| +static inline unsigned int
|
| +plt_stub_pad (struct ppc_link_hash_table *htab,
|
| + struct ppc_stub_hash_entry *stub_entry,
|
| + bfd_vma plt_off)
|
| +{
|
| + int stub_align = 1 << htab->plt_stub_align;
|
| + unsigned stub_size = plt_stub_size (htab, stub_entry, plt_off);
|
| + bfd_vma stub_off = stub_entry->stub_sec->size;
|
| +
|
| + if (((stub_off + stub_size - 1) & -stub_align) - (stub_off & -stub_align)
|
| + > (stub_size & -stub_align))
|
| + return stub_align - (stub_off & (stub_align - 1));
|
| + return 0;
|
| +}
|
| +
|
| +/* Build a .plt call stub. */
|
| +
|
| +static inline bfd_byte *
|
| +build_plt_stub (struct ppc_link_hash_table *htab,
|
| + struct ppc_stub_hash_entry *stub_entry,
|
| + bfd_byte *p, bfd_vma offset, Elf_Internal_Rela *r)
|
| +{
|
| + bfd *obfd = htab->stub_bfd;
|
| + bfd_boolean plt_static_chain = htab->plt_static_chain;
|
| + bfd_boolean plt_thread_safe = htab->plt_thread_safe;
|
| + bfd_boolean use_fake_dep = plt_thread_safe;
|
| + bfd_vma cmp_branch_off = 0;
|
| +
|
| + if (!ALWAYS_USE_FAKE_DEP
|
| + && plt_thread_safe
|
| + && !(stub_entry->h != NULL
|
| + && (stub_entry->h == htab->tls_get_addr_fd
|
| + || stub_entry->h == htab->tls_get_addr)
|
| + && !htab->no_tls_get_addr_opt))
|
| + {
|
| + bfd_vma pltoff = stub_entry->plt_ent->plt.offset & ~1;
|
| + bfd_vma pltindex = (pltoff - PLT_INITIAL_ENTRY_SIZE) / PLT_ENTRY_SIZE;
|
| + bfd_vma glinkoff = GLINK_CALL_STUB_SIZE + pltindex * 8;
|
| + bfd_vma to, from;
|
| +
|
| + if (pltindex > 32767)
|
| + glinkoff += (pltindex - 32767) * 4;
|
| + to = (glinkoff
|
| + + htab->glink->output_offset
|
| + + htab->glink->output_section->vma);
|
| + from = (p - stub_entry->stub_sec->contents
|
| + + 4 * (ALWAYS_EMIT_R2SAVE
|
| + || stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
| + + 4 * (PPC_HA (offset) != 0)
|
| + + 4 * (PPC_HA (offset + 8 + 8 * plt_static_chain)
|
| + != PPC_HA (offset))
|
| + + 4 * (plt_static_chain != 0)
|
| + + 20
|
| + + stub_entry->stub_sec->output_offset
|
| + + stub_entry->stub_sec->output_section->vma);
|
| + cmp_branch_off = to - from;
|
| + use_fake_dep = cmp_branch_off + (1 << 25) >= (1 << 26);
|
| + }
|
| +
|
| if (PPC_HA (offset) != 0)
|
| {
|
| if (r != NULL)
|
| {
|
| - r[0].r_offset += 4;
|
| + if (ALWAYS_EMIT_R2SAVE
|
| + || stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
| + 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 + 4;
|
| r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
|
| @@ -9498,7 +9682,7 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
|
| }
|
| else
|
| {
|
| - r[2].r_offset = r[1].r_offset + 8;
|
| + r[2].r_offset = r[1].r_offset + 8 + 8 * use_fake_dep;
|
| r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
|
| r[2].r_addend = r[0].r_addend + 8;
|
| if (plt_static_chain)
|
| @@ -9509,7 +9693,9 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
|
| }
|
| }
|
| }
|
| - bfd_put_32 (obfd, STD_R2_40R1, p), p += 4;
|
| + if (ALWAYS_EMIT_R2SAVE
|
| + || stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
| + 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 + 8 + 8 * plt_static_chain) != PPC_HA (offset))
|
| @@ -9518,16 +9704,22 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
|
| offset = 0;
|
| }
|
| bfd_put_32 (obfd, MTCTR_R11, p), p += 4;
|
| + if (use_fake_dep)
|
| + {
|
| + bfd_put_32 (obfd, XOR_R11_R11_R11, p), p += 4;
|
| + bfd_put_32 (obfd, ADD_R12_R12_R11, p), p += 4;
|
| + }
|
| bfd_put_32 (obfd, LD_R2_0R12 | PPC_LO (offset + 8), 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
|
| {
|
| if (r != NULL)
|
| {
|
| - r[0].r_offset += 4;
|
| + if (ALWAYS_EMIT_R2SAVE
|
| + || stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
| + r[0].r_offset += 4;
|
| r[0].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
|
| if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
|
| {
|
| @@ -9537,7 +9729,7 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
|
| }
|
| else
|
| {
|
| - r[1].r_offset = r[0].r_offset + 8;
|
| + r[1].r_offset = r[0].r_offset + 8 + 8 * use_fake_dep;
|
| r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
|
| r[1].r_addend = r[0].r_addend + 8 + 8 * plt_static_chain;
|
| if (plt_static_chain)
|
| @@ -9548,7 +9740,9 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
|
| }
|
| }
|
| }
|
| - bfd_put_32 (obfd, STD_R2_40R1, p), p += 4;
|
| + if (ALWAYS_EMIT_R2SAVE
|
| + || stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
| + 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 + 8 + 8 * plt_static_chain) != PPC_HA (offset))
|
| {
|
| @@ -9556,11 +9750,23 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
|
| offset = 0;
|
| }
|
| bfd_put_32 (obfd, MTCTR_R11, p), p += 4;
|
| + if (use_fake_dep)
|
| + {
|
| + bfd_put_32 (obfd, XOR_R11_R11_R11, p), p += 4;
|
| + bfd_put_32 (obfd, ADD_R2_R2_R11, 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;
|
| }
|
| + if (plt_thread_safe && !use_fake_dep)
|
| + {
|
| + bfd_put_32 (obfd, CMPLDI_R2_0, p), p += 4;
|
| + bfd_put_32 (obfd, BNECTR_P4, p), p += 4;
|
| + bfd_put_32 (obfd, B_DOT + cmp_branch_off, p), p += 4;
|
| + }
|
| + else
|
| + bfd_put_32 (obfd, BCTR, p), p += 4;
|
| return p;
|
| }
|
|
|
| @@ -9581,9 +9787,12 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
|
| #define MTLR_R11 0x7d6803a6
|
|
|
| static inline bfd_byte *
|
| -build_tls_get_addr_stub (bfd *obfd, bfd_byte *p, int offset,
|
| - Elf_Internal_Rela *r, bfd_boolean plt_static_chain)
|
| +build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
|
| + struct ppc_stub_hash_entry *stub_entry,
|
| + bfd_byte *p, bfd_vma offset, Elf_Internal_Rela *r)
|
| {
|
| + bfd *obfd = htab->stub_bfd;
|
| +
|
| bfd_put_32 (obfd, LD_R11_0R3 + 0, p), p += 4;
|
| bfd_put_32 (obfd, LD_R12_0R3 + 8, p), p += 4;
|
| bfd_put_32 (obfd, MR_R0_R3, p), p += 4;
|
| @@ -9596,7 +9805,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, plt_static_chain);
|
| + p = build_plt_stub (htab, stub_entry, p, offset, r);
|
| bfd_put_32 (obfd, BCTRL, p - 4);
|
|
|
| bfd_put_32 (obfd, LD_R11_0R1 + 32, p), p += 4;
|
| @@ -9943,6 +10152,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
|
| break;
|
|
|
| case ppc_stub_plt_call:
|
| + case ppc_stub_plt_call_r2save:
|
| if (stub_entry->h != NULL
|
| && stub_entry->h->is_func_descriptor
|
| && stub_entry->h->oh != NULL)
|
| @@ -10009,6 +10219,15 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
|
| return FALSE;
|
| }
|
|
|
| + if (htab->plt_stub_align != 0)
|
| + {
|
| + unsigned pad = plt_stub_pad (htab, stub_entry, off);
|
| +
|
| + stub_entry->stub_sec->size += pad;
|
| + stub_entry->stub_offset = stub_entry->stub_sec->size;
|
| + loc += pad;
|
| + }
|
| +
|
| r = NULL;
|
| if (info->emitrelocations)
|
| {
|
| @@ -10028,11 +10247,9 @@ 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,
|
| - htab->plt_static_chain);
|
| + p = build_tls_get_addr_stub (htab, stub_entry, loc, off, r);
|
| else
|
| - p = build_plt_stub (htab->stub_bfd, loc, off, r,
|
| - htab->plt_static_chain);
|
| + p = build_plt_stub (htab, stub_entry, loc, off, r);
|
| size = p - loc;
|
| break;
|
|
|
| @@ -10052,6 +10269,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
|
| "long_branch_r2off",
|
| "plt_branch",
|
| "plt_branch_r2off",
|
| + "plt_call",
|
| "plt_call" };
|
|
|
| len1 = strlen (stub_str[stub_entry->stub_type - 1]);
|
| @@ -10102,7 +10320,8 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
|
| if (htab == NULL)
|
| return FALSE;
|
|
|
| - if (stub_entry->stub_type == ppc_stub_plt_call)
|
| + if (stub_entry->stub_type == ppc_stub_plt_call
|
| + || stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
| {
|
| asection *plt;
|
| off = stub_entry->plt_ent->plt.offset & ~(bfd_vma) 1;
|
| @@ -10118,18 +10337,9 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
|
| - elf_gp (plt->output_section->owner)
|
| - 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 + 8 + 8 * htab->plt_static_chain) != PPC_HA (off))
|
| - size += 4;
|
| - if (stub_entry->h != NULL
|
| - && (stub_entry->h == htab->tls_get_addr_fd
|
| - || stub_entry->h == htab->tls_get_addr)
|
| - && !htab->no_tls_get_addr_opt)
|
| - size += 13 * 4;
|
| + size = plt_stub_size (htab, stub_entry, off);
|
| + if (htab->plt_stub_align)
|
| + size += plt_stub_pad (htab, stub_entry, off);
|
| if (info->emitrelocations)
|
| {
|
| stub_entry->stub_sec->reloc_count
|
| @@ -10344,7 +10554,9 @@ ppc64_elf_next_toc_section (struct bfd_link_info *info, asection *isec)
|
| if (!htab->second_toc_pass)
|
| {
|
| /* Keep track of the first .toc or .got section for this input bfd. */
|
| - if (htab->toc_bfd != isec->owner)
|
| + bfd_boolean new_bfd = htab->toc_bfd != isec->owner;
|
| +
|
| + if (new_bfd)
|
| {
|
| htab->toc_bfd = isec->owner;
|
| htab->toc_first_sec = isec;
|
| @@ -10372,7 +10584,8 @@ ppc64_elf_next_toc_section (struct bfd_link_info *info, asection *isec)
|
|
|
| /* Die if someone uses a linker script that doesn't keep input
|
| file .toc and .got together. */
|
| - if (elf_gp (isec->owner) != 0
|
| + if (new_bfd
|
| + && elf_gp (isec->owner) != 0
|
| && elf_gp (isec->owner) != off)
|
| return FALSE;
|
|
|
| @@ -10741,7 +10954,8 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
|
| sym_value += adjust;
|
| }
|
|
|
| - dest = opd_entry_value (sym_sec, sym_value, &sym_sec, NULL);
|
| + dest = opd_entry_value (sym_sec, sym_value,
|
| + &sym_sec, NULL, FALSE);
|
| if (dest == (bfd_vma) -1)
|
| continue;
|
| }
|
| @@ -11098,7 +11312,8 @@ maybe_strip_output (struct bfd_link_info *info, asection *isec)
|
|
|
| bfd_boolean
|
| ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
|
| - bfd_boolean plt_static_chain)
|
| + bfd_boolean plt_static_chain, int plt_thread_safe,
|
| + int plt_stub_align)
|
| {
|
| bfd_size_type stub_group_size;
|
| bfd_boolean stubs_always_before_branch;
|
| @@ -11108,6 +11323,40 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
|
| return FALSE;
|
|
|
| htab->plt_static_chain = plt_static_chain;
|
| + htab->plt_stub_align = plt_stub_align;
|
| + if (plt_thread_safe == -1)
|
| + {
|
| + const char *const thread_starter[] =
|
| + {
|
| + "pthread_create",
|
| + /* libstdc++ */
|
| + "_ZNSt6thread15_M_start_threadESt10shared_ptrINS_10_Impl_baseEE",
|
| + /* librt */
|
| + "aio_init", "aio_read", "aio_write", "aio_fsync", "lio_listio",
|
| + "mq_notify", "create_timer",
|
| + /* libanl */
|
| + "getaddrinfo_a",
|
| + /* libgomp */
|
| + "GOMP_parallel_start",
|
| + "GOMP_parallel_loop_static_start",
|
| + "GOMP_parallel_loop_dynamic_start",
|
| + "GOMP_parallel_loop_guided_start",
|
| + "GOMP_parallel_loop_runtime_start",
|
| + "GOMP_parallel_sections_start",
|
| + };
|
| + unsigned i;
|
| +
|
| + for (i = 0; i < sizeof (thread_starter)/ sizeof (thread_starter[0]); i++)
|
| + {
|
| + struct elf_link_hash_entry *h;
|
| + h = elf_link_hash_lookup (&htab->elf, thread_starter[i],
|
| + FALSE, FALSE, TRUE);
|
| + plt_thread_safe = h != NULL && h->ref_regular;
|
| + if (plt_thread_safe)
|
| + break;
|
| + }
|
| + }
|
| + htab->plt_thread_safe = plt_thread_safe;
|
| stubs_always_before_branch = group_size < 0;
|
| if (group_size < 0)
|
| stub_group_size = -group_size;
|
| @@ -11281,7 +11530,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
|
| sym_value += adjust;
|
| }
|
| dest = opd_entry_value (sym_sec, sym_value,
|
| - &code_sec, &code_value);
|
| + &code_sec, &code_value, FALSE);
|
| if (dest != (bfd_vma) -1)
|
| {
|
| destination = dest;
|
| @@ -11342,10 +11591,14 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
|
| 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;
|
| + && ELF64_R_TYPE (irela[1].r_info) == R_PPC64_TOCSAVE)
|
| + {
|
| + if (!tocsave_find (htab, INSERT,
|
| + &local_syms, irela + 1, input_bfd))
|
| + goto error_ret_free_internal;
|
| + }
|
| + else if (stub_type == ppc_stub_plt_call)
|
| + stub_type = ppc_stub_plt_call_r2save;
|
|
|
| /* Support for grouping stub sections. */
|
| id_sec = htab->stub_group[section->id].link_sec;
|
| @@ -11361,6 +11614,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
|
| {
|
| /* The proper stub has already been created. */
|
| free (stub_name);
|
| + if (stub_type == ppc_stub_plt_call_r2save)
|
| + stub_entry->stub_type = stub_type;
|
| continue;
|
| }
|
|
|
| @@ -11380,7 +11635,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
|
| }
|
|
|
| stub_entry->stub_type = stub_type;
|
| - if (stub_type != ppc_stub_plt_call)
|
| + if (stub_type != ppc_stub_plt_call
|
| + && stub_type != ppc_stub_plt_call_r2save)
|
| {
|
| stub_entry->target_value = code_value;
|
| stub_entry->target_section = code_sec;
|
| @@ -11443,9 +11699,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
|
|
|
| if (htab->glink_eh_frame != NULL
|
| && !bfd_is_abs_section (htab->glink_eh_frame->output_section)
|
| - && (htab->glink_eh_frame->flags & SEC_EXCLUDE) == 0)
|
| + && htab->glink_eh_frame->output_section->size != 0)
|
| {
|
| - bfd_size_type size = 0;
|
| + size_t size = 0, align;
|
|
|
| for (stub_sec = htab->stub_bfd->sections;
|
| stub_sec != NULL;
|
| @@ -11456,10 +11712,22 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
|
| size += 24;
|
| if (size != 0)
|
| size += sizeof (glink_eh_frame_cie);
|
| + align = 1;
|
| + align <<= htab->glink_eh_frame->output_section->alignment_power;
|
| + align -= 1;
|
| + size = (size + align) & ~align;
|
| htab->glink_eh_frame->rawsize = htab->glink_eh_frame->size;
|
| htab->glink_eh_frame->size = size;
|
| }
|
|
|
| + if (htab->plt_stub_align != 0)
|
| + for (stub_sec = htab->stub_bfd->sections;
|
| + stub_sec != NULL;
|
| + stub_sec = stub_sec->next)
|
| + if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
|
| + stub_sec->size = ((stub_sec->size + (1 << htab->plt_stub_align) - 1)
|
| + & (-1 << htab->plt_stub_align));
|
| +
|
| for (stub_sec = htab->stub_bfd->sections;
|
| stub_sec != NULL;
|
| stub_sec = stub_sec->next)
|
| @@ -11689,17 +11957,21 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
|
| && htab->glink_eh_frame->size != 0)
|
| {
|
| bfd_vma val;
|
| + bfd_byte *last_fde;
|
| + size_t last_fde_len, size, align, pad;
|
|
|
| p = bfd_zalloc (htab->glink_eh_frame->owner, htab->glink_eh_frame->size);
|
| if (p == NULL)
|
| return FALSE;
|
| htab->glink_eh_frame->contents = p;
|
| + last_fde = 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);
|
| + last_fde_len = sizeof (glink_eh_frame_cie) - 4;
|
| + bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
|
| p += sizeof (glink_eh_frame_cie);
|
|
|
| for (stub_sec = htab->stub_bfd->sections;
|
| @@ -11707,6 +11979,8 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
|
| stub_sec = stub_sec->next)
|
| if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
|
| {
|
| + last_fde = p;
|
| + last_fde_len = 16;
|
| /* FDE length. */
|
| bfd_put_32 (htab->elf.dynobj, 16, p);
|
| p += 4;
|
| @@ -11739,6 +12013,8 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
|
| }
|
| if (htab->glink != NULL && htab->glink->size != 0)
|
| {
|
| + last_fde = p;
|
| + last_fde_len = 20;
|
| /* FDE length. */
|
| bfd_put_32 (htab->elf.dynobj, 20, p);
|
| p += 4;
|
| @@ -11776,7 +12052,16 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
|
| *p++ = DW_CFA_restore_extended;
|
| *p++ = 65;
|
| }
|
| - htab->glink_eh_frame->size = p - htab->glink_eh_frame->contents;
|
| + /* Subsume any padding into the last FDE if user .eh_frame
|
| + sections are aligned more than glink_eh_frame. Otherwise any
|
| + zero padding will be seen as a terminator. */
|
| + size = p - htab->glink_eh_frame->contents;
|
| + align = 1;
|
| + align <<= htab->glink_eh_frame->output_section->alignment_power;
|
| + align -= 1;
|
| + pad = ((size + align) & ~align) - size;
|
| + htab->glink_eh_frame->size = size + pad;
|
| + bfd_put_32 (htab->elf.dynobj, last_fde_len + pad, last_fde);
|
| }
|
|
|
| /* Build the stubs as directed by the stub hash table. */
|
| @@ -11785,6 +12070,14 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
|
| if (htab->relbrlt != NULL)
|
| htab->relbrlt->reloc_count = 0;
|
|
|
| + if (htab->plt_stub_align != 0)
|
| + for (stub_sec = htab->stub_bfd->sections;
|
| + stub_sec != NULL;
|
| + stub_sec = stub_sec->next)
|
| + if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
|
| + stub_sec->size = ((stub_sec->size + (1 << htab->plt_stub_align) - 1)
|
| + & (-1 << htab->plt_stub_align));
|
| +
|
| for (stub_sec = htab->stub_bfd->sections;
|
| stub_sec != NULL;
|
| stub_sec = stub_sec->next)
|
| @@ -11818,14 +12111,16 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
|
| " toc adjust %lu\n"
|
| " long branch %lu\n"
|
| " long toc adj %lu\n"
|
| - " plt call %lu"),
|
| + " plt call %lu\n"
|
| + " plt call toc %lu"),
|
| stub_sec_count,
|
| stub_sec_count == 1 ? "" : "s",
|
| htab->stub_count[ppc_stub_long_branch - 1],
|
| htab->stub_count[ppc_stub_long_branch_r2off - 1],
|
| htab->stub_count[ppc_stub_plt_branch - 1],
|
| htab->stub_count[ppc_stub_plt_branch_r2off - 1],
|
| - htab->stub_count[ppc_stub_plt_call - 1]);
|
| + htab->stub_count[ppc_stub_plt_call - 1],
|
| + htab->stub_count[ppc_stub_plt_call_r2save - 1]);
|
| }
|
| return TRUE;
|
| }
|
| @@ -11925,8 +12220,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
|
| bfd_vma TOCstart;
|
| bfd_boolean ret = TRUE;
|
| bfd_boolean is_opd;
|
| - /* Disabled until we sort out how ld should choose 'y' vs 'at'. */
|
| - bfd_boolean is_power4 = FALSE;
|
| + /* Assume 'at' branch hints. */
|
| + bfd_boolean is_isa_v2 = TRUE;
|
| bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
|
|
|
| /* Initialize howto table if needed. */
|
| @@ -12036,10 +12331,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
|
| }
|
| h = (struct ppc_link_hash_entry *) h_elf;
|
|
|
| - if (sec != NULL && elf_discarded_section (sec))
|
| + if (sec != NULL && discarded_section (sec))
|
| RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
|
| - rel, relend,
|
| - ppc64_elf_howto_table[r_type],
|
| + rel, 1, relend,
|
| + ppc64_elf_howto_table[r_type], 0,
|
| contents);
|
|
|
| if (info->relocatable)
|
| @@ -12558,6 +12853,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
|
| 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_call_r2save
|
| || stub_entry->stub_type == ppc_stub_plt_branch_r2off
|
| || stub_entry->stub_type == ppc_stub_long_branch_r2off))
|
| {
|
| @@ -12586,7 +12882,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
|
|
|
| if (!can_plt_call)
|
| {
|
| - if (stub_entry->stub_type == ppc_stub_plt_call)
|
| + if (stub_entry->stub_type == ppc_stub_plt_call
|
| + || stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
| {
|
| /* If this is a plain branch rather than a branch
|
| and link, don't require a nop. However, don't
|
| @@ -12633,7 +12930,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
|
| }
|
|
|
| if (can_plt_call
|
| - && stub_entry->stub_type == ppc_stub_plt_call)
|
| + && (stub_entry->stub_type == ppc_stub_plt_call
|
| + || stub_entry->stub_type == ppc_stub_plt_call_r2save))
|
| unresolved_reloc = FALSE;
|
| }
|
|
|
| @@ -12646,7 +12944,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
|
| bfd_vma off = (relocation + addend
|
| - sec->output_section->vma
|
| - sec->output_offset);
|
| - bfd_vma dest = opd_entry_value (sec, off, NULL, NULL);
|
| + bfd_vma dest = opd_entry_value (sec, off, NULL, NULL, FALSE);
|
| if (dest != (bfd_vma) -1)
|
| {
|
| relocation = dest;
|
| @@ -12679,7 +12977,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
|
| + stub_entry->stub_sec->output_section->vma);
|
| addend = 0;
|
|
|
| - if (stub_entry->stub_type == ppc_stub_plt_call
|
| + if ((stub_entry->stub_type == ppc_stub_plt_call
|
| + || stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
| + && (ALWAYS_EMIT_R2SAVE
|
| + || stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
| && rel + 1 < relend
|
| && rel[1].r_offset == rel->r_offset + 4
|
| && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOCSAVE)
|
| @@ -12688,7 +12989,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
|
|
|
| if (insn != 0)
|
| {
|
| - if (is_power4)
|
| + if (is_isa_v2)
|
| {
|
| /* Set 'a' bit. This is 0b00010 in BO field for branch
|
| on CR(BI) insns (BO == 001at or 011at), and 0b01000
|
| @@ -13626,7 +13927,7 @@ static bfd_boolean
|
| ppc64_elf_finish_dynamic_symbol (bfd *output_bfd,
|
| struct bfd_link_info *info,
|
| struct elf_link_hash_entry *h,
|
| - Elf_Internal_Sym *sym)
|
| + Elf_Internal_Sym *sym ATTRIBUTE_UNUSED)
|
| {
|
| struct ppc_link_hash_table *htab;
|
| struct plt_entry *ent;
|
| @@ -13695,10 +13996,6 @@ ppc64_elf_finish_dynamic_symbol (bfd *output_bfd,
|
| bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
|
| }
|
|
|
| - /* Mark some specially defined symbols as absolute. */
|
| - if (strcmp (h->root.root.string, "_DYNAMIC") == 0)
|
| - sym->st_shndx = SHN_ABS;
|
| -
|
| return TRUE;
|
| }
|
|
|
| @@ -13739,7 +14036,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
|
| return FALSE;
|
|
|
| dynobj = htab->elf.dynobj;
|
| - sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
|
| + sdyn = bfd_get_linker_section (dynobj, ".dynamic");
|
|
|
| if (htab->elf.dynamic_sections_created)
|
| {
|
| @@ -13867,7 +14164,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
|
|
|
|
|
| if (htab->glink_eh_frame != NULL
|
| - && htab->glink_eh_frame->sec_info_type == ELF_INFO_TYPE_EH_FRAME
|
| + && htab->glink_eh_frame->sec_info_type == SEC_INFO_TYPE_EH_FRAME
|
| && !_bfd_elf_write_section_eh_frame (output_bfd, info,
|
| htab->glink_eh_frame,
|
| htab->glink_eh_frame->contents))
|
|
|