/* PowerPC64-specific support for 64-bit ELF.
Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
- 2009, 2010 Free Software Foundation, Inc.
+ 2009, 2010, 2011 Free Software Foundation, Inc.
Written by Linus Nordberg, Swox AB <info@swox.com>,
based on elf32-ppc.c by Ian Lance Taylor.
Largely rewritten by Alan Modra.
#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 **);
#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
#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
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;
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
}
}
-/* 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[]=
{
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;
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;
-};
-
/* 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. */
} 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;
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;
/* 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;
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;
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;
}
|| ! 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
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;
+
+ /* If we were called to copy over info for a weak sym, that's all. */
+ if (eind->elf.root.type != bfd_link_hash_indirect)
+ return;
+
/* 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)
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 (eind->elf.root.type != bfd_link_hash_indirect)
- return;
-
/* Copy over got entries that we may have already seen to the
symbol which just became indirect. */
if (eind->elf.got.glist != NULL)
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)
{
if ((ibfd->flags & DYNAMIC) == 0)
- elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE;
+ elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
}
else if (ELF_ST_TYPE (isym->st_info) == STT_FUNC)
;
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 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;
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 (sec->name, ".opd") == 0)
|| (!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
s = sec;
vpp = &elf_section_data (s)->local_dynrel;
- head = (struct ppc_dyn_relocs **) vpp;
+ head = (struct elf_dyn_relocs **) vpp;
}
p = *head;
/* 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;
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. */
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)
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);
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)
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)
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);
}
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;
}
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;
*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;
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;
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. */
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
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;
}
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;
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,
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;
/* 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)
_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;
bfd *ibfd;
asection *sec;
struct ppc_link_hash_table *htab;
+ unsigned char *toc_ref;
int pass;
if (info->relocatable || !info->executable)
if (htab == NULL)
return FALSE;
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
- {
- Elf_Internal_Sym *locsyms = NULL;
- asection *toc = bfd_get_section_by_name (ibfd, ".toc");
- unsigned char *toc_ref = NULL;
-
- /* Look at all the sections for this file. Make two passes over
- the relocs. On the first pass, mark toc entries involved
- with tls relocs, and check that tls relocs involved in
- setting up a tls_get_addr call are indeed followed by such a
- call. If they are not, exclude them from the optimizations
- done on the second pass. */
- for (pass = 0; pass < 2; ++pass)
+ /* Make two passes over the relocs. On the first pass, mark toc
+ entries involved with tls relocs, and check that tls relocs
+ involved in setting up a tls_get_addr call are indeed followed by
+ such a call. If they are not, we can't do any tls optimization.
+ On the second pass twiddle tls_mask flags to notify
+ relocate_section that optimization can be done, and adjust got
+ and plt refcounts. */
+ toc_ref = NULL;
+ for (pass = 0; pass < 2; ++pass)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ {
+ Elf_Internal_Sym *locsyms = NULL;
+ asection *toc = bfd_get_section_by_name (ibfd, ".toc");
+
for (sec = ibfd->sections; sec != NULL; sec = sec->next)
if (sec->has_tls_reloc && !bfd_is_abs_section (sec->output_section))
{
Elf_Internal_Rela *relstart, *rel, *relend;
+ bfd_boolean found_tls_get_addr_arg = 0;
/* Read the relocations. */
relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
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,
&& (elf_symtab_hdr (ibfd).contents
!= (unsigned char *) locsyms))
free (locsyms);
- return FALSE;
+ return ret;
}
if (h != NULL)
else if (h->root.type == bfd_link_hash_undefweak)
value = 0;
else
- continue;
+ {
+ found_tls_get_addr_arg = 0;
+ continue;
+ }
}
else
/* Symbols referenced by TLS relocs must be of type
}
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:
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:
}
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;
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)
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)
{
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
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;
}
/* 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)
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;
}
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)
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)
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
|| 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;
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;
&& 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;
|| (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;
if (val >= toc->size)
continue;
+ if ((skip[val >> 3] & can_optimize) != 0)
+ {
+ bfd_vma off;
+ unsigned char opc;
+
+ switch (r_type)
+ {
+ case R_PPC64_TOC16_HA:
+ break;
+
+ case R_PPC64_TOC16_LO_DS:
+ off = rel->r_offset + (bfd_big_endian (ibfd) ? -2 : 3);
+ if (!bfd_get_section_contents (ibfd, sec, &opc, off, 1))
+ return FALSE;
+ if ((opc & (0x3f << 2)) == (58u << 2))
+ break;
+ /* Fall thru */
+
+ default:
+ /* Wrong sort of reloc, or not a ld. We may
+ as well clear ref_from_discarded too. */
+ skip[val >> 3] = 0;
+ }
+ }
+
/* For the toc section, we only mark as used if
this entry itself isn't unused. */
if (sec == toc
&& !used[val >> 3]
&& (used[rel->r_offset >> 3]
- || !skip[rel->r_offset >> 3]))
+ || !(skip[rel->r_offset >> 3] & ref_from_discarded)))
/* Do all the relocs again, to catch reference
chains. */
repeat = 1;
used[val >> 3] = 1;
}
while (repeat);
+
+ if (elf_section_data (sec)->relocs != relstart)
+ free (relstart);
}
/* Merge the used and skip arrays. Assume that TOC
{
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;
{
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. */
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
continue;
relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
- TRUE);
+ info->keep_memory);
if (relstart == NULL)
goto error_ret;
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)
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;
- rel->r_addend -= skip[rel->r_addend >> 3];
+ 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[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;
elf_link_hash_traverse (elf_hash_table (info), adjust_toc_syms,
&toc_inf);
}
+
+ if (toc->reloc_count != 0)
+ {
+ Elf_Internal_Shdr *rel_hdr;
+ Elf_Internal_Rela *wrel;
+ bfd_size_type sz;
+
+ /* Remove unused toc relocs, and adjust those we keep. */
+ if (toc_relocs == NULL)
+ toc_relocs = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL,
+ info->keep_memory);
+ if (toc_relocs == NULL)
+ goto error_ret;
+
+ wrel = toc_relocs;
+ for (rel = toc_relocs; rel < toc_relocs + toc->reloc_count; ++rel)
+ if ((skip[rel->r_offset >> 3]
+ & (ref_from_discarded | can_optimize)) == 0)
+ {
+ wrel->r_offset = rel->r_offset - skip[rel->r_offset >> 3];
+ wrel->r_info = rel->r_info;
+ wrel->r_addend = rel->r_addend;
+ ++wrel;
+ }
+ else if (!dec_dynrel_count (rel->r_info, toc, info,
+ &local_syms, NULL, NULL))
+ goto error_ret;
+
+ elf_section_data (toc)->relocs = toc_relocs;
+ toc->reloc_count = wrel - toc_relocs;
+ rel_hdr = _bfd_elf_single_rel_hdr (toc);
+ sz = rel_hdr->sh_entsize;
+ rel_hdr->sh_size = toc->reloc_count * sz;
+ }
}
+ else if (toc_relocs != NULL
+ && elf_section_data (toc)->relocs != toc_relocs)
+ free (toc_relocs);
if (local_syms != NULL
&& symtab_hdr->contents != (unsigned char *) local_syms)
struct ppc_link_hash_table *htab;
asection *s;
struct ppc_link_hash_entry *eh;
- struct ppc_dyn_relocs *p;
+ 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)
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; )
{
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)
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)
{
/* Strip this section if we don't need it; see the
comment below. */
}
+ 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)
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)
/* 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)
r[1].r_offset = r[0].r_offset + 8;
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);
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, 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
{
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);
{
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;
}
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;
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;
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;
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)
{
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;
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;
}
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;
}
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;
}
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;
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. */
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
: "<local sym>");
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;
&& (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;
- 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
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;
}
}
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;
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;
}
if (h->root.type == bfd_link_hash_indirect)
return TRUE;
- if (h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
merge_got_entries (&h->got.glist);
return TRUE;
if (h->root.type == bfd_link_hash_indirect)
return TRUE;
- if (h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
for (gent = h->got.glist; gent != NULL; gent = gent->next)
if (!gent->is_indirect)
allocate_got (h, (struct bfd_link_info *) inf, gent);
if (elf_gp (isec->owner) != 0)
htab->toc_curr = elf_gp (isec->owner);
}
- else if (!isec->call_check_done
- && toc_adjusting_stub_needed (info, isec) < 0)
- return FALSE;
+ else
+ {
+ if (!isec->call_check_done
+ && toc_adjusting_stub_needed (info, isec) < 0)
+ return FALSE;
+ /* 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, because the fragments generally don't use the TOC. */
+ Use the last TOC base. */
htab->stub_group[isec->id].toc_off = htab->toc_curr;
return TRUE;
}
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)
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"),
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;
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)
{
#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
instruction. */
bfd_boolean
-ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size)
+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;
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;
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);
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)
/* 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;
}
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);
}
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)
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;
return _bfd_elf_default_action_discarded (sec);
}
-/* REL points to a low-part reloc on a bigtoc instruction sequence.
+/* REL points to a low-part reloc on a largetoc instruction sequence.
Find the matching high-part reloc instruction and verify that it
- is addis REG,r2,x. If so, return a pointer to the high-part reloc. */
+ is addis REG,x,imm. If so, set *REG to x and return a pointer to
+ the high-part reloc. */
static const Elf_Internal_Rela *
ha_reloc_match (const Elf_Internal_Rela *relocs,
const Elf_Internal_Rela *rel,
- unsigned int reg,
+ unsigned int *reg,
+ bfd_boolean match_addend,
const bfd *input_bfd,
const bfd_byte *contents)
{
while (--rel >= relocs)
if (rel->r_info == r_info_ha
- && rel->r_addend == r_addend)
+ && (!match_addend
+ || rel->r_addend == r_addend))
{
const bfd_byte *p = contents + (rel->r_offset & ~3);
unsigned int insn = bfd_get_32 (input_bfd, p);
- if ((insn & ((0x3f << 26) | (0x1f << 16)))
- == ((15u << 26) | (2 << 16)) /* addis rt,r2,x */
- && (insn & (0x1f << 21)) == (reg << 21))
- return rel;
+ if ((insn & (0x3f << 26)) == (15u << 26) /* addis rt,x,imm */
+ && (insn & (0x1f << 21)) == (*reg << 21))
+ {
+ *reg = (insn >> 16) & 0x1f;
+ return rel;
+ }
break;
}
return NULL;
Elf_Internal_Rela outrel;
bfd_byte *loc;
struct got_entry **local_got_ents;
+ unsigned char *ha_opt;
bfd_vma TOCstart;
+ bfd_boolean no_ha_opt;
bfd_boolean ret = TRUE;
bfd_boolean is_opd;
/* Disabled until we sort out how ld should choose 'y' vs 'at'. */
symtab_hdr = &elf_symtab_hdr (input_bfd);
sym_hashes = elf_sym_hashes (input_bfd);
is_opd = ppc64_elf_section_data (input_section)->sec_type == sec_opd;
+ ha_opt = NULL;
+ no_ha_opt = FALSE;
rel = relocs;
relend = relocs + input_section->reloc_count;
bfd_boolean unresolved_reloc;
bfd_boolean warned;
unsigned int insn;
- bfd_vma mask;
+ unsigned int mask;
struct ppc_stub_hash_entry *stub_entry;
bfd_vma max_br_offset;
bfd_vma from;
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;
/* 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
/* 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);
}
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:
}
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
+ 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);
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);
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);
".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);
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);
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
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)
;
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);
? 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)
{
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);
case R_PPC64_GOT_DTPREL16_HA:
case R_PPC64_GOT16_HA:
case R_PPC64_TOC16_HA:
- /* For now we don't nop out the first instruction. */
+ /* nop is done later. */
break;
case R_PPC64_GOT_TLSLD16_LO:
&& ((insn & 3) == 0 || (insn & 3) == 3)))
{
unsigned int reg = (insn >> 16) & 0x1f;
- if (ha_reloc_match (relocs, rel, reg, input_bfd, contents))
+ const Elf_Internal_Rela *ha;
+ bfd_boolean match_addend;
+
+ match_addend = (sym != NULL
+ && ELF_ST_TYPE (sym->st_info) == STT_SECTION);
+ ha = ha_reloc_match (relocs, rel, ®, match_addend,
+ input_bfd, contents);
+ if (ha != NULL)
{
insn &= ~(0x1f << 16);
- insn |= 2 << 16;
+ insn |= reg << 16;
bfd_put_32 (input_bfd, insn, p);
+ if (ha_opt == NULL)
+ {
+ ha_opt = bfd_zmalloc (input_section->reloc_count);
+ if (ha_opt == NULL)
+ return FALSE;
+ }
+ ha_opt[ha - relocs] = 1;
}
+ else
+ /* If we don't find a matching high part insn,
+ something is fishy. Refuse to nop any high
+ part insn in this section. */
+ no_ha_opt = TRUE;
}
}
break;
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);
&& !((input_section->flags & SEC_DEBUGGING) != 0
&& h->elf.def_dynamic))
{
- (*_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;
}
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);
}
}
+ if (ha_opt != NULL)
+ {
+ if (!no_ha_opt)
+ {
+ unsigned char *opt = ha_opt;
+ rel = relocs;
+ relend = relocs + input_section->reloc_count;
+ for (; rel < relend; opt++, rel++)
+ if (*opt != 0)
+ {
+ bfd_byte *p = contents + (rel->r_offset & ~3);
+ bfd_put_32 (input_bfd, NOP, p);
+ }
+ }
+ free (ha_opt);
+ }
+
/* If we're emitting relocations, then shortly after this function
returns, reloc offsets and addends for this section will be
adjusted. Worse, reloc symbol indices will be for the output
&& 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;
&& 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. */