/* PowerPC64-specific support for 64-bit ELF.
- Copyright (C) 1999-2016 Free Software Foundation, Inc.
+ Copyright (C) 1999-2017 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.
#define elf_backend_plt_alignment 3
#define elf_backend_plt_not_loaded 1
#define elf_backend_got_header_size 8
+#define elf_backend_want_dynrelro 1
#define elf_backend_can_gc_sections 1
#define elf_backend_can_refcount 1
#define elf_backend_rela_normal 1
#define elf_backend_grok_prstatus ppc64_elf_grok_prstatus
#define elf_backend_grok_psinfo ppc64_elf_grok_psinfo
#define elf_backend_write_core_note ppc64_elf_write_core_note
-#define elf_backend_create_dynamic_sections ppc64_elf_create_dynamic_sections
+#define elf_backend_create_dynamic_sections _bfd_elf_create_dynamic_sections
#define elf_backend_copy_indirect_symbol ppc64_elf_copy_indirect_symbol
#define elf_backend_add_symbol_hook ppc64_elf_add_symbol_hook
#define elf_backend_check_directives ppc64_elf_before_check_relocs
struct ppc_link_hash_entry *dot_syms;
/* Shortcuts to get to dynamic linker sections. */
- asection *dynbss;
- asection *relbss;
asection *glink;
asection *sfpr;
asection *brlt;
return TRUE;
}
-/* Create the dynamic sections, and set up shortcuts. */
-
-static bfd_boolean
-ppc64_elf_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
-{
- struct ppc_link_hash_table *htab;
-
- if (!_bfd_elf_create_dynamic_sections (dynobj, info))
- return FALSE;
-
- htab = ppc_hash_table (info);
- if (htab == NULL)
- return FALSE;
-
- htab->dynbss = bfd_get_linker_section (dynobj, ".dynbss");
- if (!bfd_link_pic (info))
- htab->relbss = bfd_get_linker_section (dynobj, ".rela.bss");
-
- if (!htab->elf.sgot || !htab->elf.splt || !htab->elf.srelplt || !htab->dynbss
- || (!bfd_link_pic (info) && !htab->relbss))
- abort ();
-
- return TRUE;
-}
-
/* Follow indirect and warning symbol links. */
static inline struct bfd_link_hash_entry *
if (eind->oh != NULL)
edir->oh = ppc_follow_link (eind->oh);
- if (edir->elf.versioned != versioned_hidden)
- {
- /* 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;
+ /* 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;
- edir->elf.pointer_equality_needed |= eind->elf.pointer_equality_needed;
- }
+ if (edir->elf.versioned != versioned_hidden)
+ 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;
+ edir->elf.pointer_equality_needed |= eind->elf.pointer_equality_needed;
/* If we were called to copy over info for a weak sym, don't copy
dyn_relocs, plt/got info, or dynindx. We used to copy dyn_relocs
table entry for a non-ifunc local symbol. */
info->callbacks->einfo
/* xgettext:c-format */
- (_("%P: %H: %s reloc against local symbol\n"),
+ (_("%H: %s reloc against local symbol\n"),
abfd, sec, rel->r_offset,
ppc64_elf_howto_table[r_type]->name);
bfd_set_error (bfd_error_bad_value);
if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
ppc_howto_init ();
/* xgettext:c-format */
- info->callbacks->einfo (_("%P: %H: %s reloc unsupported "
+ info->callbacks->einfo (_("%H: %s reloc unsupported "
"in shared libraries and PIEs.\n"),
abfd, sec, rel->r_offset,
ppc64_elf_howto_table[r_type]->name);
&& ELF_ST_VISIBILITY (eh->elf.other) != STV_INTERNAL
&& ELF_ST_VISIBILITY (eh->elf.other) != STV_HIDDEN
&& (!bfd_link_executable (info)
+ || info->gc_keep_exported
|| info->export_dynamic
|| (eh->elf.dynamic
&& d != NULL
&& (*d->match) (&d->head, NULL, eh->elf.root.root.string)))
- && (strchr (eh->elf.root.root.string, ELF_VER_CHR) != NULL
+ && (eh->elf.versioned >= versioned
|| !bfd_hide_sym_by_version (info->version_info,
eh->elf.root.root.string)))))
{
struct elf_link_hash_entry *h)
{
struct ppc_link_hash_table *htab;
- asection *s;
+ asection *s, *srel;
htab = ppc_hash_table (info);
if (htab == NULL)
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_READONLY) != 0)
+ {
+ s = htab->elf.sdynrelro;
+ srel = htab->elf.sreldynrelro;
+ }
+ else
+ {
+ s = htab->elf.sdynbss;
+ srel = htab->elf.srelbss;
+ }
if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
{
- htab->relbss->size += sizeof (Elf64_External_Rela);
+ srel->size += sizeof (Elf64_External_Rela);
h->needs_copy = 1;
}
- s = htab->dynbss;
-
return _bfd_elf_adjust_dynamic_copy (info, h, s);
}
tga_fd->root.type = bfd_link_hash_indirect;
tga_fd->root.u.i.link = &opt_fd->root;
ppc64_elf_copy_indirect_symbol (info, opt_fd, tga_fd);
- opt_fd->forced_local = 0;
+ opt_fd->mark = 1;
if (opt_fd->dynindx != -1)
{
/* Use __tls_get_addr_opt in dynamic relocations. */
tga->root.type = bfd_link_hash_indirect;
tga->root.u.i.link = &opt->root;
ppc64_elf_copy_indirect_symbol (info, opt, tga);
- opt->forced_local = 0;
+ opt->mark = 1;
_bfd_elf_link_hash_hide_symbol (info, opt,
tga->forced_local);
htab->tls_get_addr = (struct ppc_link_hash_entry *) opt;
sprintf (str, "%#08x", insn);
info->callbacks->einfo
/* xgettext:c-format */
- (_("%P: %H: toc optimization is not supported for"
+ (_("%H: toc optimization is not supported for"
" %s instruction.\n"),
ibfd, sec, rel->r_offset & ~3, str);
}
ppc_howto_init ();
info->callbacks->einfo
/* xgettext:c-format */
- (_("%P: %H: %s references "
+ (_("%H: %s references "
"optimized away TOC entry\n"),
ibfd, sec, rel->r_offset,
ppc64_elf_howto_table[r_type]->name);
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);
gent->got.offset = got->size;
got->size += entsize;
- dyn = htab->elf.dynamic_sections_created;
if (h->type == STT_GNU_IFUNC)
{
htab->elf.irelplt->size += rentsize;
htab->got_reli_size += rentsize;
}
else if ((bfd_link_pic (info)
- || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))
+ || (htab->elf.dynamic_sections_created
+ && h->dynindx != -1
+ && !SYMBOL_REFERENCES_LOCAL (info, h)))
&& (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
|| h->root.type != bfd_link_hash_undefweak))
{
}
}
+/* If H is undefined weak, make it dynamic if that makes sense. */
+
+static bfd_boolean
+ensure_undefweak_dynamic (struct bfd_link_info *info,
+ struct elf_link_hash_entry *h)
+{
+ struct elf_link_hash_table *htab = elf_hash_table (info);
+
+ if (htab->dynamic_sections_created
+ && h->root.type == bfd_link_hash_undefweak
+ && h->dynindx == -1
+ && !h->forced_local
+ && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
+ return bfd_elf_link_record_dynamic_symbol (info, h);
+ return TRUE;
+}
+
/* Allocate space in .plt, .got and associated reloc sections for
dynamic relocs. */
if (!gent->is_indirect)
{
/* Make sure this symbol is output as a dynamic symbol.
- Undefined weak syms won't yet be marked as dynamic,
- nor will all TLS symbols. */
- if (h->dynindx == -1
- && !h->forced_local
- && h->type != STT_GNU_IFUNC
- && htab->elf.dynamic_sections_created)
- {
- if (! bfd_elf_link_record_dynamic_symbol (info, h))
- return FALSE;
- }
+ Undefined weak syms won't yet be marked as dynamic. */
+ if (!ensure_undefweak_dynamic (info, h))
+ return FALSE;
if (!is_ppc64_elf (gent->owner))
abort ();
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
- else if (h->dynindx == -1
- && !h->forced_local)
- {
- if (! bfd_elf_link_record_dynamic_symbol (info, h))
- return FALSE;
- }
+ else if (!ensure_undefweak_dynamic (info, h))
+ return FALSE;
}
}
else if (h->type == STT_GNU_IFUNC)
/* For the non-pic case, discard space for relocs against
symbols which turn out to need copy relocs or are not
dynamic. */
+ if (!h->non_got_ref
+ && !h->def_regular)
+ {
+ /* Make sure this symbol is output as a dynamic symbol.
+ Undefined weak syms won't yet be marked as dynamic. */
+ if (!ensure_undefweak_dynamic (info, h))
+ return FALSE;
- /* First make sure this symbol is output as a dynamic symbol.
- Undefined weak syms won't yet be marked as dynamic. */
- if (h->root.type == bfd_link_hash_undefweak
- && !h->non_got_ref
- && !h->def_regular
- && h->dynindx == -1
- && !h->forced_local
- && !bfd_elf_link_record_dynamic_symbol (info, h))
- return FALSE;
-
- if (h->non_got_ref
- || h->def_regular
- || h->dynindx == -1)
+ if (h->dynindx == -1)
+ eh->dyn_relocs = NULL;
+ }
+ else
eh->dyn_relocs = NULL;
}
}
if ((htab->elf.dynamic_sections_created
- && h->dynindx != -1
- && WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, bfd_link_pic (info), h))
+ && h->dynindx != -1)
|| h->type == STT_GNU_IFUNC)
{
struct plt_entry *pent;
|| s == htab->elf.splt
|| s == htab->elf.iplt
|| s == htab->glink
- || s == htab->dynbss)
+ || s == htab->elf.sdynbss
+ || s == htab->elf.sdynrelro)
{
/* Strip this section if we don't need it; see the
comment below. */
but this way if it does we get a R_PPC64_NONE reloc in .rela
sections instead of garbage.
We also rely on the section contents being zero when writing
- the GOT. */
+ the GOT and .dynrelro. */
s->contents = bfd_zalloc (dynobj, s->size);
if (s->contents == NULL)
return FALSE;
info->callbacks->einfo
(!IS_PPC64_TLS_RELOC (r_type)
/* xgettext:c-format */
- ? _("%P: %H: %s used with TLS symbol `%T'\n")
+ ? _("%H: %s used with TLS symbol `%T'\n")
/* xgettext:c-format */
- : _("%P: %H: %s used with non-TLS symbol `%T'\n"),
+ : _("%H: %s used with non-TLS symbol `%T'\n"),
input_bfd, input_section, rel->r_offset,
ppc64_elf_howto_table[r_type]->name,
sym_name);
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
info->callbacks->einfo
/* xgettext:c-format */
- (_("%P: %H: call to `%T' lacks nop, can't restore toc; "
+ (_("%H: call to `%T' lacks nop, can't restore toc; "
"recompile with -fPIC\n"),
input_bfd, input_section, rel->r_offset, sym_name);
else
info->callbacks->einfo
/* xgettext:c-format */
- (_("%P: %H: call to `%T' lacks nop, can't restore toc; "
+ (_("%H: call to `%T' lacks nop, can't restore toc; "
"(-mcmodel=small toc adjust stub)\n"),
input_bfd, input_section, rel->r_offset, sym_name);
ent = ppc64_tlsld_got (input_bfd);
else
{
-
if (h != NULL)
{
- bfd_boolean dyn = htab->elf.dynamic_sections_created;
- if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info),
- &h->elf)
- || (bfd_link_pic (info)
- && SYMBOL_REFERENCES_LOCAL (info, &h->elf)))
+ if (!htab->elf.dynamic_sections_created
+ || h->elf.dynindx == -1
+ || SYMBOL_REFERENCES_LOCAL (info, &h->elf)
+ || (ELF_ST_VISIBILITY (h->elf.other) != STV_DEFAULT
+ && h->elf.root.type == bfd_link_hash_undefweak))
/* 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
;
else
{
- BFD_ASSERT (h->elf.dynindx != -1);
indx = h->elf.dynindx;
unresolved_reloc = FALSE;
}
: ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC);
if (ifunc)
relgot = htab->elf.irelplt;
- else if ((bfd_link_pic (info) || indx != 0)
- && (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))
+ else if (indx != 0
+ || (bfd_link_pic (info)
+ && (h == NULL
+ || (ELF_ST_VISIBILITY (h->elf.other)
+ == STV_DEFAULT)
+ || h->elf.root.type != bfd_link_hash_undefweak
+ || (tls_type == (TLS_TLS | TLS_LD)
+ && !h->elf.def_dynamic))))
relgot = ppc64_elf_tdata (ent->owner)->relgot;
if (relgot != NULL)
{
emitting a reloc. */
else
{
+ int tlsopt
+ = (htab->params->tls_get_addr_opt
+ && htab->tls_get_addr_fd != NULL
+ && htab->tls_get_addr_fd->elf.plt.plist != NULL);
+
relocation += addend;
- if (tls_type == (TLS_TLS | TLS_LD))
- relocation = 1;
- else if (tls_type != 0)
+ if (tls_type != 0)
{
if (htab->elf.tls_sec == NULL)
relocation = 0;
else
{
- relocation -= htab->elf.tls_sec->vma + DTP_OFFSET;
- if (tls_type == (TLS_TLS | TLS_TPREL))
+ if (tls_type & TLS_LD)
+ relocation = 0;
+ else
+ relocation -= htab->elf.tls_sec->vma + DTP_OFFSET;
+ if ((tls_type & TLS_TPREL)
+ || (tlsopt && !(tls_type & TLS_DTPREL)))
relocation += DTP_OFFSET - TP_OFFSET;
}
- if (tls_type == (TLS_TLS | TLS_GD))
+ if (tls_type & (TLS_GD | TLS_LD))
{
bfd_put_64 (output_bfd, relocation,
got->contents + off + 8);
- relocation = 1;
+ relocation = !tlsopt;
}
}
-
bfd_put_64 (output_bfd, relocation,
got->contents + off);
}
{
info->callbacks->einfo
/* xgettext:c-format */
- (_("%P: %H: %s for indirect "
+ (_("%H: %s for indirect "
"function `%T' unsupported\n"),
input_bfd, input_section, rel->r_offset,
ppc64_elf_howto_table[r_type]->name,
addend = outrel.r_addend;
/* Adjust pc_relative relocs to have zero in *r_offset. */
else if (ppc64_elf_howto_table[r_type]->pc_relative)
- addend = (input_section->output_section->vma
- + input_section->output_offset
- + rel->r_offset);
+ addend = outrel.r_offset;
}
}
+ else if (r_type == R_PPC64_DTPMOD64
+ && htab->params->tls_get_addr_opt
+ && htab->tls_get_addr_fd != NULL
+ && htab->tls_get_addr_fd->elf.plt.plist != NULL)
+ {
+ /* Set up for __tls_get_addr_opt stub, when this entry
+ does not have dynamic relocs. */
+ relocation = 0;
+ /* Set up the next word for local dynamic. If it turns
+ out to be global dynamic, the reloc will overwrite
+ this value. */
+ if (rel->r_offset + 16 <= input_section->size)
+ bfd_put_64 (input_bfd, DTP_OFFSET - TP_OFFSET,
+ contents + rel->r_offset + 8);
+ }
+ else if (r_type == R_PPC64_DTPREL64
+ && htab->params->tls_get_addr_opt
+ && htab->tls_get_addr_fd != NULL
+ && htab->tls_get_addr_fd->elf.plt.plist != NULL
+ && rel > relocs
+ && rel[-1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_DTPMOD64)
+ && rel[-1].r_offset + 8 == rel->r_offset)
+ {
+ /* __tls_get_addr_opt stub value. */
+ addend += DTP_OFFSET - TP_OFFSET;
+ }
break;
case R_PPC64_COPY:
relocation ^= relocation & mask;
info->callbacks->einfo
/* xgettext:c-format */
- (_("%P: %H: error: %s not a multiple of %u\n"),
+ (_("%H: error: %s not a multiple of %u\n"),
input_bfd, input_section, rel->r_offset,
howto->name,
mask + 1);
{
info->callbacks->einfo
/* xgettext:c-format */
- (_("%P: %H: unresolvable %s against `%T'\n"),
+ (_("%H: unresolvable %s against `%T'\n"),
input_bfd, input_section, rel->r_offset,
howto->name,
h->elf.root.root.string);
{
info->callbacks->einfo
/* xgettext:c-format */
- (_("%P: %H: %s against `%T': error %d\n"),
+ (_("%H: %s against `%T': error %d\n"),
input_bfd, input_section, rel->r_offset,
reloc_name, sym_name, (int) r);
ret = FALSE;
if (h->needs_copy)
{
/* This symbol needs a copy reloc. Set it up. */
+ asection *srel;
if (h->dynindx == -1
|| (h->root.type != bfd_link_hash_defined
&& h->root.type != bfd_link_hash_defweak)
- || htab->relbss == NULL)
+ || htab->elf.srelbss == NULL
+ || htab->elf.sreldynrelro == NULL)
abort ();
rela.r_offset = (h->root.u.def.value
+ h->root.u.def.section->output_offset);
rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_COPY);
rela.r_addend = 0;
- loc = htab->relbss->contents;
- loc += htab->relbss->reloc_count++ * sizeof (Elf64_External_Rela);
+ if ((h->root.u.def.section->flags & SEC_READONLY) != 0)
+ srel = htab->elf.sreldynrelro;
+ else
+ srel = htab->elf.srelbss;
+ loc = srel->contents;
+ loc += srel->reloc_count++ * sizeof (Elf64_External_Rela);
bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
}
}
}
- if (htab->elf.sgot != NULL && htab->elf.sgot->size != 0)
+ if (htab->elf.sgot != NULL && htab->elf.sgot->size != 0
+ && htab->elf.sgot->output_section != bfd_abs_section_ptr)
{
/* Fill in the first entry in the global offset table.
We use it to hold the link-time TOCbase. */
elf_section_data (htab->elf.sgot->output_section)->this_hdr.sh_entsize = 8;
}
- if (htab->elf.splt != NULL && htab->elf.splt->size != 0)
+ if (htab->elf.splt != NULL && htab->elf.splt->size != 0
+ && htab->elf.splt->output_section != bfd_abs_section_ptr)
{
/* Set .plt entry size. */
elf_section_data (htab->elf.splt->output_section)->this_hdr.sh_entsize